diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
index 8826c55..05c82de 100644
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -2,6 +2,8 @@ name: Build
on:
push:
+ branches:
+ - main
pull_request:
jobs:
diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml
new file mode 100644
index 0000000..c3926c9
--- /dev/null
+++ b/.github/workflows/deploy.yml
@@ -0,0 +1,38 @@
+name: Deploy to Cloudflare Workers
+
+on:
+ push:
+ tags:
+ - '*'
+ workflow_dispatch:
+
+jobs:
+ deploy:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v4
+
+ - name: Setup Flutter
+ uses: subosito/flutter-action@v2
+ with:
+ channel: 'stable'
+ # Match local development version which provides Dart 3.11.0
+ flutter-version: '3.41.2'
+
+ - name: Setup Bun
+ uses: oven-sh/setup-bun@v2
+ with:
+ bun-version: latest
+
+ - name: Get dependencies
+ run: flutter pub get
+
+ - name: Build Web
+ run: bun run build
+
+ - name: Deploy to Cloudflare
+ uses: cloudflare/wrangler-action@v3
+ with:
+ apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
+ accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
+ command: deploy
diff --git a/.github/workflows/flutter_analyze.yml b/.github/workflows/flutter_dart.yml
similarity index 61%
rename from .github/workflows/flutter_analyze.yml
rename to .github/workflows/flutter_dart.yml
index af4a3b7..117eb4f 100644
--- a/.github/workflows/flutter_analyze.yml
+++ b/.github/workflows/flutter_dart.yml
@@ -1,8 +1,10 @@
-name: Flutter Analyze
+name: Flutter and Dart
on:
pull_request:
push:
+ branches:
+ - main
jobs:
analyze:
@@ -19,5 +21,11 @@ jobs:
- name: Install dependencies
run: flutter pub get
- - name: Analyze
+ - name: Analyze code
run: flutter analyze --fatal-infos --fatal-warnings
+
+ - name: Verify formatting
+ run: dart format --output=none --set-exit-if-changed .
+
+ - name: Run tests
+ run: flutter test -r github
diff --git a/.gitignore b/.gitignore
index b918113..88295e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -30,8 +30,12 @@ migrate_working_dir/
.flutter-plugins-dependencies
.pub-cache/
.pub/
+pubspec.lock
/build/
/coverage/
+# fvm project files
+.fvm/
+.fvmrc
# Symbolication related
app.*.symbols
@@ -57,6 +61,7 @@ secrets.dart
.DS_Store
.AppleDouble
.LSOverride
+macos/Flutter/GeneratedPluginRegistrant.swift
# iOS
**/ios/Pods/
@@ -65,6 +70,7 @@ secrets.dart
**/ios/Flutter/Flutter.podspec
# Android
+.gradle/
**/android/.gradle/
**/android/captures/
**/android/local.properties
@@ -81,3 +87,6 @@ keystore.properties
# IDE
.vscode/launch.json
.vscode/settings.json
+
+# Cloudflare Wrangler
+.wrangler
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..e69de29
diff --git a/.ruby-version b/.ruby-version
new file mode 100644
index 0000000..fcdb2e1
--- /dev/null
+++ b/.ruby-version
@@ -0,0 +1 @@
+4.0.0
diff --git a/.swift-version b/.swift-version
new file mode 100644
index 0000000..31b44b0
--- /dev/null
+++ b/.swift-version
@@ -0,0 +1 @@
+6.2.4
\ No newline at end of file
diff --git a/AGENTS.md b/AGENTS.md
index bac981d..273bb96 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -6,7 +6,7 @@
## BLE Frames & Protocol Notes
- Nordic UART Service (NUS) UUIDs: Service `6e400001-b5a3-f393-e0a9-e50e24dcca9e`, RX `6e400002-b5a3-f393-e0a9-e50e24dcca9e`, TX `6e400003-b5a3-f393-e0a9-e50e24dcca9e`.
-- Discovery: scans for device name prefix `MeshCore-` and filters by `platformName`/`advertisementData.advName`.
+- Discovery: scans for device names matching known prefixes and filters by `platformName`/`advertisementData.advName`.
- Frames are capped at `maxFrameSize = 172` bytes; byte 0 is the command/response/push code. I/O is `MeshCoreConnector.sendFrame` and `MeshCoreConnector.receivedFrames`.
- Command codes (to device): `cmdAppStart`=1, `cmdSendTxtMsg`=2, `cmdSendChannelTxtMsg`=3, `cmdGetContacts`=4, `cmdGetDeviceTime`=5, `cmdSetDeviceTime`=6, `cmdSendSelfAdvert`=7, `cmdSetAdvertName`=8, `cmdAddUpdateContact`=9, `cmdSyncNextMessage`=10, `cmdSetRadioParams`=11, `cmdSetRadioTxPower`=12, `cmdResetPath`=13, `cmdSetAdvertLatLon`=14, `cmdRemoveContact`=15, `cmdShareContact`=16, `cmdExportContact`=17, `cmdImportContact`=18, `cmdReboot`=19, `cmdSendLogin`=26, `cmdGetChannel`=31, `cmdSetChannel`=32, `cmdGetRadioSettings`=57.
- Response codes (from device): `respCodeOk`=0, `respCodeErr`=1, `respCodeContactsStart`=2, `respCodeContact`=3, `respCodeEndOfContacts`=4, `respCodeSelfInfo`=5, `respCodeSent`=6, `respCodeContactMsgRecv`=7, `respCodeChannelMsgRecv`=8, `respCodeCurrTime`=9, `respCodeNoMoreMessages`=10, `respCodeContactMsgRecvV3`=16, `respCodeChannelMsgRecvV3`=17, `respCodeChannelInfo`=18, `respCodeRadioSettings`=25.
diff --git a/CLAUDE.md b/CLAUDE.md
index 08ef342..55af890 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -61,7 +61,7 @@ lib/
- **TX Characteristic**: `6e400003-b5a3-f393-e0a9-e50e24dcca9e` (Notify from device)
### Device Discovery
-- Scans for devices with name prefix `MeshCore-`
+- Scans for devices with known name prefixes
- Filters by `platformName` or `advertisementData.advName`
### Connection States
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..ac727ba
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,71 @@
+# How to contribute to Meshcore Open
+
+Before submitting any pull requests (PR), please review the following information.
+
+Unsolicited PRs without previous discussion or open issues may be
+rejected. As may changes that are too broad (i.e. 100 files changed) or that
+cover too many separate changes. If the changes are clearly AI generated they
+may also be rejected. [See more](#ai-use)
+
+## First Step Checklist
+
+### **Did you find a bug?**
+
+* **Ensure the bug was not already reported** by searching on GitHub under [Issues](https://github.com/zjs81/meshcore-open/issues).
+
+* If you're unable to find an open issue addressing the problem, [open a new one](https://github.com/zjs81/meshcore-open/issues/new).
+Be sure to include a **title and clear description**, as much relevant
+information as possible, and a **code sample** or an **executable test case**
+demonstrating the expected behavior that is not occurring. You can also include
+screenshots or video.
+
+* DO NOT start work and submit a PR at this time, please discuss the issue and
+your implementation plan first.
+
+### **Did you fix whitespace, format code, or make a purely cosmetic patch?**
+
+Changes that are cosmetic in nature and do not add anything substantial to the
+stability, functionality, or testability of the application will generally not
+be accepted.
+
+### **Do you intend to add a new feature or change an existing one?**
+
+* Suggest your change in a new issue as a feature request.
+
+* DO NOT start work and submit a PR at this time, please discuss the change and
+your implementation plan first.
+
+* After it is generally decided that the feature or change fits the goals of the
+project you can start work or open a PR if you have already started.
+
+## Submitting your patch
+
+* All changes should be based on the `dev` branch. When creating your PR please
+be sure to change the target to merge into dev, and when starting work on a new
+branch be sure to start on latest `dev`.
+
+* Ensure the PR description clearly describes the problem and solution. Include
+the relevant issue number if applicable.
+
+* The PR should contain **one commit** only, the commit message should have a
+clear title followed by a new line and then brief description if needed. PR with
+multiple commits will be squashed into one before merging if required. See
+[Git Mastery](https://git-mastery.org/lessons/commitMessage/) for more
+information on good commit messages.
+
+* **Before committing changes** on your branch, be sure to run both
+`dart format .` and `flutter analyze`. The continuous development checks will
+fail if issues here are not addressed before hand.
+
+## AI-use
+
+Everyone loves some help, AI agents are a tool in many of our belts. The project
+is not anti-AI.
+
+There are some limits to acceptable use however. Generally:
+
+* All code generated by AI should be thoroughly reviewed by the contributor.
+* The changes should be tightly controlled to not change anything out of scope
+for the patch, bug fix, etc.
+* The contributor should have a good understanding of what the code does and how
+the application works in order to effectively be able to manage the agent.
diff --git a/README.md b/README.md
index 2acb390..ac188f6 100644
--- a/README.md
+++ b/README.md
@@ -6,6 +6,10 @@ Open-source Flutter client for MeshCore LoRa mesh networking devices.
MeshCore Open is a cross-platform mobile application for communicating with MeshCore LoRa mesh network devices via Bluetooth Low Energy (BLE). The app enables long-range, off-grid communication through peer-to-peer messaging, public channels, and mesh networking capabilities.
+
+
+
+
## Screenshots
@@ -21,6 +25,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
## Features
### Core Functionality
+
- **Direct Messaging**: Private encrypted conversations with individual contacts
- **Public Channels**: Broadcast messages to channel subscribers on the mesh network
- **Contact Management**: Organize contacts, track last seen times, and manage conversation history
@@ -29,6 +34,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
- **Message Replies**: Thread conversations with inline reply functionality
### Mesh Network
+
- **Path Visualization**: View routing paths and signal quality for each contact
- **Route Management**: Manual path overriding and automatic route rotation
- **Signal Metrics**: Real-time SNR (Signal-to-Noise Ratio) tracking
@@ -36,6 +42,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
- **Repeater Support**: Connect to and manage repeater nodes for extended range
### Map & Location
+
- **Live Map View**: Real-time visualization of mesh network nodes on an interactive map
- **Node Filtering**: Filter by node type (chat, repeater, sensor) and time range
- **Location Sharing**: Share GPS coordinates and custom markers with contacts
@@ -43,12 +50,14 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
- **MGRS Coordinates**: Support for Military Grid Reference System coordinate format
### Device Management
-- **BLE Connection**: Scan and connect to MeshCore devices via Bluetooth
+
+- **BLE, USB, TCP Connection**: Scan and connect to MeshCore devices via Bluetooth, USB or TCP
- **Device Settings**: Configure radio parameters, power settings, and network options
- **Battery Monitoring**: Real-time battery status with chemistry-specific voltage curves
- **Firmware Updates**: Over-the-air firmware updates via BLE (coming soon)
### Repeater Hub
+
- **CLI Access**: Full command-line interface to repeater nodes
- **Settings Management**: Configure repeater behavior, power limits, and network settings
- **Statistics Dashboard**: View repeater traffic, connected clients, and system health
@@ -57,6 +66,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
## Technical Details
### Architecture
+
- **Framework**: Flutter 3.38.5 / Dart 3.10.4
- **State Management**: Provider pattern with ChangeNotifier
- **BLE Protocol**: Nordic UART Service (NUS) over Bluetooth Low Energy
@@ -64,11 +74,20 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
- **Encryption**: End-to-end encryption for private messages using the MeshCore protocol
### Platform Support
-- ✅ **Android**: Full support (API 21+)
-- ✅ **iOS**: Full support (iOS 12+)
-- 🚧 **Desktop**: Limited support (macOS/Linux/Windows)
+
+| Feature | Android (API 21+) | iOS (12+) | Linux | Windows | macOS | Web |
+|--------------------|:-----------------:|:---------:|:-----:|:-------:|:-----:|:---------------------------------:|
+| BLE companion | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| USB companion | ✅ | 🚧 | ✅ | ✅ | ✅ | ✅ |
+| TCP companion | ✅ | 🚧 | ✅ | ✅ | ✅ | ❌ (requires websocket bridge) |
+| Core Functionality | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Mesh Network | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Map & Location | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Device Management | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
+| Repeater Hub | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
### Dependencies
+
| Package | Purpose |
|---------|---------|
| flutter_blue_plus | Bluetooth Low Energy communication |
@@ -84,6 +103,7 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
## Getting Started
### Prerequisites
+
- Flutter SDK 3.38.5 or later
- Android Studio / Xcode (for mobile development)
- A MeshCore-compatible LoRa device
@@ -91,17 +111,20 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
### Installation
1. **Clone the repository**
+
```bash
git clone https://github.com/zjs81/meshcore-open.git
cd meshcore-open
```
2. **Install dependencies**
+
```bash
flutter pub get
```
3. **Run the app**
+
```bash
flutter run
```
@@ -109,11 +132,13 @@ MeshCore Open is a cross-platform mobile application for communicating with Mesh
### Building for Release
**Android APK:**
+
```bash
flutter build apk --release
```
**iOS:**
+
```bash
flutter build ios --release
```
@@ -125,7 +150,8 @@ lib/
├── main.dart # App entry point
├── connector/
│ ├── meshcore_connector.dart # BLE communication & state management
-│ └── meshcore_protocol.dart # Protocol definitions & frame parsing
+│ ├── meshcore_protocol.dart # Protocol definitions & frame parsing
+│ └── meshcore_uuids.dart # Device names and IDs (add prefixes here!)
├── screens/
│ ├── scanner_screen.dart # Device scanning (home screen)
│ ├── contacts_screen.dart # Contact list
@@ -152,25 +178,39 @@ lib/
## BLE Protocol
### Nordic UART Service (NUS)
+
- **Service UUID**: `6e400001-b5a3-f393-e0a9-e50e24dcca9e`
- **RX Characteristic**: `6e400002-b5a3-f393-e0a9-e50e24dcca9e` (Write to device)
- **TX Characteristic**: `6e400003-b5a3-f393-e0a9-e50e24dcca9e` (Notify from device)
### Device Discovery
-Devices are discovered by scanning for BLE advertisements with the name prefix `MeshCore-`
+
+Devices are discovered by scanning for BLE advertisements with known MeshCore device name prefixes. These are currently:
+ - `MeshCore-`
+ - `Whisper-`
+ - `WisCore-`
+ - `HT-`
+ - `LowMesh_MC_`
+
+New device prefixes can be added in `lib/connector/meshcore_uuids.dart`.
+
### Message Format
+
Messages are transmitted as binary frames using a custom protocol optimized for LoRa transmission. See `meshcore_protocol.dart` for frame structure definitions.
## Configuration
### App Settings
+
- **Theme**: System default, light, or dark mode
+- **Language**: Use one of 15 languages (English, Chinese, French, Spanish, Portuguese, German, Dutch, Polish, Swedish, Italian, Slovak, Slovene, Bulgarian, Russian, Ukrainian)
- **Notifications**: Configurable for messages, channels, and node advertisements
- **Battery Chemistry**: Support for NMC, LiFePO4, and LiPo battery types
- **Message Retry**: Automatic retry with configurable path clearing
### Device Settings
+
- **Radio Power**: Transmit power adjustment (10-30 dBm)
- **Frequency**: LoRa frequency configuration
- **Bandwidth**: Channel bandwidth selection
@@ -182,22 +222,24 @@ Messages are transmitted as binary frames using a custom protocol optimized for
This is an open-source project. Contributions are welcome!
### Development Guidelines
+
- Follow the Flutter style guide
- Use Material 3 design components
- Write clear commit messages
- Test on both Android and iOS before submitting PRs
### Code Style
+
- Prefer `StatelessWidget` with `Consumer` for reactive UI
- Use `const` constructors where possible
- Keep functions small and focused
- Avoid premature abstractions
-
+- Run dart format on all changes before submitting
## Support
For issues, questions, or feature requests, please open an issue on GitHub:
-https://github.com/zjs81/meshcore-open/issues
+
## Donate
@@ -205,6 +247,11 @@ If you find MeshCore Open useful and would like to support development, you can
**Solana Address:** `F15YanjZj96YTBtKJYgNa8RLQLCZkx5CEwogPWkqXeoQ`
+
+**Monero Address:** `453TxnpUqjkJtXxzdjMsrgERNkBRXEGamPbpC45ENrvKAk9tH7kZbxWF82Hz66etgDZyXFPEBU2JUEqhLeJyWt9kBvTVy5m`
+
+**Bitcoin Address:** `bc1qh45x28v8dslcg4v4upmqd9g0mvc3lnyffmyzr5`
+
Your support helps maintain and improve this open-source project!
## Acknowledgments
diff --git a/TESTFLIGHT_GUIDE.md b/TESTFLIGHT_GUIDE.md
new file mode 100644
index 0000000..b092678
--- /dev/null
+++ b/TESTFLIGHT_GUIDE.md
@@ -0,0 +1,244 @@
+# TestFlight and App Store Deployment Guide
+
+## Prerequisites
+
+- [x] Apple Developer Account ($99/year) - [developer.apple.com](https://developer.apple.com)
+- [x] Xcode installed
+- [x] Apple Transporter app installed
+- [x] App icons ready (1024x1024px)
+- [x] Bundle ID configured: `com.monitormx.meshcoreopen`
+
+## Step 1: Register Bundle Identifier
+
+1. Go to [Apple Developer - Identifiers](https://developer.apple.com/account/resources/identifiers/list)
+2. Click the **"+"** button
+3. Select **"App IDs"** → Continue
+4. Select **"App"** → Continue
+5. Fill in:
+ - **Description**: Meshcore Open
+ - **Bundle ID**: Explicit - `com.monitormx.meshcoreopen`
+ - **Capabilities**: Leave defaults (or add as needed)
+6. Click **Continue** → **Register**
+
+## Step 2: Create App in App Store Connect
+
+1. Go to [App Store Connect](https://appstoreconnect.apple.com)
+2. Sign in with your Apple ID
+3. Click **"My Apps"**
+4. Click the **"+"** button → **"New App"**
+5. Fill in the form:
+ - **Platforms**: iOS
+ - **Name**: Meshcore Open
+ - **Primary Language**: English (U.S.)
+ - **Bundle ID**: Select `com.monitormx.meshcoreopen` from dropdown
+ - **SKU**: `meshcore-open-001` (or any unique identifier)
+ - **User Access**: Full Access
+6. Click **"Create"**
+
+## Step 3: Build the IPA
+
+Run these commands from the project directory:
+
+```bash
+# Add CocoaPods to PATH
+export PATH="/opt/homebrew/lib/ruby/gems/4.0.0/bin:$PATH"
+
+# Clean previous builds
+../flutter/bin/flutter clean
+
+# Build IPA for App Store
+../flutter/bin/flutter build ipa
+```
+
+The IPA will be created at: `build/ios/ipa/meshcore_open.ipa`
+
+## Step 4: Upload to App Store Connect via Transporter
+
+1. **Open Apple Transporter**
+ - Launch from Applications folder
+ - Sign in with your Apple ID
+
+2. **Upload the IPA**
+ - Drag and drop `build/ios/ipa/meshcore_open.ipa` into Transporter
+ - Click **"Deliver"**
+ - Wait for upload to complete (usually 1-5 minutes)
+
+3. **Processing**
+ - Apple will process your build (10-30 minutes)
+ - You'll receive an email when processing is complete
+
+## Step 5: Configure App Store Connect Metadata
+
+### App Information
+1. In App Store Connect, go to your app
+2. Fill in required information:
+ - **Subtitle**: Short description (30 chars max)
+ - **Privacy Policy URL**: Required for Bluetooth apps
+ - **Category**: Utilities or Productivity
+ - **Age Rating**: Complete questionnaire
+
+### App Store Listing
+1. Go to **App Store** tab
+2. Upload **Screenshots** (required):
+ - iPhone 6.7" display (1290 x 2796 pixels) - At least 1 screenshot
+ - iPhone 6.5" display (1242 x 2688 pixels) - At least 1 screenshot
+ - Optional: iPad screenshots
+
+3. Fill in **Description**:
+ ```
+ Meshcore Open is a Flutter client for MeshCore LoRa mesh networking devices.
+
+ Features:
+ - BLE connectivity to MeshCore devices
+ - Real-time mesh network communication
+ - Map visualization with OpenStreetMap
+ - Community management with QR code scanning
+ - Message tracking and retry system
+
+ Connect to your MeshCore LoRa device and start communicating over the mesh network.
+ ```
+
+4. **Keywords**: `lora,mesh,networking,bluetooth,communication`
+5. **Support URL**: Your GitHub or website URL
+6. **Marketing URL**: (Optional)
+
+### Version Information
+1. **What's New in This Version**:
+ ```
+ Initial release of Meshcore Open
+
+ - BLE device connectivity
+ - Mesh network messaging
+ - Map integration
+ - Community features
+ ```
+
+2. **Build**: Select the uploaded build once processing completes
+
+## Step 6: TestFlight Setup
+
+### Internal Testing (No Review Required)
+1. Go to **TestFlight** tab in App Store Connect
+2. Click **Internal Testing** → **"+"** to create a group
+3. Name your group (e.g., "Internal Testers")
+4. Add yourself as a tester using your email
+5. Select the build you uploaded
+6. Testers will receive an email with TestFlight invitation
+
+### External Testing (Requires Beta Review)
+1. Click **External Testing** → **"+"** to create a group
+2. Add build and testers
+3. Fill in **Test Information**:
+ - **What to Test**: Brief description of features
+ - **Feedback Email**: Your email address
+4. Click **Submit for Review**
+5. Beta review typically takes 24-48 hours
+
+## Step 7: App Store Submission
+
+Once you're ready for public release:
+
+1. Go to **App Store** tab
+2. Complete all required metadata (if not done)
+3. Select your build
+4. Fill in **App Review Information**:
+ - **Contact Information**: Your name, phone, email
+ - **Demo Account**: If app requires login
+ - **Notes**: Any special instructions for reviewers
+5. Answer **Export Compliance** questions:
+ - Does your app use encryption? **Yes** (uses TLS/HTTPS)
+ - Is encryption registration required? **No** (standard encryption)
+6. Click **Add for Review**
+7. Review summary and click **Submit to App Review**
+
+## Step 8: After Submission
+
+- **App Review**: Typically 24-48 hours
+- **Common Rejection Reasons**:
+ - Missing privacy policy
+ - Incomplete app information
+ - Crashes or bugs
+ - Misleading app description
+
+- **If Approved**: You can release immediately or schedule a release date
+- **If Rejected**: Address issues and resubmit
+
+## Updating the App
+
+When you need to release an update:
+
+1. **Update version** in `pubspec.yaml`:
+ ```yaml
+ version: 0.5.0+6 # Increment version (0.5.0) and build number (+6)
+ ```
+
+2. **Build new IPA**:
+ ```bash
+ export PATH="/opt/homebrew/lib/ruby/gems/4.0.0/bin:$PATH"
+ ../flutter/bin/flutter clean
+ ../flutter/bin/flutter build ipa
+ ```
+
+3. **Upload via Transporter** (same process as above)
+
+4. **Create new version** in App Store Connect:
+ - Click **"+"** next to versions
+ - Select version number
+ - Update "What's New" text
+ - Select new build
+ - Submit for review
+
+## macOS Build (Bonus)
+
+To build for macOS:
+
+```bash
+export PATH="/opt/homebrew/lib/ruby/gems/4.0.0/bin:$PATH"
+../flutter/bin/flutter build macos --release
+cd build/macos/Build/Products/Release
+zip -r meshcore_open-macos.zip meshcore_open.app
+```
+
+Distribution:
+- Share the zip file directly
+- Users unzip and drag to Applications
+- First run: Right-click → Open (to bypass Gatekeeper)
+
+## Troubleshooting
+
+### Build Errors
+- **CocoaPods not found**: Ensure PATH includes `/opt/homebrew/lib/ruby/gems/4.0.0/bin`
+- **No signing certificate**: Configure Team in Xcode (Signing & Capabilities)
+- **Bundle ID mismatch**: Check `ios/Runner.xcodeproj/project.pbxproj`
+
+### Upload Errors
+- **No profiles found**: Create app in App Store Connect first
+- **Bundle ID not registered**: Register in Apple Developer portal
+- **Authentication failed**: Use Transporter app instead of CLI
+
+### TestFlight Issues
+- **Build not appearing**: Wait 10-30 minutes for processing
+- **Can't add testers**: Check you have available slots (100 internal, 10,000 external)
+- **TestFlight crashes**: Check device logs in Xcode → Devices & Simulators
+
+## Important Files
+
+- **iOS IPA**: `build/ios/ipa/meshcore_open.ipa`
+- **macOS App**: `build/macos/Build/Products/Release/meshcore_open.app`
+- **Bundle ID Config**: `ios/Runner.xcodeproj/project.pbxproj`
+- **Version Info**: `pubspec.yaml`
+
+## Useful Links
+
+- [App Store Connect](https://appstoreconnect.apple.com)
+- [Apple Developer Portal](https://developer.apple.com/account)
+- [TestFlight Documentation](https://developer.apple.com/testflight/)
+- [App Store Review Guidelines](https://developer.apple.com/app-store/review/guidelines/)
+- [Flutter iOS Deployment](https://docs.flutter.dev/deployment/ios)
+
+## Support
+
+For issues with:
+- **App Store Process**: [Apple Developer Support](https://developer.apple.com/contact/)
+- **Flutter Build Issues**: [Flutter GitHub](https://github.com/flutter/flutter/issues)
+- **Meshcore Open App**: [GitHub Issues](https://github.com/wel97459/meshcore-open/issues)
diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts
index 740451b..c8028e0 100644
--- a/android/app/build.gradle.kts
+++ b/android/app/build.gradle.kts
@@ -16,16 +16,16 @@ if (keystorePropertiesFile.exists()) {
android {
namespace = "com.meshcore.meshcore_open"
compileSdk = flutter.compileSdkVersion
- ndkVersion = flutter.ndkVersion
+ ndkVersion = "29.0.14206865"
compileOptions {
- sourceCompatibility = JavaVersion.VERSION_11
- targetCompatibility = JavaVersion.VERSION_11
+ sourceCompatibility = JavaVersion.VERSION_17
+ targetCompatibility = JavaVersion.VERSION_17
isCoreLibraryDesugaringEnabled = true
}
kotlinOptions {
- jvmTarget = JavaVersion.VERSION_11.toString()
+ jvmTarget = JavaVersion.VERSION_17.toString()
}
defaultConfig {
@@ -83,5 +83,5 @@ flutter {
}
dependencies {
- coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4")
+ coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.4")
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index 43cacc9..4ff626f 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -19,6 +19,7 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt
index 4350b1e..9022c8b 100644
--- a/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MainActivity.kt
@@ -1,5 +1,18 @@
package com.meshcore.meshcore_open
import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
-class MainActivity : FlutterActivity()
+class MainActivity : FlutterActivity() {
+ private val usbFunctions by lazy { MeshcoreUsbFunctions(this) }
+
+ override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+ usbFunctions.configureFlutterEngine(flutterEngine)
+ }
+
+ override fun onDestroy() {
+ usbFunctions.dispose()
+ super.onDestroy()
+ }
+}
diff --git a/android/app/src/main/kotlin/com/meshcore/meshcore_open/MeshcoreUsbFunctions.kt b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MeshcoreUsbFunctions.kt
new file mode 100644
index 0000000..279ba8a
--- /dev/null
+++ b/android/app/src/main/kotlin/com/meshcore/meshcore_open/MeshcoreUsbFunctions.kt
@@ -0,0 +1,582 @@
+package com.meshcore.meshcore_open
+
+import android.app.PendingIntent
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.content.IntentFilter
+import android.hardware.usb.UsbConstants
+import android.hardware.usb.UsbDevice
+import android.hardware.usb.UsbDeviceConnection
+import android.hardware.usb.UsbEndpoint
+import android.hardware.usb.UsbInterface
+import android.hardware.usb.UsbManager
+import android.os.Build
+import android.os.Handler
+import android.os.Looper
+import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.EventChannel
+import io.flutter.plugin.common.MethodCall
+import io.flutter.plugin.common.MethodChannel
+import java.util.Locale
+import java.util.concurrent.ExecutorService
+import java.util.concurrent.Executors
+
+class MeshcoreUsbFunctions(
+ private val activity: FlutterActivity,
+) {
+ private companion object {
+ const val usbRecipientInterface = 0x01
+ }
+
+ private val usbMethodChannelName = "meshcore_open/android_usb_serial"
+ private val usbEventChannelName = "meshcore_open/android_usb_serial_events"
+ private val usbPermissionAction = "com.meshcore.meshcore_open.USB_PERMISSION"
+
+ private val usbManager by lazy {
+ activity.getSystemService(Context.USB_SERVICE) as UsbManager
+ }
+ private val mainHandler = Handler(Looper.getMainLooper())
+ private val usbIoExecutor: ExecutorService = Executors.newSingleThreadExecutor()
+
+ @Volatile private var eventSink: EventChannel.EventSink? = null
+ @Volatile private var usbConnection: UsbDeviceConnection? = null
+ @Volatile private var usbInEndpoint: UsbEndpoint? = null
+ @Volatile private var usbOutEndpoint: UsbEndpoint? = null
+ @Volatile private var controlInterface: UsbInterface? = null
+ @Volatile private var dataInterface: UsbInterface? = null
+ private var readThread: Thread? = null
+ @Volatile private var isReading = false
+ @Volatile private var connectedDeviceName: String? = null
+
+ private var pendingConnectResult: MethodChannel.Result? = null
+ private var pendingConnectPortName: String? = null
+ private var pendingConnectBaudRate: Int = 115200
+
+ private data class PortConfig(
+ val controlInterface: UsbInterface?,
+ val dataInterface: UsbInterface,
+ val inEndpoint: UsbEndpoint,
+ val outEndpoint: UsbEndpoint,
+ )
+
+ private val permissionReceiver =
+ object : BroadcastReceiver() {
+ override fun onReceive(context: Context?, intent: Intent?) {
+ when (intent?.action) {
+ UsbManager.ACTION_USB_DEVICE_DETACHED -> {
+ handleUsbDetached(intent)
+ return
+ }
+ usbPermissionAction -> Unit
+ else -> return
+ }
+
+ val result = pendingConnectResult
+ val portName = pendingConnectPortName
+ pendingConnectResult = null
+ pendingConnectPortName = null
+
+ if (result == null || portName == null) {
+ return
+ }
+
+ val device = findUsbDevice(portName)
+ if (device == null) {
+ result.error(
+ "usb_device_missing",
+ null,
+ null,
+ )
+ return
+ }
+
+ val granted =
+ intent.getBooleanExtra(UsbManager.EXTRA_PERMISSION_GRANTED, false)
+ if (!granted || !usbManager.hasPermission(device)) {
+ result.error("usb_permission_denied", null, null)
+ return
+ }
+
+ openUsbDevice(device, pendingConnectBaudRate, result)
+ }
+ }
+
+ fun configureFlutterEngine(flutterEngine: FlutterEngine) {
+ registerUsbPermissionReceiver()
+
+ MethodChannel(flutterEngine.dartExecutor.binaryMessenger, usbMethodChannelName)
+ .setMethodCallHandler { call, result ->
+ when (call.method) {
+ "listPorts" -> result.success(listUsbPorts())
+ "connect" -> handleUsbConnect(call, result)
+ "write" -> handleUsbWrite(call, result)
+ "disconnect" -> {
+ scheduleCloseUsbConnection {
+ result.success(null)
+ }
+ }
+ else -> result.notImplemented()
+ }
+ }
+
+ EventChannel(flutterEngine.dartExecutor.binaryMessenger, usbEventChannelName)
+ .setStreamHandler(
+ object : EventChannel.StreamHandler {
+ override fun onListen(arguments: Any?, events: EventChannel.EventSink) {
+ eventSink = events
+ }
+
+ override fun onCancel(arguments: Any?) {
+ eventSink = null
+ }
+ },
+ )
+ }
+
+ fun dispose() {
+ closeUsbConnection()
+ usbIoExecutor.shutdownNow()
+ try {
+ activity.unregisterReceiver(permissionReceiver)
+ } catch (_: IllegalArgumentException) {
+ }
+ }
+
+ private fun registerUsbPermissionReceiver() {
+ val filter =
+ IntentFilter().apply {
+ addAction(usbPermissionAction)
+ addAction(UsbManager.ACTION_USB_DEVICE_DETACHED)
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ activity.registerReceiver(permissionReceiver, filter, Context.RECEIVER_NOT_EXPORTED)
+ } else {
+ @Suppress("DEPRECATION")
+ activity.registerReceiver(permissionReceiver, filter)
+ }
+ }
+
+ private fun listUsbPorts(): List {
+ return usbManager.deviceList.values.map { device ->
+ val productName = device.productName ?: "USB Serial Device"
+ val vendorProduct =
+ String.format(
+ Locale.US,
+ "VID:%04X PID:%04X",
+ device.vendorId,
+ device.productId,
+ )
+ "${device.deviceName} - $productName - $vendorProduct"
+ }
+ }
+
+ private fun handleUsbConnect(call: MethodCall, result: MethodChannel.Result) {
+ val portName = call.argument("portName")
+ val baudRate = call.argument("baudRate") ?: 115200
+ if (portName.isNullOrBlank()) {
+ result.error("usb_invalid_port", null, null)
+ return
+ }
+
+ val device = findUsbDevice(portName)
+ if (device == null) {
+ result.error("usb_device_missing", null, null)
+ return
+ }
+
+ if (usbManager.hasPermission(device)) {
+ openUsbDevice(device, baudRate, result)
+ return
+ }
+
+ if (pendingConnectResult != null) {
+ result.error("usb_busy", null, null)
+ return
+ }
+
+ pendingConnectResult = result
+ pendingConnectPortName = portName
+ pendingConnectBaudRate = baudRate
+
+ val permissionIntent = PendingIntent.getBroadcast(
+ activity,
+ 0,
+ Intent(usbPermissionAction).setPackage(activity.packageName),
+ pendingIntentFlags(),
+ )
+ usbManager.requestPermission(device, permissionIntent)
+ }
+
+ private fun handleUsbWrite(call: MethodCall, result: MethodChannel.Result) {
+ val data = call.argument("data")
+ val connection = usbConnection
+ val endpoint = usbOutEndpoint
+ if (data == null) {
+ result.error("usb_invalid_data", null, null)
+ return
+ }
+ if (connection == null || endpoint == null) {
+ result.error("usb_not_connected", null, null)
+ return
+ }
+
+ usbIoExecutor.execute {
+ try {
+ writeToDevice(data)
+ mainHandler.post { result.success(null) }
+ } catch (error: Exception) {
+ mainHandler.post {
+ result.error("usb_write_failed", error.message, null)
+ }
+ }
+ }
+ }
+
+ private fun findUsbDevice(portName: String): UsbDevice? {
+ val devices = usbManager.deviceList.values
+ val exactMatch = devices.firstOrNull { it.deviceName == portName }
+ if (exactMatch != null) {
+ return exactMatch
+ }
+
+ val normalizedName = portName.substringBefore(" - ").trim()
+ return devices.firstOrNull { it.deviceName == normalizedName }
+ }
+
+ private fun openUsbDevice(
+ device: UsbDevice,
+ baudRate: Int,
+ result: MethodChannel.Result,
+ ) {
+ usbIoExecutor.execute {
+ try {
+ closeUsbConnection()
+
+ val config = resolvePortConfig(device)
+ if (config == null) {
+ mainHandler.post {
+ result.error(
+ "usb_driver_missing",
+ null,
+ null,
+ )
+ }
+ return@execute
+ }
+
+ val connection = usbManager.openDevice(device)
+ if (connection == null) {
+ mainHandler.post {
+ result.error(
+ "usb_open_failed",
+ null,
+ null,
+ )
+ }
+ return@execute
+ }
+
+ if (!connection.claimInterface(config.dataInterface, true)) {
+ connection.close()
+ mainHandler.post {
+ result.error(
+ "usb_open_failed",
+ null,
+ null,
+ )
+ }
+ return@execute
+ }
+
+ if (config.controlInterface != null &&
+ config.controlInterface.id != config.dataInterface.id &&
+ !connection.claimInterface(config.controlInterface, true)
+ ) {
+ connection.releaseInterface(config.dataInterface)
+ connection.close()
+ mainHandler.post {
+ result.error(
+ "usb_open_failed",
+ null,
+ null,
+ )
+ }
+ return@execute
+ }
+
+ usbConnection = connection
+ usbInEndpoint = config.inEndpoint
+ usbOutEndpoint = config.outEndpoint
+ controlInterface = config.controlInterface
+ dataInterface = config.dataInterface
+
+ configureDevice(connection, config, baudRate)
+
+ connectedDeviceName = device.deviceName
+ startReadLoop()
+
+ mainHandler.post {
+ result.success(null)
+ }
+ } catch (error: Exception) {
+ closeUsbConnection()
+ mainHandler.post {
+ result.error("usb_connect_failed", error.message, null)
+ }
+ }
+ }
+ }
+
+ private fun resolvePortConfig(device: UsbDevice): PortConfig? {
+ var preferredDataInterface: UsbInterface? = null
+ var preferredInEndpoint: UsbEndpoint? = null
+ var preferredOutEndpoint: UsbEndpoint? = null
+ var fallbackDataInterface: UsbInterface? = null
+ var fallbackInEndpoint: UsbEndpoint? = null
+ var fallbackOutEndpoint: UsbEndpoint? = null
+ var preferredControlInterface: UsbInterface? = null
+
+ for (interfaceIndex in 0 until device.interfaceCount) {
+ val usbInterface = device.getInterface(interfaceIndex)
+ var inEndpoint: UsbEndpoint? = null
+ var outEndpoint: UsbEndpoint? = null
+
+ for (endpointIndex in 0 until usbInterface.endpointCount) {
+ val endpoint = usbInterface.getEndpoint(endpointIndex)
+ if (endpoint.type != UsbConstants.USB_ENDPOINT_XFER_BULK) {
+ continue
+ }
+ when (endpoint.direction) {
+ UsbConstants.USB_DIR_IN -> if (inEndpoint == null) inEndpoint = endpoint
+ UsbConstants.USB_DIR_OUT -> if (outEndpoint == null) outEndpoint = endpoint
+ }
+ }
+
+ val hasDataPair = inEndpoint != null && outEndpoint != null
+ when {
+ usbInterface.interfaceClass == UsbConstants.USB_CLASS_COMM &&
+ preferredControlInterface == null -> {
+ preferredControlInterface = usbInterface
+ }
+ hasDataPair &&
+ usbInterface.interfaceClass == UsbConstants.USB_CLASS_CDC_DATA -> {
+ preferredDataInterface = usbInterface
+ preferredInEndpoint = inEndpoint
+ preferredOutEndpoint = outEndpoint
+ }
+ hasDataPair && fallbackDataInterface == null -> {
+ fallbackDataInterface = usbInterface
+ fallbackInEndpoint = inEndpoint
+ fallbackOutEndpoint = outEndpoint
+ }
+ }
+ }
+
+ val dataInterface = preferredDataInterface ?: fallbackDataInterface ?: return null
+ val inEndpoint = preferredInEndpoint ?: fallbackInEndpoint ?: return null
+ val outEndpoint = preferredOutEndpoint ?: fallbackOutEndpoint ?: return null
+ return PortConfig(preferredControlInterface, dataInterface, inEndpoint, outEndpoint)
+ }
+
+ private fun configureDevice(
+ connection: UsbDeviceConnection,
+ config: PortConfig,
+ baudRate: Int,
+ ) {
+ val control = config.controlInterface ?: return
+ val lineCoding =
+ byteArrayOf(
+ (baudRate and 0xFF).toByte(),
+ ((baudRate shr 8) and 0xFF).toByte(),
+ ((baudRate shr 16) and 0xFF).toByte(),
+ ((baudRate shr 24) and 0xFF).toByte(),
+ 0, // stop bits: 1
+ 0, // parity: none
+ 8, // data bits
+ )
+
+ val lineCodingResult =
+ connection.controlTransfer(
+ UsbConstants.USB_DIR_OUT or
+ UsbConstants.USB_TYPE_CLASS or
+ usbRecipientInterface,
+ 0x20,
+ 0,
+ control.id,
+ lineCoding,
+ lineCoding.size,
+ 1000,
+ )
+ if (lineCodingResult < 0) {
+ throw IllegalStateException("Failed to configure USB line coding")
+ }
+
+ val controlLineResult =
+ connection.controlTransfer(
+ UsbConstants.USB_DIR_OUT or
+ UsbConstants.USB_TYPE_CLASS or
+ usbRecipientInterface,
+ 0x22,
+ 0x0001, // DTR on, RTS off
+ control.id,
+ null,
+ 0,
+ 1000,
+ )
+ if (controlLineResult < 0) {
+ throw IllegalStateException("Failed to configure USB control line state")
+ }
+ }
+
+ private fun startReadLoop() {
+ val connection = usbConnection ?: return
+ val endpoint = usbInEndpoint ?: return
+
+ isReading = true
+ readThread =
+ Thread({
+ val packetSize = endpoint.maxPacketSize.coerceAtLeast(64)
+ val buffer = ByteArray(packetSize * 4)
+ try {
+ while (isReading) {
+ val bytesRead = connection.bulkTransfer(endpoint, buffer, buffer.size, 250)
+ if (!isReading) {
+ break
+ }
+ if (bytesRead <= 0) {
+ continue
+ }
+ val packet = buffer.copyOf(bytesRead)
+ mainHandler.post {
+ eventSink?.success(packet)
+ }
+ }
+ } catch (error: Exception) {
+ if (isReading) {
+ mainHandler.post {
+ eventSink?.error(
+ "usb_io_error",
+ error.message ?: "USB serial I/O error",
+ null,
+ )
+ }
+ scheduleCloseUsbConnection()
+ }
+ }
+ }, "MeshCoreUsbRead").also { thread ->
+ thread.isDaemon = true
+ thread.start()
+ }
+ }
+
+ private fun writeToDevice(data: ByteArray) {
+ val connection = usbConnection ?: throw IllegalStateException("USB connection missing")
+ val endpoint = usbOutEndpoint ?: throw IllegalStateException("USB output endpoint missing")
+ var offset = 0
+ val maxPacketSize = endpoint.maxPacketSize.coerceAtLeast(64)
+ while (offset < data.size) {
+ val chunkSize = minOf(maxPacketSize, data.size - offset)
+ val chunk = data.copyOfRange(offset, offset + chunkSize)
+ val bytesWritten = connection.bulkTransfer(endpoint, chunk, chunkSize, 1000)
+ if (bytesWritten != chunkSize) {
+ throw IllegalStateException("Short USB write: wrote $bytesWritten of $chunkSize bytes")
+ }
+ offset += chunkSize
+ }
+ }
+
+ private fun scheduleCloseUsbConnection(onComplete: (() -> Unit)? = null) {
+ usbIoExecutor.execute {
+ closeUsbConnection()
+ if (onComplete != null) {
+ mainHandler.post(onComplete)
+ }
+ }
+ }
+
+ @Synchronized
+ private fun closeUsbConnection() {
+ isReading = false
+ readThread?.interrupt()
+ if (readThread != null && readThread !== Thread.currentThread()) {
+ try {
+ readThread?.join(300)
+ } catch (_: InterruptedException) {
+ Thread.currentThread().interrupt()
+ }
+ }
+ readThread = null
+
+ val connection = usbConnection
+ val claimedControl = controlInterface
+ val claimedData = dataInterface
+
+ usbInEndpoint = null
+ usbOutEndpoint = null
+ controlInterface = null
+ dataInterface = null
+ usbConnection = null
+
+ if (connection != null) {
+ if (claimedControl != null) {
+ try {
+ connection.releaseInterface(claimedControl)
+ } catch (_: Exception) {
+ }
+ }
+ if (claimedData != null && claimedData.id != claimedControl?.id) {
+ try {
+ connection.releaseInterface(claimedData)
+ } catch (_: Exception) {
+ }
+ }
+ try {
+ connection.close()
+ } catch (_: Exception) {
+ }
+ }
+ connectedDeviceName = null
+ }
+
+ private fun handleUsbDetached(intent: Intent) {
+ val detachedDevice =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ intent.getParcelableExtra(UsbManager.EXTRA_DEVICE, UsbDevice::class.java)
+ } else {
+ @Suppress("DEPRECATION")
+ intent.getParcelableExtra(UsbManager.EXTRA_DEVICE)
+ }
+
+ val detachedName = detachedDevice?.deviceName ?: return
+
+ if (pendingConnectPortName == detachedName) {
+ pendingConnectResult?.error(
+ "usb_device_detached",
+ "USB device was removed before the connection completed",
+ null,
+ )
+ pendingConnectResult = null
+ pendingConnectPortName = null
+ }
+
+ if (connectedDeviceName == detachedName) {
+ scheduleCloseUsbConnection {
+ eventSink?.error(
+ "usb_device_detached",
+ "USB device was disconnected",
+ null,
+ )
+ }
+ }
+ }
+
+ private fun pendingIntentFlags(): Int {
+ var flags = PendingIntent.FLAG_UPDATE_CURRENT
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ flags = flags or PendingIntent.FLAG_MUTABLE
+ }
+ return flags
+ }
+}
diff --git a/android/build/reports/problems/problems-report.html b/android/build/reports/problems/problems-report.html
new file mode 100644
index 0000000..2220133
--- /dev/null
+++ b/android/build/reports/problems/problems-report.html
@@ -0,0 +1,663 @@
+
+
+
+
+
+
+
+
+
+
+
+
+ Gradle Configuration Cache
+
+
+
+
+
+
+ Loading...
+
+
+
+
+
+
+
diff --git a/assets/badges/badge_obtainium.png b/assets/badges/badge_obtainium.png
new file mode 100644
index 0000000..cc3a0ed
Binary files /dev/null and b/assets/badges/badge_obtainium.png differ
diff --git a/assets/icons/done_all.svg b/assets/icons/done_all.svg
new file mode 100644
index 0000000..bfeeec0
--- /dev/null
+++ b/assets/icons/done_all.svg
@@ -0,0 +1 @@
+
\ No newline at end of file
diff --git a/docs/BLE_PROTOCOL.md b/docs/BLE_PROTOCOL.md
index 993c3ea..c17c3e7 100644
--- a/docs/BLE_PROTOCOL.md
+++ b/docs/BLE_PROTOCOL.md
@@ -21,7 +21,12 @@ The MeshCore BLE protocol implements a binary frame-based communication system u
### Connection Flow
-1. **Scan** for devices with name prefix `MeshCore-`
+1. **Scan** for devices with known name prefixes (defined in `MeshCoreUuids.deviceNamePrefixes`):
+ - `MeshCore-`
+ - `Whisper-`
+ - `WisCore-`
+ - `HT-`
+ - `LowMesh_MC_`
2. **Connect** with 15-second timeout
3. **Request MTU** of 185 bytes (falls back to default if unsupported)
4. **Discover services** and locate NUS characteristics
diff --git a/docs/screenshots/signal-ui-consistency.png b/docs/screenshots/signal-ui-consistency.png
new file mode 100644
index 0000000..2575945
Binary files /dev/null and b/docs/screenshots/signal-ui-consistency.png differ
diff --git a/documentation/README.md b/documentation/README.md
new file mode 100644
index 0000000..1367013
--- /dev/null
+++ b/documentation/README.md
@@ -0,0 +1,30 @@
+# MeshCore Open - Feature Documentation
+
+MeshCore Open is an open-source Flutter client for MeshCore LoRa mesh networking devices. This documentation covers every user-facing feature, how to access it, and what it does.
+
+## Table of Contents
+
+1. [Scanner & Connection](scanner-and-connection.md) - BLE scanning, USB serial, and TCP connection
+2. [Navigation](navigation.md) - App flow, device screen, and quick-switch navigation
+3. [Contacts](contacts.md) - Contact management, groups, discovery, and sharing
+4. [Chat & Messaging](chat-and-messaging.md) - Direct messages, message status, reactions, and retries
+5. [Channels](channels.md) - Broadcast channels, communities, and channel chat
+6. [Map & Location](map-and-location.md) - Node map, path tracing, line-of-sight, and offline caching
+7. [Settings](settings.md) - Device settings, app settings, radio configuration, and exports
+8. [Notifications](notifications.md) - System notifications, unread badges, and notification preferences
+9. [Repeater Management](repeater-management.md) - Repeater hub, status, CLI, telemetry, and neighbors
+10. [Additional Features](additional-features.md) - GIF picker, localization, debug logs, SMAZ compression, and more
+11. [BLE Protocol & Data Layer](ble-protocol.md) - Technical reference for the communication protocol and data architecture
+
+## App Overview
+
+MeshCore Open connects to MeshCore LoRa mesh radios over BLE, USB, or TCP. Once connected, users can:
+
+- **Chat** with other mesh nodes via encrypted direct messages
+- **Broadcast** on shared channels (public, hashtag, private, or community-scoped)
+- **View nodes on a map** with GPS locations, predicted positions, and path traces
+- **Manage repeaters** with CLI access, telemetry, neighbor info, and settings
+- **Share contacts** via `meshcore://` URIs and QR codes
+- **Configure radio settings** including frequency, power, bandwidth, and spreading factor
+- **Cache offline maps** for use without internet connectivity
+- **Analyze line-of-sight** between nodes with terrain elevation profiles
diff --git a/documentation/additional-features.md b/documentation/additional-features.md
new file mode 100644
index 0000000..f7b8319
--- /dev/null
+++ b/documentation/additional-features.md
@@ -0,0 +1,187 @@
+# Additional Features
+
+## GIF Picker (Giphy Integration)
+
+### How to Access
+In any chat screen (direct or channel), tap the GIF button in the message input bar.
+
+### What the User Sees
+A bottom sheet with a search field and a grid of GIF thumbnails.
+
+### Key Interactions
+- On open, loads trending GIFs (G-rated, 25 results)
+- Type to search and press the keyboard submit button (search triggers on submit, not on each keystroke). Clearing the search field reloads trending GIFs
+- On network/API errors, a "Retry" button is shown in-place
+- Tap a GIF to select it — the chat input shows an inline preview with an X button to dismiss
+- Send the message to transmit the GIF reference (`g:`)
+- Recipients see the GIF rendered inline via Giphy CDN
+- "Powered by Giphy" attribution is always shown at the bottom of the picker
+- The bottom sheet occupies 70% of screen height
+
+---
+
+## Localization / Multi-Language Support
+
+### How to Access
+App Settings → Appearance → Language
+
+### Supported Languages (15)
+English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian
+
+### How It Works
+- All UI strings go through Flutter's ARB localization system
+- Language can follow the system locale or be explicitly overridden
+- Changes take effect immediately
+
+---
+
+## Discovered Contacts Screen
+
+### How to Access
+From Contacts screen → overflow menu → "Discovered Contacts"
+
+### What the User Sees
+A list of nodes heard passively over the air but not yet added as contacts. Each shows:
+- Color-coded avatar (by type)
+- Name
+- Short public key
+- Last-seen time
+
+### Key Interactions
+- Search bar with debounced filtering
+- Sort by last seen or name; filter by type
+- **Tap**: Import the contact (adds to your contact list)
+- **Long-press**: Add Contact, Copy `meshcore://` URI to clipboard, or Delete
+- Overflow menu → "Delete All" (with confirmation)
+- Already-known contacts and your own node are filtered out
+
+---
+
+## SMAZ Compression
+
+### What It Is
+An optional per-contact and per-channel text compression feature using the SMAZ algorithm (optimized for short English text).
+
+### How to Enable
+- **Per contact**: Chat screen → info button → toggle "SMAZ compression"
+- **Per channel**: Long-press channel → Edit → toggle "SMAZ compression"
+
+### How It Works
+- When enabled, compression is applied using a "compress only if smaller" strategy — the message is only transmitted compressed if the encoded result is actually shorter than the original. Otherwise, the original text is sent uncompressed
+- Compressed messages are transmitted with a `s:` prefix followed by base64-encoded data
+- Recipients using MeshCore Open will decompress automatically. **Recipients using other software** that is not SMAZ-aware will see garbled `s:...` text
+- The codec operates on ASCII. Non-ASCII / non-English text generally does not benefit from compression and may even expand. Best suited for short English messages
+- Disabled by default
+
+---
+
+## Community QR Scanner
+
+### How to Access
+From Channels screen → "+" FAB → "Scan Community QR"
+
+### What the User Sees
+A live QR scanner view with instruction text overlay.
+
+### Key Interactions
+- Scan a community QR code shared by another member
+- On valid scan: confirmation dialog showing community name and ID
+- Option to "Add public channel to device" on join
+- If already a member: shows an "Already a member" dialog
+- Invalid QR: shows an orange error snackbar
+
+---
+
+## Channel Message Path Viewing
+
+### How to Access
+In a channel chat, tap a message bubble (mobile) or use the "Path" action (desktop).
+
+### What the User Sees
+- Summary card: sender, time, repeat count, path type, observed hops
+- "Other Observed Paths" section (if multiple paths detected)
+- "Repeater Hops" section listing each hop with hex prefix, resolved name, and GPS coordinates
+
+### Actions
+- **Radar icon**: Opens path trace map for live trace
+- **Map icon**: Opens a map with hop markers and polyline
+- **Path dropdown**: Switch between observed path variants (if multiple)
+
+---
+
+## Debug Logging
+
+### BLE Debug Log
+**Access**: Settings → BLE Debug Log
+
+Two views:
+- **Frames**: Each BLE frame with direction, description, hex preview, timestamp. Long-press to copy hex.
+- **Raw Log RX**: Decoded LoRa packets with route type, payload type, path bytes, and summary.
+
+### App Debug Log
+**Access**: Settings → App Debug Log (must be enabled first in App Settings → Debug)
+
+Structured log entries with level (Info/Warning/Error), tag, message, and timestamp.
+
+Both logs support copy-all and clear operations.
+
+---
+
+## Chrome Required Screen
+
+### When It Appears
+Automatically shown on web platforms when a non-Chromium browser is detected.
+
+### What the User Sees
+A full-screen informational page explaining that Web Bluetooth requires a Chromium-based browser. No interactive elements — purely informational.
+
+---
+
+## Path History Service
+
+### What It Does (Background Service)
+Maintains an in-memory LRU cache of up to 50 contacts, each with up to 100 route history entries, tracking:
+- Hop count and trip time
+- Success/failure counts and route weights
+- Flood vs. direct discovery
+
+### Path Scoring
+Paths are scored using a weighted formula: reliability (45%), route weight (20%), latency (25%), and freshness (10%). These weights are internal and not user-configurable. Paths whose weight drops to zero or below are automatically deleted. Flood deliveries that receive an ACK give a weight boost (+0.5) to the specific return path.
+
+Used internally for:
+- **Auto route rotation**: Cycles through known paths using configurable weights on retries, with a diversity window to avoid re-using recently tried paths
+- **Path selection**: Picks the best-scored path for each retry attempt
+- **Flood statistics**: Tracks flood vs. direct discovery ratios
+
+---
+
+## Message Retry Service
+
+### What It Does (Background Service)
+Handles reliable delivery of outgoing direct messages:
+1. Assigns a UUID and sends immediately. Only one message per contact can be in-flight at a time (avoids overflowing the firmware's 8-entry ACK table); subsequent messages are queued
+2. Listens for ACK frames matched via SHA-256 hash of `[timestamp][attempt][text][sender_pubkey]`
+3. On timeout, retries with exponential backoff: `1000 × 2^retryCount` ms (1s, 2s, 4s, 8s...)
+4. Each retry may use a different path (via path history diversity window)
+5. After max retries: marks failed but keeps a **30-second grace window** during which a late ACK can still resolve the message to "delivered". Optionally clears the contact's path
+6. Reports RTT and path data for quality learning
+7. Maintains an ACK hash history (last 50 entries) to handle duplicate ACKs
+
+### Configurable Settings (App Settings → Messaging)
+- Max retries (2–10, default 5)
+- Clear path on max retry (on/off)
+- Auto route rotation with weight parameters
+
+---
+
+## Timeout Prediction (ML)
+
+### What It Does (Background Service)
+An ML-based service that predicts expected delivery timeouts:
+- Collects delivery observations (path length, message size, time since last RX, delivery time) in a sliding window of up to 100 observations (oldest evicted first)
+- Requires **10 minimum observations** before first training. After that, retrains every 5 new observations
+- Applies a **1.5x safety margin** to raw predictions (the actual timeout issued is 1.5× the model's predicted delivery time)
+- Features with zero variance are automatically excluded from training
+- Blends per-contact statistics with ML predictions
+- Falls back to `3000 + 3000 × pathLength` ms when insufficient data
+- Observations are persisted to storage via a 2-second debounced timer (observations within 2s of app termination may be lost)
diff --git a/documentation/ble-protocol.md b/documentation/ble-protocol.md
new file mode 100644
index 0000000..ec24094
--- /dev/null
+++ b/documentation/ble-protocol.md
@@ -0,0 +1,254 @@
+# BLE Protocol & Data Layer
+
+This is a technical reference for the communication protocol and data architecture.
+
+## Transport Layer
+
+The app supports three transports, all sharing the same command/response protocol:
+
+| Transport | Method | Implementation |
+|---|---|---|
+| Bluetooth LE | Nordic UART Service (NUS) GATT | `flutter_blue_plus` |
+| USB Serial | Packet-framed serial | `MeshCoreUsbManager` |
+| TCP | Packet-framed socket | `MeshCoreTcpConnector` |
+
+### BLE (Nordic UART Service)
+
+- **Service UUID**: `6e400001-b5a3-f393-e0a9-e50e24dcca9e`
+- **RX Characteristic** (write to device): `6e400002-b5a3-f393-e0a9-e50e24dcca9e`
+- **TX Characteristic** (notify from device): `6e400003-b5a3-f393-e0a9-e50e24dcca9e`
+
+Raw `Uint8List` payloads are written directly to the RX characteristic. Writes use "write without response" if supported, falling back to "write with response".
+
+### USB and TCP Framing
+
+Both use a lightweight packet framing codec:
+
+```
+TX (host → device): [0x3C][len_lo][len_hi][payload...]
+RX (device → host): [0x3E][len_lo][len_hi][payload...]
+```
+
+- Frame start: `0x3C` (`<`) for outgoing, `0x3E` (`>`) for incoming
+- Length: 2-byte little-endian, payload only
+- Max payload: 172 bytes
+- TCP: `tcpNoDelay: true` (Nagle disabled), writes serialized to prevent interleaving
+- USB: 10ms post-write delay between frames
+
+## Connection State Machine
+
+```
+enum MeshCoreConnectionState {
+ disconnected,
+ scanning,
+ connecting,
+ connected,
+ disconnecting,
+}
+```
+
+## BLE Connection Lifecycle
+
+1. **Scan** with known name prefixes (defined in `MeshCoreUuids.deviceNamePrefixes`):
+ - `MeshCore-`
+ - `Whisper-`
+ - `WisCore-`
+ - `HT-`
+ - `LowMesh_MC_`
+2. **Connect** with 15-second timeout
+3. **Request MTU** 185 bytes (non-web only)
+4. **Discover services** and locate NUS
+5. **Enable TX notifications** (up to 3 attempts on native)
+6. **Subscribe** to TX characteristic for incoming frames
+7. **Initial sync**: device info query, time sync, channel sync
+
+## Auto-Reconnect (BLE Only)
+
+On unexpected disconnection, auto-reconnect with exponential backoff:
+- Delays: 1s, 2s, 4s, 8s, 16s, 30s, 30s...
+- Resets on successful connection
+- Disabled for manual disconnects
+- Not available for USB or TCP
+
+## Protocol Constants
+
+| Constant | Value | Description |
+|---|---|---|
+| Max frame size | 172 bytes | BLE/USB/TCP payload limit |
+| Public key size | 32 bytes | Ed25519 public key |
+| Max path size | 64 bytes | Maximum path data |
+| Max name size | 32 bytes | Maximum node name |
+| Max text payload | 160 bytes | Firmware `MAX_TEXT_LEN` |
+| App protocol version | 3 | Sent in device query |
+| Contact frame size | 148 bytes | Fixed-size contact record |
+
+## Command Codes (App → Device)
+
+| Code | Name | Description |
+|------|------|-------------|
+| 1 | CMD_APP_START | Announce app connection |
+| 2 | CMD_SEND_TXT_MSG | Send direct text message |
+| 3 | CMD_SEND_CHANNEL_TXT_MSG | Send channel text message |
+| 4 | CMD_GET_CONTACTS | Request contact list |
+| 5 | CMD_GET_DEVICE_TIME | Query device clock |
+| 6 | CMD_SET_DEVICE_TIME | Set device clock |
+| 7 | CMD_SEND_SELF_ADVERT | Broadcast own advertisement |
+| 8 | CMD_SET_ADVERT_NAME | Set node name |
+| 9 | CMD_ADD_UPDATE_CONTACT | Add or update a contact |
+| 10 | CMD_SYNC_NEXT_MESSAGE | Request next queued message |
+| 11 | CMD_SET_RADIO_PARAMS | Set radio parameters |
+| 12 | CMD_SET_RADIO_TX_POWER | Set TX power |
+| 13 | CMD_RESET_PATH | Reset contact path |
+| 14 | CMD_SET_ADVERT_LATLON | Set advertised location |
+| 15 | CMD_REMOVE_CONTACT | Remove a contact |
+| 16 | CMD_SHARE_CONTACT | Share contact to mesh |
+| 17 | CMD_EXPORT_CONTACT | Export contact as bytes |
+| 18 | CMD_IMPORT_CONTACT | Import contact from bytes |
+| 19 | CMD_REBOOT | Reboot device |
+| 20 | CMD_GET_BATT_AND_STORAGE | Query battery and storage |
+| 22 | CMD_DEVICE_QUERY | Query device info |
+| 26 | CMD_SEND_LOGIN | Login to repeater/room |
+| 27 | CMD_SEND_STATUS_REQ | Request repeater status |
+| 30 | CMD_GET_CONTACT_BY_KEY | Get contact by public key |
+| 31 | CMD_GET_CHANNEL | Get channel definition |
+| 32 | CMD_SET_CHANNEL | Set channel name and PSK |
+| 36 | CMD_SEND_TRACE_PATH | Request path trace |
+| 38 | CMD_SET_OTHER_PARAMS | Set misc parameters |
+| 39 | CMD_GET_TELEMETRY_REQ | Request sensor telemetry |
+| 40 | CMD_GET_CUSTOM_VAR | Get custom variables |
+| 41 | CMD_SET_CUSTOM_VAR | Set a custom variable |
+| 50 | CMD_SEND_BINARY_REQ | Send binary request |
+| 57 | CMD_SEND_ANON_REQ | Send anonymous request |
+| 58 | CMD_SET_AUTO_ADD_CONFIG | Set auto-add configuration |
+| 59 | CMD_GET_AUTO_ADD_CONFIG | Get auto-add configuration |
+
+## Response / Push Codes (Device → App)
+
+| Code | Name | Description |
+|------|------|-------------|
+| 0 | RESP_CODE_OK | Generic success |
+| 1 | RESP_CODE_ERR | Generic error |
+| 2 | RESP_CODE_CONTACTS_START | Contact list begins |
+| 3 | RESP_CODE_CONTACT | Single contact data |
+| 4 | RESP_CODE_END_OF_CONTACTS | Contact list complete |
+| 5 | RESP_CODE_SELF_INFO | Device self-info response |
+| 6 | RESP_CODE_SENT | Message transmitted; carries `[1]=is_flood, [2–5]=ack_hash, [6–9]=estimated_timeout_ms` |
+| 7 | RESP_CODE_CONTACT_MSG_RECV | Incoming direct message (v2) |
+| 8 | RESP_CODE_CHANNEL_MSG_RECV | Incoming channel message (v2) |
+| 10 | RESP_CODE_NO_MORE_MESSAGES | No more queued messages |
+| 11 | RESP_CODE_EXPORT_CONTACT | Exported contact data |
+| 9 | RESP_CODE_CURR_TIME | Current device time |
+| 12 | RESP_CODE_BATT_AND_STORAGE | Battery mV (uint16 LE) + storage used/total (uint32 LE each) |
+| 13 | RESP_CODE_DEVICE_INFO | Firmware info |
+| 16 | RESP_CODE_CONTACT_MSG_RECV_V3 | Incoming direct message (v3) |
+| 17 | RESP_CODE_CHANNEL_MSG_RECV_V3 | Incoming channel message (v3) |
+| 18 | RESP_CODE_CHANNEL_INFO | Channel definition |
+| 21 | RESP_CODE_CUSTOM_VARS | Custom variables |
+| 25 | RESP_CODE_AUTO_ADD_CONFIG | Auto-add flags |
+| 0x80 | PUSH_CODE_ADVERT | Known contact re-seen |
+| 0x81 | PUSH_CODE_PATH_UPDATED | Better path found; carries the 32-byte public key of the updated contact |
+| 0x82 | PUSH_CODE_SEND_CONFIRMED | Delivery ACK from remote; carries ACK hash (4 bytes) + trip time (4 bytes) |
+| 0x83 | PUSH_CODE_MSG_WAITING | Offline messages queued |
+| 0x85 | PUSH_CODE_LOGIN_SUCCESS | Repeater/room login succeeded |
+| 0x86 | PUSH_CODE_LOGIN_FAIL | Repeater/room login failed |
+| 0x87 | PUSH_CODE_STATUS_RESPONSE | Repeater status response |
+| 0x88 | PUSH_CODE_LOG_RX_DATA | Radio RX data with SNR (int8, units 1/4 dB), RSSI, and raw radio packet |
+| 0x89 | PUSH_CODE_TRACE_DATA | Path trace result |
+| 0x8A | PUSH_CODE_NEW_ADVERT | New node discovered |
+| 0x8B | PUSH_CODE_TELEMETRY_RESPONSE | Sensor telemetry data |
+| 0x8C | PUSH_CODE_BINARY_RESPONSE | Binary data response |
+
+## Data Models
+
+### Contact
+32-byte public key (primary identity), name, type (chat/repeater/room/sensor), flags, path data, GPS coordinates, last-seen timestamp. Parsed from 148-byte firmware frames with this layout:
+
+```
+[0] = resp_code
+[1–32] = public key (32 bytes)
+[33] = type (1=chat, 2=repeater, 3=room, 4=sensor)
+[34] = flags (bit 0 = favorite)
+[35] = path_length
+[36–99] = path (64 bytes)
+[100–131] = name (32 bytes, null-padded)
+[132–135] = timestamp (uint32 LE)
+[136–139] = latitude (int32 LE, × 1e-6 degrees)
+[140–143] = longitude (int32 LE, × 1e-6 degrees)
+[144–147] = last_modified (uint32 LE)
+```
+
+### Message (Direct)
+Sender key, text, timestamp, outgoing flag, status (pending/sent/delivered/failed), message ID (UUID), retry count, ACK hash, trip time, path data, reactions.
+
+### Channel Message
+Sender name, text, timestamp, status (pending/sent/failed), repeater hops, path variants, channel index, reactions, reply threading fields.
+
+### Channel
+Index (0–7), name, 16-byte PSK, unread count. PSK derivation methods for hashtag (SHA-256) and community (HMAC-SHA256) channels.
+
+### Community
+UUID, name, 32-byte secret, hashtag channel list. Shared via QR code.
+
+## Persistence
+
+All data is stored via `SharedPreferences` (JSON-serialized). No SQLite or other database.
+
+| Data | Storage Key Pattern | Scope |
+|---|---|---|
+| Contacts | `contacts` | Per device identity |
+| Messages | `messages_` | Per device + contact |
+| Channel Messages | `channel_messages_` | Per device + channel |
+| Channels | `channels` | Per device identity |
+| Channel Order | `channel_order_` | Per device identity |
+| Contact Groups | `contact_groups` | Per device identity |
+| Communities | `communities_v1` | Per device identity |
+| Unread Counts | `contact_unread_count` | Per device identity |
+| Discovered Contacts | `discovered_contacts` | Global |
+| App Settings | `app_settings` | Global |
+| Path History | `path_history_` | Per contact |
+
+## Auto-Add Configuration Bitmask
+
+Used by `CMD_SET_AUTO_ADD_CONFIG` (58) and `RESP_CODE_AUTO_ADD_CONFIG` (25):
+
+| Bit | Flag | Description |
+|-----|------|-------------|
+| 0 | 0x01 | Overwrite oldest contact when list is full |
+| 1 | 0x02 | Auto-add chat users |
+| 2 | 0x04 | Auto-add repeaters |
+| 3 | 0x08 | Auto-add room servers |
+| 4 | 0x10 | Auto-add sensors |
+
+## Radio Packet Payload Types
+
+Seen inside `PUSH_CODE_LOG_RX_DATA` raw packets:
+
+| Code | Type |
+|------|------|
+| 0x00 | REQ (request) |
+| 0x01 | RESPONSE |
+| 0x02 | TXTMSG (text message) |
+| 0x03 | ACK |
+| 0x04 | ADVERT |
+| 0x05 | GRPTXT (group/channel text) |
+| 0x06 | GRPDATA (group data) |
+| 0x07 | ANONREQ (anonymous request) |
+| 0x08 | PATH |
+| 0x09 | TRACE |
+| 0x0A | MULTIPART |
+| 0x0B | CONTROL |
+| 0x0F | RAW_CUSTOM |
+
+## State Management
+
+Uses Flutter `Provider` with `ChangeNotifier`. The central state holder is `MeshCoreConnector`, which owns all in-memory collections and fires debounced (50ms) `notifyListeners()` to update the UI. In-memory conversations are windowed to 200 messages per contact; older messages remain on disk and are loaded on demand.
+
+### Data Flow
+
+1. Raw frames arrive over BLE/USB/TCP
+2. First byte is parsed as response/push code
+3. Appropriate model factory (`fromFrame()`) parses the data
+4. In-memory collections are updated
+5. Storage stores are persisted (async)
+6. `notifyListeners()` triggers UI rebuilds
+7. Screens read current state via getters
diff --git a/documentation/channels.md b/documentation/channels.md
new file mode 100644
index 0000000..21fb52e
--- /dev/null
+++ b/documentation/channels.md
@@ -0,0 +1,164 @@
+# Channels
+
+## Overview
+
+Channels are broadcast group-chat spaces secured by a 16-byte pre-shared key (PSK). Any device with the same channel index and PSK will receive and decrypt channel messages. Unlike direct messages, channel messages are broadcast to the entire mesh.
+
+Up to 8 channels (indices 0–7) can be active simultaneously on one device.
+
+## How to Access
+
+QuickSwitchBar tab 1 (middle) from any main screen.
+
+## Channel Types
+
+| Type | Icon | Color | Description |
+|---|---|---|---|
+| Public | Globe | Green | Fixed well-known PSK; any device can join |
+| Hashtag | Hash tag | Blue | PSK derived from the hashtag name via SHA-256; discoverable by convention |
+| Private | Lock | Blue | Random PSK; requires out-of-band sharing of the 32-hex key |
+| Community | Groups/Tag | Purple | PSK derived via HMAC-SHA256 from a community's shared secret |
+
+## Channels List Screen
+
+### What the User Sees
+
+- **Search bar** with live text filtering (300ms debounce)
+- **Sort/filter button**
+- **Scrollable list of channel cards**, each showing:
+ - Type icon with color coding (purple badge overlay for community channels)
+ - Channel name (or "Channel N" if unnamed)
+ - Subtitle: "Public channel", "Hashtag channel", "Private channel", or "Community channel - {name}"
+ - Unread badge (if messages are unread)
+ - Drag handle (when manual sort is active)
+- **"+" FAB** to add a new channel
+- **Overflow menu**: Disconnect, Manage Communities (only shown when at least one community exists), Settings
+
+If no channels exist, an empty state with an "Add Public Channel" shortcut is shown. If a search produces no results, a separate "no results" empty state with a search-off icon is shown.
+
+Pull-to-refresh (swipe down) forces a re-fetch of channels from the device firmware.
+
+### Sorting Options
+
+- **Manual** (default): Drag-and-drop reordering, persisted (drag handles are hidden when a search query is active)
+- **A–Z**: Alphabetical
+- **Latest messages**: Most recent first
+- **Unread**: Most unread first
+
+## Adding a Channel
+
+Tap the "+" FAB to open a dialog with six options:
+
+1. **Create Private Channel** — Enter a name (max 31 characters); a random PSK is generated
+2. **Join Private Channel** — Enter a name and a 32-hex PSK (non-hex characters like spaces and dashes are silently stripped, so pasted keys with formatting are accepted)
+3. **Join Public Channel** — One tap; uses the well-known public PSK (only shown if no public channel exists)
+4. **Join Hashtag Channel** — Enter a hashtag name; PSK is derived from the name. If communities exist, choose between regular hashtag (SHA-256) or community hashtag (HMAC)
+5. **Scan Community QR** — Opens QR scanner to join a community
+6. **Create Community** — Enter a name; generates a random 32-byte secret; optionally adds a community public channel; shows QR code for sharing
+
+## Channel Actions (Long-Press / Right-Click)
+
+| Action | Description |
+|---|---|
+| Edit | Change name, PSK (with a dice icon to generate a random PSK), or SMAZ compression toggle (compresses outgoing messages to allow longer text within the byte limit) |
+| Mute / Unmute | Toggle push notification suppression for this channel |
+| Delete | Remove the channel from the device (confirmation required) |
+
+## Channel Chat
+
+Tap a channel card to open the channel chat screen.
+
+### App Bar
+
+- Type icon (public/private/hashtag)
+- Channel name
+- Subtitle: "{type} - {N} unread"
+
+### Message Display
+
+- Reverse-scrolling list (newest at bottom)
+- **Incoming messages**: Colored avatar with sender's initial (or first emoji if name starts with one; color is deterministic from sender name hash), sender name in primary color, message bubble
+- **Outgoing messages**: Primary container color bubble with a small status icon: pending (clock), sent (checkmark), or failed (red error circle)
+- Automatic older-message loading on scroll-to-top
+- Jump-to-bottom button when scrolled up
+- **Pinch-to-zoom**: Two-finger zoom (0.8x–1.8x) and double-tap to reset text size
+- **Message tracing mode** (when enabled in App Settings): Each bubble additionally shows path prefix bytes (`via XX,YY,...`), a timestamp, and a repeat count icon
+
+### Message Types in Chat
+
+- **Plain text** with linkified URLs
+- **GIFs** (`g:{gifId}`) rendered inline via Giphy CDN
+- **Location pins** (`m:{lat},{lon}|{label}|`) shown as tappable location cards
+- **Reactions** displayed as emoji pills below target messages
+
+### Replies (Channel Chat Only)
+
+- **Mobile**: Swipe an **incoming** message left to trigger reply (with haptic feedback). You cannot swipe your own outgoing messages. Swipe reply is not available on desktop.
+- **All platforms**: Long-press → "Reply"
+- Reply banner appears above the input bar with the quoted message (tap X to cancel)
+- Sent replies are prefixed `@[{senderName}] {text}`
+- Received replies show a bordered quote block inside the bubble; tapping scrolls to the original. Reply previews render GIF thumbnails and location pin icons, not just text.
+
+### Message Path Viewing
+
+- **Mobile**: Tap a message bubble to view its routing path
+- **Desktop**: Long-press/right-click → "Path" (tapping the bubble does nothing on desktop)
+- Opens the Channel Message Path Screen (see [Additional Features](additional-features.md))
+
+### Context Actions (Long-Press / Right-Click)
+
+| Action | Availability | Description |
+|---|---|---|
+| Reply | All messages | Triggers reply mode |
+| Path | Desktop only | Opens message path view |
+| Add Reaction | Incoming messages only | Opens emoji picker (cannot react to your own messages) |
+| Copy | All messages | Copies text to clipboard |
+| Delete | All messages | Removes locally (not from mesh) |
+
+### Message Path Viewing
+
+Tap a message bubble to open the Channel Message Path Screen, which shows:
+- Each hop in the path as a visual chain
+- Known contacts identified by name at each hop
+- Observed vs. declared hop counts
+- Alternative path variants (if received via multiple paths)
+- Map view buttons for geographic path visualization
+
+## Communities
+
+Communities are a layer above channels that provide a private namespace.
+
+### What is a Community?
+
+A community has a name and a 32-byte random secret. Channel PSKs are derived from this secret:
+- **Public channel**: `HMAC-SHA256(secret, "channel:v1:__public__")[:16]`
+- **Hashtag channel**: `HMAC-SHA256(secret, "channel:v1:{hashtag}")[:16]`
+
+Outsiders who don't know the secret cannot discover or join community channels.
+
+### Sharing a Community
+
+Communities are shared via QR codes containing a JSON payload:
+```json
+{"v": 1, "type": "meshcore_community", "name": "...", "k": ""}
+```
+
+### Managing Communities
+
+From the channels screen overflow menu → "Manage Communities". Opens a draggable scrollable sheet (resizable 30–90% of screen height):
+
+- Each community shows its name and a short community ID (first 8 hex characters)
+- **Tap a community** to directly show its QR code for sharing
+- **Popup menu** per community:
+ - **Show QR** — displays the QR code for sharing with new members
+ - **Delete** — removes the community locally and deletes all associated device channels (confirmation dialog warns how many channels will be removed)
+
+## How Channels Differ from Direct Messages
+
+| Aspect | Channels | Direct Messages |
+|---|---|---|
+| Addressing | Broadcast to all nodes with matching PSK | Point-to-point to a specific contact |
+| Encryption | Shared PSK (symmetric) | Contact's public key (asymmetric) |
+| Sender identity | Plain text prefix in payload | Verified via public key |
+| Replies | Supported (swipe or long-press) | Not supported |
+| Retry mechanism | No automatic retry | Exponential backoff with path rotation |
diff --git a/documentation/chat-and-messaging.md b/documentation/chat-and-messaging.md
new file mode 100644
index 0000000..22030d5
--- /dev/null
+++ b/documentation/chat-and-messaging.md
@@ -0,0 +1,120 @@
+# Chat & Messaging
+
+## Overview
+
+The app supports two chat modes:
+- **Direct messages**: Encrypted point-to-point messages to individual contacts
+- **Channel messages**: Broadcast messages to shared channels (see [Channels](channels.md))
+
+This page covers direct messaging. For channel chat, see the Channels documentation.
+
+## How to Access
+
+From the Contacts screen, tap any Chat-type contact to open the ChatScreen.
+
+## Chat Screen Layout
+
+### App Bar
+
+- **Title**: Contact name
+- **Subtitle**: Current routing path label (e.g., "2 hops", "flood (auto)", "direct (forced)") and unread count. Tapping the subtitle shows the full path details.
+- **Action buttons**:
+ - **Routing mode** (waves icon): Switch between Auto, Direct, and Flood routing
+ - **Path management** (timeline icon): View recent paths with hop count, round-trip time, age, and success count. Paths are color-coded by direct repeater (green/yellow/red/blue for ranked repeaters, grey for unknown). Tap a path to activate it (the device verifies and confirms via snackbar), long-press to view full path details, set custom paths, or force flood mode. A warning banner appears when history reaches 100 entries.
+ - **Info** (info icon): Contact info dialog showing type, path, GPS coordinates, public key, and SMAZ compression toggle
+
+### Message List
+
+- Scrollable list with newest messages at the bottom
+- **Outgoing messages**: Right-aligned, primary color background. **Failed messages** change to a red-toned error container background
+- **Incoming messages**: Left-aligned, grey background with a colored avatar (initial letter or first emoji of sender name; color is deterministic from a hash of the sender name)
+- Bubble width capped at 65% of screen width
+- Hyperlinks rendered as tappable green underlined text
+- **Pinch-to-zoom**: Two-finger zoom (0.8x–1.8x) and double-tap to reset
+- **Jump to bottom**: Floating button appears when scrolled away from the bottom
+- **Lazy loading**: Scrolling to top loads older messages from storage
+
+### Input Bar
+
+- **GIF button** (left): Opens GIF picker bottom sheet
+- **Text field** (center): Auto-capitalization, enforces UTF-8 byte limit in real-time
+- **Send button** (right): Submits the message
+- On desktop: Enter/Numpad Enter also submits
+- When a GIF is selected, the text field shows an inline GIF preview with a dismiss button
+
+## Message Types
+
+| Type | Wire Format | Display |
+|---|---|---|
+| Plain text | Raw UTF-8 string | Inline text with link detection |
+| GIF | `g:` | Inline GIF image from Giphy CDN |
+| Location pin | `m:,\|\|...` | Location icon + label; tap to open map |
+| Reaction | `r::` | Applied to target message as emoji pill |
+
+## Message Status
+
+Outgoing messages display a status indicator:
+
+| Status | Icon | Meaning |
+|---|---|---|
+| Pending | Grey double-check | Queued, waiting for device to transmit (visually identical to Sent) |
+| Sent | Grey double-check | Device confirmed transmission (visually identical to Pending) |
+| Delivered | Green double-check | Remote node acknowledged receipt |
+| Failed | Red X | All retries exhausted |
+
+### Message Tracing Mode
+
+When enabled in App Settings, additional metadata appears inside each bubble:
+- Timestamp (HH:MM)
+- Retry count (e.g., "Retry 2 of 4")
+- Status icon
+- Round-trip time in seconds (if delivered)
+
+## Message Length Limits
+
+- **Direct messages**: 156 bytes (UTF-8) — enforced in real-time by the input formatter
+- **Channel messages**: 160 minus sender name length minus 2 bytes for the `": "` prefix
+- Over-length paste shows a snackbar error
+
+## Send Queue
+
+Only one message per contact can be in-flight at a time (to avoid overflowing the firmware's 8-entry ACK table). If you send multiple messages rapidly, they are queued and sent sequentially — each waits for the previous one to be delivered, fail, or exhaust retries before transmitting.
+
+## Retry Mechanism
+
+When a direct message is sent:
+
+1. The app computes an expected ACK hash: `SHA256([timestamp][attempt][text][selfPubKey])[0:4]` — matching the firmware's hash calculation. If SMAZ compression is enabled, the compressed text (not the original) is hashed
+2. On device acknowledgment (`RESP_CODE_SENT`), the message transitions to "sent" and a timeout timer starts
+3. **Timeout duration**: Preferably from the ML timeout prediction service; otherwise `3000 + 3000 × path_length` milliseconds (15000ms for flood)
+4. On timeout, the message is retried with **exponential backoff**: `1000 × 2^retryCount` ms (1s, 2s, 4s, 8s, 16s...)
+5. **Max retries**: Configurable (default 5, range 2–10)
+6. After max retries, the message is marked "failed" — but a **30-second grace window** remains during which a late ACK can still resolve the message to "delivered"
+7. If **Clear Path on Max Retry** is enabled (App Settings), the contact's stored routing path is automatically cleared when max retries are exhausted
+8. **Auto route rotation**: When enabled (and no manual path override is set), the retry service uses a diversity window to avoid re-using recently tried paths, cycling through known routes on each attempt
+
+### Manual Retry
+
+Long-press a failed message → "Retry" to re-send using the current routing settings.
+
+## Reactions
+
+Add emoji reactions to incoming messages (not your own):
+
+1. Long-press (or right-click on desktop) a message
+2. Select "Add reaction" from the context menu
+3. Choose from quick emojis (thumbs up, heart, laugh, party, clap, fire) or browse the full emoji picker
+4. Reactions appear as pills below the message bubble with emoji and count
+5. Pending reactions show at 50% opacity with a spinner
+6. Failed reactions show a red retry icon (tap to retry)
+
+## Context Actions (Long-Press / Right-Click)
+
+| Action | Availability | Description |
+|---|---|---|
+| Add reaction | Incoming messages only | Opens emoji picker |
+| View path | Mobile: tap bubble directly; Desktop: long-press/right-click menu | Shows message routing path |
+| Copy | All messages | Copies text to clipboard |
+| Delete | All messages | Removes locally (not from mesh) |
+| Retry | Failed outgoing messages | Re-sends the message |
+| Open chat with sender | Room server chats | Opens 1:1 chat with the message sender |
diff --git a/documentation/contacts.md b/documentation/contacts.md
new file mode 100644
index 0000000..1a94ba2
--- /dev/null
+++ b/documentation/contacts.md
@@ -0,0 +1,118 @@
+# Contacts
+
+## Overview
+
+The Contacts screen is the primary hub for managing mesh nodes your radio has a relationship with. A "contact" is any node whose cryptographic advertisement has been received — it can be a chat user, repeater, room server, or sensor.
+
+## How to Access
+
+- Automatically shown after connecting to a device
+- QuickSwitchBar tab 0 (leftmost) from Channels or Map screens
+- Back navigation from Chat or Settings screens
+
+## Contact Types
+
+| Type | Avatar Color | Icon | Description |
+|---|---|---|---|
+| Chat | Blue | Chat bubble | Another user's mesh radio |
+| Repeater | Orange | Cell tower | A mesh repeater/relay node |
+| Room | Purple | Group | A room server for group chat |
+| Sensor | Green | Sensors | A sensor device |
+
+## Contact List
+
+Each contact is displayed as a list tile showing:
+
+- **Avatar**: Color-coded circle with type icon (or first emoji of the contact's name if it starts with one)
+- **Name**: Contact name (single line)
+- **Path label**: "Direct", "N hops", or "Flood" (with forced variants if a path override is active)
+- **Public key**: Shortened hex format ``
+- **Unread badge**: Red pill with count (if unread messages exist)
+- **Last seen**: Relative timestamp ("Now", "5 mins ago", "2 hours ago", "3 days ago"). For chat contacts, this shows whichever is more recent: the last advertisement time or the last message time
+- **Favorite star**: Amber star icon if favorited
+- **Location pin**: Grey pin icon if the contact has GPS coordinates
+
+Pull-to-refresh re-fetches the full contact list from the device.
+
+## Search and Filter
+
+A toolbar at the top provides:
+
+**Search**: Matches contact name (case-insensitive) or public key hex prefix. Debounced at 300ms.
+
+**Sort options**:
+- Latest Messages (by most recent message)
+- Heard Recently (by last seen / last message)
+- A–Z (alphabetical)
+
+**Filter options**:
+- All, Favorites, Users, Repeaters, Room Servers, Unread Only
+
+## Contact Groups
+
+Groups are a client-side organizational feature for grouping contacts.
+
+- **Create a group**: Tap the group dropdown → "+" icon → enter name → select members → Save
+- **Edit a group**: Group dropdown → pencil icon next to the group
+- **Delete a group**: Group dropdown → trash icon next to the group
+- **Filter by group**: Select a group from the dropdown to show only its members
+
+Groups are stored per radio identity (scoped by public key).
+
+**Validation rules**: Group names cannot be empty, cannot be "all" (reserved, case-insensitive), and must be unique (case-insensitive). The group creation dialog includes a built-in search field to filter contacts when selecting members. Creating a new group automatically selects it as the active filter.
+
+## Tap Actions
+
+| Contact Type | Action on Tap |
+|---|---|
+| Chat / Sensor | Opens ChatScreen for direct messaging |
+| Repeater | Shows password login dialog → opens RepeaterHubScreen |
+| Room | Shows password login dialog → opens ChatScreen for room chat |
+
+## Long-Press / Right-Click Menu
+
+| Action | Availability | Description |
+|---|---|---|
+| Path Trace / Ping | Repeaters, Rooms (always); Chat if `pathLength > 0` | Opens PathTraceMapScreen. Label shows "Ping" when no path bytes are known, "Path Trace" otherwise |
+| Manage Repeater | Repeaters only | Login dialog → RepeaterHubScreen |
+| Room Login | Rooms only | Login dialog → ChatScreen |
+| Room Management | Rooms only | Login dialog → RepeaterHubScreen (management mode) |
+| Open Chat | Chat/Sensor | Same as single tap |
+| Add/Remove Favorite | All types | Toggles the favorite flag |
+| Share Contact | All types | Copies `meshcore://` URI to clipboard |
+| Share Contact Zero-Hop | All types | Broadcasts the contact's advertisement one hop |
+| Delete Contact | All types | Confirmation dialog → removes from device and clears messages |
+
+## App Bar Menus
+
+The Contacts screen has **two separate popup menus** in the app bar:
+
+**Antenna icon menu** (contact sharing):
+- Zero-Hop Advert — broadcasts your advertisement to immediately adjacent nodes
+- Flood Advert — broadcasts across the full mesh network
+- Copy Advert to Clipboard — copies your `meshcore://` URI for sharing externally
+- Add Contact from Clipboard — reads a `meshcore://` URI from clipboard and imports it
+
+**Three-dot overflow menu**:
+- Disconnect — disconnects from the device
+- Discovered Contacts — opens the DiscoveryScreen
+- Settings — opens the Settings screen
+
+## Adding Contacts
+
+### Automatic (Passive)
+When the radio hears an advertisement, the contact appears automatically if auto-add is enabled for that type (configurable in Settings → Contact Settings).
+
+### Import from Clipboard
+Antenna menu → "Add Contact from Clipboard". Reads a `meshcore://` URI from clipboard and imports it to the device.
+
+### Import from Discovered Contacts
+Overflow menu → "Discovered Contacts". Shows nodes heard passively that haven't been added yet. Tap to immediately import (no confirmation dialog), or long-press for more options (Add, Copy URI, Delete). The Discovery screen has its own search bar, type filters (Users, Repeaters, Rooms, Favorites), and sort options (Last Seen, A-Z). An overflow "Delete All" option clears all discovered contacts.
+
+## Contact Sharing Format
+
+Contacts are shared using the `meshcore://` URI scheme:
+```
+meshcore://
+```
+This contains the node's public key and metadata. Paste it into another MeshCore app to import.
diff --git a/documentation/map-and-location.md b/documentation/map-and-location.md
new file mode 100644
index 0000000..f293abe
--- /dev/null
+++ b/documentation/map-and-location.md
@@ -0,0 +1,186 @@
+# Map & Location
+
+## Overview
+
+The Map feature is a full-featured node-location visualization and radio-planning tool built on OpenStreetMap tiles. It is one of the three primary views accessible from the QuickSwitchBar.
+
+## How to Access
+
+- **QuickSwitchBar tab 2** (rightmost) from Contacts or Channels
+- **Deep-link from a chat message**: Tapping a shared location pin in a chat opens the map centered on that pin
+- **Settings → Offline Map Cache**: Opens the tile cache management screen
+
+## What the Map Displays
+
+### Self Location (Teal Circle)
+Your own node's position, obtained from the device firmware. Displayed as a teal `person_pin_circle` icon. Only appears if the device has GPS data or a manually-set location.
+
+### Contact / Node Markers (Color-Coded)
+All contacts with known GPS coordinates are plotted:
+
+| Type | Color | Icon |
+|---|---|---|
+| Chat user | Blue | Person |
+| Repeater | Green | Router |
+| Room | Purple | Meeting room |
+| Sensor | Orange | Sensors |
+
+Node name labels appear automatically at zoom level 12 and above.
+
+### Shared Map Pins (Flag Icons)
+Location pins shared in chat messages are displayed as flags:
+- **Blue flag**: From a direct message
+- **Purple flag**: From a private channel
+- **Orange flag**: From a public channel
+
+Tap a pin to see its info. Options to "Hide" (session only) or "Remove" (persistent).
+
+### Predicted / Guessed Locations (Semi-Transparent)
+
+Many contacts on the mesh don't have GPS hardware, so the map has no explicit coordinates for them. Instead of leaving these contacts invisible, the app **infers an approximate position** by analyzing the repeater path the contact's messages travel through. These inferred positions are displayed as semi-transparent markers with a `not_listed_location` icon, visually distinct from confirmed-location markers.
+
+#### Why guessed locations exist
+
+In a mesh network, every message hops through one or more repeaters on its way to the destination. Each repeater in the path is identified by the first byte of its public key. If any of those repeaters have a known GPS location (because they advertise it), then a contact that routes through those repeaters must be somewhere within radio range of them. By combining the positions of multiple repeaters a contact is known to use, the app can triangulate a rough area where the contact is likely located.
+
+#### How the algorithm works
+
+1. **Build a repeater index**: The app collects all known contacts of type Repeater that have a valid GPS position and indexes them by the first byte of their public key.
+
+2. **Collect anchor points**: For each contact that lacks GPS, the app looks at the **last-hop byte** of the contact's current path and also searches the `PathHistoryService` for recent paths. Each last-hop byte that matches a located repeater becomes an "anchor point" — a GPS coordinate the contact is likely near.
+
+3. **Resolve ambiguity**: If multiple repeaters share the same first public-key byte (a hash collision), that byte is discarded as ambiguous. Only unambiguous one-to-one matches are kept.
+
+4. **Filter geometric inconsistencies**: Two anchor points separated by more than `2 × maxRangeKm` (the estimated LoRa radio range, computed from the current frequency, bandwidth, spreading factor, and TX power using a free-space path loss model) cannot both be in range of the same node. Outlier anchors are removed to keep only a geometrically consistent set.
+
+5. **Compute the estimated position**:
+ - **Single anchor**: The contact is placed on a small circle (330m radius) around the repeater. The angle on the circle is deterministic — derived from an FNV-1a hash of the contact's public key — so the same contact always appears at the same offset, preventing markers from stacking on top of each other.
+ - **Two or more anchors**: The position is the average (centroid) of all anchor coordinates, with a smaller offset radius (80–120m) applied for visual separation.
+
+6. **Assign confidence level**:
+ - **High confidence** (2+ anchors): Displayed at 55% opacity.
+ - **Low confidence** (1 anchor): Displayed at 30% opacity.
+
+7. **Cache the result**: The computation is cached using a key derived from the contact's paths, anchor positions, path-history version, and radio parameters. The cache is only invalidated when any of these inputs change, avoiding recomputation on every UI rebuild.
+
+#### How to read guessed locations on the map
+
+- **Semi-transparent marker** with a `not_listed_location` icon: This is a guessed position, not a confirmed GPS fix.
+- **More opaque** (55%): Higher confidence — the contact was seen through 2 or more repeaters with known positions.
+- **More transparent** (30%): Lower confidence — based on a single repeater anchor only.
+- Coordinates shown in the marker info dialog are prefixed with `~` to indicate they are estimated.
+- Guessed locations can be toggled on/off in the map filter dialog (FAB → "Guessed locations" toggle).
+
+## Map Interactions
+
+### Zoom and Pan
+Standard pinch-to-zoom (range 2–18). Initial camera position is calculated from the statistical spread of all plotted points.
+
+### Tap on a Node Marker
+Opens a dialog showing: type, path (hop chain), coordinates, last-seen time, and public key. Action buttons vary by type:
+- **Chat nodes**: "Open Chat"
+- **Repeaters**: "Manage Repeater"
+- **Rooms**: "Join Room"
+
+### Long-Press on Empty Map Area
+Shows a bottom sheet with:
+- **Share marker here**: Prompts for a label, then pick a DM contact or channel to send the location to. Wire format: `m:,||poi`
+- **Set as my location**: Updates your device's advertised location
+
+### Filter Dialog (FAB)
+Toggle visibility of: chat nodes, repeaters, other nodes, guessed locations, discovery contacts.
+Additional filters:
+- **Key prefix filter**: Show only contacts whose public key starts with a given prefix
+- **Last-seen time slider**: From 1 hour to "all time"
+
+### Legend Card (Top-Right)
+Shows node count and pin count. Tappable to expand a legend of all marker types.
+
+---
+
+## Path Trace Map
+
+### How to Access
+- From the main map's radar icon
+- From a contact's long-press menu → "Path Trace / Ping"
+- From a message's path view → radar icon
+
+### What the User Sees
+A map with a polyline showing the route from your node through repeater hops to the target:
+- **Green circles**: Hops with known GPS coordinates
+- **Orange circles** (`~HH`): Inferred positions (no GPS but deducible from contacts)
+- **Red endpoint**: Target contact with known GPS
+- **Purple semi-transparent endpoint**: Target with guessed position
+
+A legend card at the bottom lists each hop pair with SNR quality icons and total path distance.
+
+### How It Works
+Sends a trace request frame over the mesh. The repeater network traces the path hop-by-hop and returns per-hop SNR data. For hops without GPS, positions are inferred by averaging GPS coordinates of contacts sharing that last-hop byte.
+
+---
+
+## Line-of-Sight (LOS) Analysis
+
+### How to Access
+From the main map, tap the terrain/antenna icon.
+
+### What the User Sees
+A full-screen map with a collapsible control panel containing:
+- **Elevation profile chart**: Terrain fill (green), LOS beam line (white), radio horizon line (yellow)
+- **Status**: Clear (green) or blocked (red) with distance and minimum clearance
+- **Options panel**: Node toggles, endpoint dropdowns, antenna height sliders (0–400 ft), Run LOS button
+
+### Key Interactions
+- **Long-press the map** to add custom endpoints (orange pushpin markers, renameable/deleteable)
+- **Tap a marker** to select it as Point A or B; LOS runs automatically when both are set
+- **Antenna heights** are adjustable for both endpoints
+- **Map line** between endpoints is colored green (clear) or red (blocked)
+- Terrain elevation is fetched from the Open-Meteo API (21–81 sample points, cached 24 hours)
+- K-factor is adjusted per radio frequency from a baseline of 4/3 at 915 MHz
+
+---
+
+## Offline Map Cache
+
+### How to Access
+Settings → App Settings → Map Display → Offline Map Cache
+
+### What the User Sees
+- Map with a blue polygon overlay showing previously selected cache bounds
+- Bounding box coordinates card
+- **Cache Area** controls: "Use Current View" and Clear buttons
+- **Zoom Range** slider (3–18) with estimated tile count
+- **Download progress** bar (when downloading)
+- **Download Tiles** and **Clear Cache** buttons
+
+### Key Interactions
+1. Pan/zoom the map to the desired area
+2. Tap "Use Current View" to capture the viewport as cache bounds
+3. Adjust the zoom range slider
+4. Tap "Download Tiles" (confirmation dialog shows estimated count)
+5. Tiles are downloaded with up to 8 concurrent connections
+6. Once cached, tiles are served from disk without internet (365-day stale period)
+
+---
+
+## GPX Export
+
+### How to Access
+Settings → Export section
+
+### What It Does
+Exports contacts with GPS coordinates to a `.gpx` file via the OS share sheet. Three export options:
+- **Export Repeaters**: Repeater and Room contacts with locations
+- **Export Contacts**: Chat contacts with locations
+- **Export All**: All contacts with locations
+
+Each waypoint includes: name, lat/lon, type label, and public key hex.
+
+---
+
+## Location Data Sources
+
+The phone's own GPS is **never used**. All location data comes from the mesh:
+
+1. **Device self-location**: Read from firmware device-info response. Set manually in Settings → Location, or updated automatically if the device has a GPS module.
+2. **Remote node locations**: Extracted from advertisement packets received over the mesh. Encoded as integer lat/lon × 1,000,000.
diff --git a/documentation/navigation.md b/documentation/navigation.md
new file mode 100644
index 0000000..9003122
--- /dev/null
+++ b/documentation/navigation.md
@@ -0,0 +1,87 @@
+# Navigation
+
+## App Flow
+
+The app follows this general flow:
+
+```
+Launch → Scanner Screen → [Connect via BLE/USB/TCP] → Contacts Screen
+```
+
+After connecting, the three main screens (Contacts, Channels, Map) are accessible via a persistent bottom navigation bar called the **QuickSwitchBar**.
+
+## Quick Switch Bar
+
+The QuickSwitchBar is a Material 3 `NavigationBar` with a frosted-glass visual treatment (blur backdrop, transparent theme, rounded corners). It appears at the bottom of all three main screens.
+
+| Index | Icon | Label | Screen |
+|---|---|---|---|
+| 0 | People | Contacts | ContactsScreen |
+| 1 | Tag | Channels | ChannelsScreen |
+| 2 | Map | Map | MapScreen |
+
+Tapping a tab replaces the current screen with a subtle fade + slight horizontal nudge transition (220ms forward, 200ms reverse). The back button is suppressed on all three main screens — navigation between them is flat, not stacked. All icons use outline variants (`people_outline`, `tag`, `map_outlined`) following Material 3 conventions.
+
+## Device Screen
+
+The Device Screen is a transitional hub that shows after connection. In practice, the app navigates directly to Contacts after connecting, but the Device Screen is reachable via the QuickSwitchBar.
+
+### What the User Sees
+
+**App Bar**:
+- Left: Battery indicator chip (tappable — toggles between percentage and voltage display). Icon changes based on level: `battery_unknown` when data unavailable, `battery_alert` (orange) at 15% or below, `battery_full` otherwise
+- Left-aligned title (`centerTitle: false`): Two-line layout — small grey "MeshCore" label above the device name in bold
+- Right: Disconnect button (`bluetooth_disabled` crossed-out icon) and Settings button (tune icon)
+
+**Body**:
+- **Connection Card**: Device avatar, device name, device ID, "Connected" chip, and battery chip
+- **Quick Switch** section: The QuickSwitchBar widget for navigating to Contacts/Channels/Map
+
+### Disconnection
+
+- The disconnect button shows a confirmation dialog before disconnecting
+- If the device disconnects unexpectedly, the app automatically navigates back to the Scanner screen (fires after the current frame completes via a post-frame callback)
+- This auto-navigation behavior (`DisconnectNavigationMixin`) is shared across all main screens
+
+## Theme and Locale
+
+- **Theme mode** is user-configurable in App Settings (System / Light / Dark) — not locked to system
+- **Language** can be overridden to one of 15 supported languages, or follow the system locale
+- On web, if a non-Chromium browser is detected, the app shows a `ChromeRequiredScreen` instead of the Scanner (Web Bluetooth requires Chromium)
+
+## Full Navigation Graph
+
+```
+ScannerScreen (root, always on stack)
+ ├─ [BLE connect] → push → ContactsScreen
+ ├─ [TCP FAB] → push → TcpScreen
+ │ └─ [TCP connected] → pushReplacement → ContactsScreen
+ └─ [USB FAB] → push → UsbScreen
+ └─ [USB connected] → pushReplacement → ContactsScreen
+
+ContactsScreen (selected=0)
+ ├─ [quick-switch 1] → pushReplacement → ChannelsScreen
+ ├─ [quick-switch 2] → pushReplacement → MapScreen
+ ├─ [tap contact] → push → ChatScreen
+ ├─ [overflow > Settings] → push → SettingsScreen
+ └─ [overflow > Discovered] → push → DiscoveryScreen
+
+ChannelsScreen (selected=1)
+ ├─ [quick-switch 0] → pushReplacement → ContactsScreen
+ ├─ [quick-switch 2] → pushReplacement → MapScreen
+ ├─ [tap channel] → push → ChannelChatScreen
+ └─ [overflow > Settings] → push → SettingsScreen
+
+MapScreen (selected=2)
+ ├─ [quick-switch 0] → pushReplacement → ContactsScreen
+ ├─ [quick-switch 1] → pushReplacement → ChannelsScreen
+ ├─ [radar button] → push → PathTraceMapScreen
+ ├─ [terrain button] → push → LineOfSightMapScreen
+ └─ [long-press] → share marker / set location
+
+Settings (push from any main screen)
+ └─ [App Settings] → push → AppSettingsScreen
+ └─ [Offline Map Cache] → push → MapCacheScreen
+```
+
+Any disconnection from any screen triggers `popUntil(route.isFirst)`, returning to the Scanner.
diff --git a/documentation/notifications.md b/documentation/notifications.md
new file mode 100644
index 0000000..0eb2574
--- /dev/null
+++ b/documentation/notifications.md
@@ -0,0 +1,92 @@
+# Notifications
+
+## Overview
+
+MeshCore Open provides both **system notifications** (push-style OS alerts) and **in-app unread badges** to inform users of new activity.
+
+## Notification Types
+
+### 1. Direct Message Notifications
+- **Triggered when**: A new incoming message arrives from a Chat or Room contact
+- **Title**: Contact's name
+- **Body**: Message text (reactions show "Reacted [emoji]", GIFs show "Sent a GIF")
+- **Priority**: High
+- **Android channel**: `messages`
+
+### 2. Channel Message Notifications
+- **Triggered when**: A new message arrives on a non-muted channel
+- **Title**: Channel name (or "Channel N" if unnamed)
+- **Body**: `": "`
+- **Priority**: High
+- **Android channel**: `channel_messages`
+
+### 3. Advertisement Notifications
+- **Triggered when**: A new node is discovered on the mesh for the first time
+- **Title**: "New [type] discovered" (e.g., "New chat node discovered")
+- **Body**: Contact's name
+- **Priority**: Default
+- **Android channel**: `adverts`
+
+### 4. Background Service Notification (Android Only)
+- A persistent low-priority notification: "MeshCore running — Keeping BLE connected"
+- Required by Android for foreground services to keep BLE alive in the background
+- Tap to re-launch the app
+- **Does not auto-start on reboot** — the user must re-open the app manually after a phone restart
+
+### Notification Tap Behavior
+
+Tapping a notification currently re-launches the app at the root route. It does **not** navigate directly to the relevant chat or channel.
+
+## In-App Unread Badges
+
+Red numeric badges appear throughout the UI:
+- **Contacts list**: Each contact row shows a red pill badge (e.g., "3") for unread messages
+- **Channels list**: Each channel row shows an unread badge
+- **Chat screen subtitle**: Shows unread count inline
+- Badges cap at "99+" for display
+
+### How Unread Counts Work
+
+- Stored per contact (by public key) and per channel, **scoped to the connected device's identity** (first 10 hex characters of its public key). Switching between different radios gives each its own independent unread state
+- **Suppressed when viewing**: Opening a chat resets the count to 0 and cancels the OS notification
+- **Ignored for**: Outgoing messages, CLI messages, and repeater contacts
+- Debounced writes (500ms) to avoid excessive storage I/O during message bursts
+
+## Notification Settings
+
+Access via **App Settings → Notifications**:
+
+| Setting | Default | Description |
+|---|---|---|
+| Enable Notifications | On | Master toggle; requests OS permission when turned on |
+| Message Notifications | On | DM alerts (greyed out if master is off) |
+| Channel Message Notifications | On | Channel alerts (greyed out if master is off) |
+| Advertisement Notifications | On | New node alerts (greyed out if master is off) |
+
+### Per-Channel Muting
+
+Long-press a channel in the channels list → "Mute channel" / "Unmute channel". Muted channels do not generate OS notifications.
+
+There is no per-contact muting.
+
+## Rate Limiting
+
+The notification system prevents notification storms:
+- **Minimum interval**: 3 seconds between individual notifications
+- **Batch window**: If multiple notifications arrive within 5 seconds, they are combined into a single summary notification on a fourth Android channel (`batch_summary`): "MeshCore Activity — 2 messages, 1 channel message, 3 new nodes". Note: batch summaries are Android-only; on Apple platforms individual notifications are shown
+
+## Notification Clearing
+
+- **Opening a contact chat**: Cancels the OS notification and resets unread count
+- **Opening a channel**: Cancels the channel notification and resets unread count
+- **Opening Contacts screen**: Cancels all advertisement notifications
+
+## Platform Support
+
+| Platform | Message Notifs | Badge | Background Service |
+|---|---|---|---|
+| Android | Yes | Via notification number | Yes (foreground service) |
+| iOS | Yes | Yes (app badge) | No |
+| macOS | Yes | Yes | No |
+| Windows | Yes | No | No |
+| Linux | Yes (if D-Bus available) | No | No |
diff --git a/documentation/repeater-management.md b/documentation/repeater-management.md
new file mode 100644
index 0000000..be5015f
--- /dev/null
+++ b/documentation/repeater-management.md
@@ -0,0 +1,186 @@
+# Repeater Management
+
+## Overview
+
+Repeater Management provides tools for administering MeshCore repeater and room server nodes. It includes device status monitoring, CLI access, telemetry reading, neighbor discovery, and remote configuration.
+
+## How to Access
+
+From the Contacts screen:
+1. Long-press a **Repeater** or **Room** contact
+2. Select "Manage Repeater" or "Room Management"
+3. Enter the admin password in the login dialog
+4. Navigate to the Repeater Hub Screen
+
+### Login Dialog
+
+- Password field with show/hide toggle
+- "Save password" checkbox (persists for future logins). If a saved password exists, it is pre-filled and the checkbox is pre-checked, making login one-tap
+- Routing mode selector and "Manage Paths" link are available directly in the dialog (configure routing before login)
+- Auto-retries up to 5 times on timeout, showing progress ("Attempt 2 of 5"). A wrong password stops immediately after the first attempt — only timeouts trigger retries
+- After 5 failed attempts, further login attempts are blocked
+
+---
+
+## Repeater Hub Screen
+
+The central management screen showing:
+
+- **Header card**: Repeater name, short public key, path label, GPS coordinates (if known)
+- **Battery chemistry selector**: NMC / LiFePO4 / LiPo (saved per repeater)
+- **Management tool cards** (full-width cards with chevron arrows, not a grid). Title dynamically shows "Repeater Management" or "Room Management" based on contact type:
+
+| Card | Destination |
+|---|---|
+| Status | Repeater Status Screen |
+| Telemetry | Telemetry Screen |
+| CLI | Repeater CLI Screen |
+| Neighbors | Neighbors Screen |
+| Settings | Repeater Settings Screen |
+
+---
+
+## Repeater Status
+
+### What the User Sees
+
+Three information cards:
+
+**System Information**:
+- Battery percentage
+- Uptime
+- Queue length
+- Error flags
+- Clock at login time
+
+**Radio Statistics**:
+- Last RSSI and SNR
+- Noise floor
+- TX and RX airtime
+
+**Packet Statistics**:
+- Packets sent, received, and duplicates
+- Broken down by flood vs. direct
+
+### Key Interactions
+- Auto-queries the repeater on open; shows a loading spinner until data arrives
+- On timeout: red snackbar error. On success: data appears with a green snackbar confirmation
+- Pull-to-refresh or refresh button to re-query
+- Routing mode popup and path management dialog in app bar (these controls appear on **all** management sub-screens, not just Status)
+
+---
+
+## Repeater CLI
+
+A terminal-style interface for sending commands directly to the repeater.
+
+### What the User Sees
+
+- **Quick-command bar** (horizontal scroll): Shortcut buttons for common commands (get name, get radio, get tx, neighbors, ver, advert, clock)
+- **Command history list**: Sent commands in primary color, responses in secondary color
+- **Input bar**: Up/down history arrows, monospace text field with `> ` prefix, send button
+
+### Key Interactions
+
+- Type a command and press send (or Enter on desktop)
+- Up/down arrows navigate through command history
+- Quick-command buttons populate and send common commands
+- Bug report icon: Shows raw frame debug info for the next typed command (shows error snackbar if input field is empty)
+- Help icon: Opens a scrollable reference of all known CLI commands. Tapping any command populates the input field immediately
+- Clear icon: Wipes the command/response history
+- Failed/timed-out commands are automatically retried once
+
+### Available CLI Commands
+
+**General**: `advert`, `reboot`, `clock`, `password`, `ver`, `clear stats`
+
+**Settings**: `set name`, `set af`, `set tx`, `set repeat`, `set allow.read.only`, `set flood.max`, `set int.thresh`, `set agc.reset.interval`, `set multi.acks`, `set advert.interval`, `set flood.advert.interval`, `set guest.password`, `set lat`, `set lon`, `set radio`, `set rxdelay`, `set txdelay`, `set direct.txdelay`, `set bridge.*`, `set adc.multiplier`, `tempradio`, `setperm`
+
+**Bridge**: `get bridge.type`
+
+**Logging**: `log start`, `log stop`, `log erase`
+
+**Neighbors**: `neighbors`, `neighbor.remove`
+
+**Region Management**: `region`, `region load/get/put/remove/allowf/denyf/home/save`
+
+**GPS**: `gps`, `gps on/off/sync/setloc/advert`
+
+---
+
+## Telemetry
+
+### What the User Sees
+
+A list of Cayenne LPP sensor channel cards:
+
+- **Channel 1** (special): Battery voltage (shown as percentage or raw mV) and MCU temperature
+- **Other channels**: Raw sensor values with appropriate labels
+
+Shows "No data" until a response arrives from the repeater.
+
+### Key Interactions
+- Auto-queries on open
+- Pull-to-refresh
+- Temperature respects metric/imperial setting
+- Battery readings are stored for the repeater's battery snapshot
+
+---
+
+## Neighbors
+
+### What the User Sees
+
+A card titled "Repeater's Neighbors - N" listing each neighbor as:
+- Repeater name (or hex key prefix if unknown)
+- Time since last heard
+- SNR quality icon with color coding and label
+
+### Key Interactions
+- Auto-queries up to 15 neighbors on open
+- Matches public key prefixes against known contacts to show names
+- Pull-to-refresh
+
+---
+
+## Repeater Settings
+
+### What the User Sees
+
+Five configuration cards:
+
+**1. Basic Settings**
+- Name field
+- Admin password field
+- Guest password field
+
+**2. Radio Settings**
+- Frequency (MHz)
+- TX Power (dBm)
+- Bandwidth dropdown (kHz)
+- Spreading Factor (SF5–SF12)
+- Coding Rate (4/5–4/8)
+
+**3. Location Settings**
+- Latitude and longitude fields
+
+**4. Features**
+- Packet forwarding toggle
+- Guest access toggle
+
+**5. Advertisement Settings**
+- Local advert interval slider (60–240 minutes) with enable/disable toggle
+- Flood advert interval slider (3–168 hours) with enable/disable toggle
+
+**6. Danger Zone** (red-styled card)
+- Reboot repeater
+- Erase filesystem (serial-only warning)
+
+### Key Interactions
+- **Settings are NOT auto-fetched on open**. Only name and location are pre-filled from locally cached contact data. You must tap each section's refresh button to fetch live values from the repeater
+- TX Power has its own separate refresh button, independent from the main Radio Settings refresh
+- Save button appears when changes are detected
+- Settings are sent sequentially with 200ms delays between commands (fire-and-forget, no per-command acknowledgment wait)
+- Validation prevents invalid values (e.g., frequency range, LoRa parameter compatibility)
+- Advertisement interval sliders reset to defaults when re-enabled (local: 60 min, flood: 3 hours)
+- **Erase Filesystem** does NOT send any command over the air — tapping it only shows a snackbar explaining the operation requires physical serial access. It is effectively non-functional when connected wirelessly
diff --git a/documentation/scanner-and-connection.md b/documentation/scanner-and-connection.md
new file mode 100644
index 0000000..2c5dbae
--- /dev/null
+++ b/documentation/scanner-and-connection.md
@@ -0,0 +1,124 @@
+# Scanner & Connection
+
+## BLE Scanner (Home Screen)
+
+The BLE Scanner is the app's home screen, displayed immediately on launch.
+
+### How to Access
+
+- Opens automatically when the app starts
+- Returns here when disconnecting from any device
+- Accessible by navigating back from a connected session
+
+### What the User Sees
+
+**App Bar**: Centered title "Scanner".
+
+**Bluetooth-Off Warning Banner** (conditional): Appears when the Bluetooth adapter is off, showing a `bluetooth_disabled` icon, a warning message, and on Android, an "Enable Bluetooth" button.
+
+**Status Bar**: A full-width colored strip reflecting the current connection state:
+
+| State | Text | Color |
+|---|---|---|
+| Disconnected | "Not connected" | Grey |
+| Scanning | "Scanning..." | Blue |
+| Connecting | "Connecting..." | Orange |
+| Connected | "Connected to \" | Green |
+| Disconnecting | "Disconnecting..." | Orange |
+
+**Device List**: When no devices are found, shows a large Bluetooth icon with a prompt. The prompt text is dynamic: "Searching for devices..." while actively scanning, or "Tap Scan to search" when idle. When devices are found, shows a scrollable list of `DeviceTile` widgets.
+
+**Bottom FAB Row**: Up to three floating action buttons:
+- **USB** button - Opens USB connection screen (Android, Windows, Linux, macOS, Chrome web only)
+- **TCP/IP** button - Opens TCP connection screen (all non-web platforms)
+- **BLE Scan** button - Toggles BLE scanning on/off; shows a spinner when scanning. **Disabled** (greyed out, not tappable) when Bluetooth is off
+
+### Device Tile
+
+Each discovered device is displayed as a list tile showing:
+- **Signal strength icon** (color-coded by RSSI):
+ - Green: >= -60 dBm (excellent)
+ - Light green: -60 to -70 dBm (good)
+ - Amber: -70 to -80 dBm (fair)
+ - Orange: -80 to -90 dBm (weak)
+ - Red: < -90 dBm (poor)
+- **RSSI value** in dBm (e.g., "-72 dBm")
+- **Device name** (falls back to "Unknown Device")
+- **Device ID** (BLE MAC address on Android; a system-assigned UUID on iOS/macOS)
+- **Connect button** (the entire tile row is also tappable — both trigger connection)
+
+Note: The weak (-80 to -90 dBm) and poor (< -90 dBm) tiers share the same icon shape and are only differentiated by color (orange vs. red).
+
+### How Scanning Works
+
+- Filters for devices with names starting with `MeshCore-` or `Whisper-`
+- Uses low-latency scan mode on Android
+- Scans for 10 seconds then auto-stops
+- On iOS/macOS, waits for BLE adapter initialization before starting
+- If Bluetooth is turned off during a scan, scanning stops immediately
+
+### Connecting to a Device
+
+Tap a device tile or its Connect button:
+1. The connector stops scanning and transitions to "connecting"
+2. Connects to the device with a 15-second timeout
+3. Requests MTU 185 bytes for optimal throughput
+4. Discovers BLE services and locates the Nordic UART Service
+5. Subscribes to TX notifications for receiving data
+6. On success, automatically navigates to the Contacts screen
+7. On failure, shows a red error snackbar
+
+---
+
+## USB Connection
+
+### How to Access
+
+From the Scanner screen, tap the **USB** FAB button.
+
+### What the User Sees
+
+- A colored status bar at the top (same color scheme as BLE scanner)
+- A list of detected USB serial ports, each showing:
+ - Friendly display name
+ - Raw port name (subtitle, only shown when it differs from the display name)
+ - "Connect" button
+- FABs at the bottom to switch to BLE or TCP (these use `pushReplacement`, so back navigation returns to Scanner, not between USB/TCP)
+
+### Key Interactions
+
+- On desktop (Windows, Linux, macOS): ports are polled every 2 seconds for hot-plug detection (polling pauses while connecting/connected)
+- On mobile: tap the "Scan" FAB to manually refresh
+- Tap a port or its Connect button to connect
+- On successful connection, navigates to Contacts screen
+- On connection failure, the port list automatically refreshes
+- Platform-specific error messages for common USB failures (permission denied, device missing, device detached, device busy, driver missing, port invalid, timeout, and more)
+
+---
+
+## TCP Connection
+
+### How to Access
+
+From the Scanner screen, tap the **TCP/IP** FAB button.
+
+### What the User Sees
+
+- A colored status bar at the top
+- **Host address** text field
+- **Port number** text field
+- **Connect** button
+- FABs at the bottom to switch to USB or BLE
+
+### Key Interactions
+
+- Last-used host and port are pre-populated from saved settings
+- Tap Connect to validate inputs and connect
+ - Host must not be empty
+ - Port must be a number between 1 and 65535
+ - Validation errors are shown as red snackbars
+- The Connect button shows a spinner and "Connecting..." label while in progress
+- The status bar shows the specific host:port being connected to (e.g., "Connecting to 192.168.1.1:5000")
+- On success, navigates to Contacts screen and saves the host/port to settings
+- On connection, the status bar shows the active TCP endpoint (e.g., "Connected to 192.168.1.1:5000")
+- Error messages for timeout, unsupported platform, and connection failures
diff --git a/documentation/settings.md b/documentation/settings.md
new file mode 100644
index 0000000..70e39e2
--- /dev/null
+++ b/documentation/settings.md
@@ -0,0 +1,169 @@
+# Settings
+
+## How to Access
+
+- From the Device Screen: tap the tune/sliders icon in the app bar
+- From Contacts or Channels: overflow menu (three-dot) → Settings
+
+Settings are only accessible while a device is connected.
+
+## Settings Screen Layout
+
+The settings screen is a scrollable list of cards:
+
+1. [Device Info](#device-info)
+2. [App Settings](#app-settings) (link to sub-screen)
+3. [Node Settings](#node-settings)
+4. [Actions](#actions)
+5. [Debug](#debug)
+6. [Export](#export)
+7. [About](#about)
+
+---
+
+## Device Info
+
+A collapsible card showing read-only device information. **Collapsed by default** — tap the header to expand with an animated chevron indicator:
+
+| Field | Description |
+|---|---|
+| Name | Connected device's display name |
+| ID | Device identifier |
+| Status | Connected / Disconnected |
+| Battery | Percentage or voltage (tap to toggle) |
+| Node Name | The node's mesh identity name |
+| Public Key | First 16 hex characters + "..." |
+| Contacts Count | Number of known contacts |
+| Channel Count | Number of configured channels |
+
+Battery shows an alert icon and orange text when at 15% or below. The toggle only works when millivolt data is available from the firmware.
+
+---
+
+## App Settings
+
+A dedicated sub-screen for app-level preferences (nothing here is sent to the device). All settings persist locally via SharedPreferences.
+
+### Appearance
+- **Theme**: System / Light / Dark
+- **Language**: System default or one of 15 languages (English, French, Spanish, German, Polish, Slovenian, Portuguese, Italian, Chinese, Swedish, Dutch, Slovak, Bulgarian, Russian, Ukrainian)
+- **Enable Message Tracing**: Shows path trace overlays and extra metadata on messages
+
+### Notifications
+- **Master enable/disable**: Requests OS permission when enabling
+- **Message notifications**: New direct message alerts
+- **Channel message notifications**: New channel message alerts
+- **Advertisement notifications**: New node discovery alerts
+
+### Messaging
+- **Clear Path on Max Retry**: Erases the stored routing path after all retries fail
+- **Auto Route Rotation**: Enables weighted routing algorithm. When enabled, expands to show five slider sub-settings (hidden when off):
+ - Max Route Weight (1–10, default 5, integer steps)
+ - Initial Route Weight (0.5–5.0, default 3.0)
+ - Success Increment (0.1–2.0, default 0.5, 0.1 steps)
+ - Failure Decrement (0.1–2.0, default 0.2, 0.1 steps)
+ - Max Message Retries (2–10, default 5)
+
+### Battery
+- **Battery Chemistry**: NMC / LiFePO4 / LiPo (per device, used to calibrate percentage from voltage)
+
+### Map Display
+- **Show Repeaters**: Toggle repeater markers on map
+- **Show Chat Nodes**: Toggle chat node markers
+- **Show Other Nodes**: Toggle room/sensor markers
+- **Time Filter**: All time / Last 1h / Last 6h / Last 24h / Last week
+- **Units**: Metric / Imperial
+- **Offline Map Cache**: Navigate to tile download screen
+
+### Debug
+- **App Debug Logging**: Enable the in-app debug log
+
+---
+
+## Node Settings
+
+These settings are sent directly to the connected device firmware.
+
+### Node Name
+- Opens a dialog with a text field (max 31 characters)
+- Sends the new name to the device
+- Confirmed via snackbar
+
+### Radio Settings
+Opens a dialog pre-populated with the device's current radio settings. Contains:
+- **Preset dropdown**: 19 regional presets — selecting a preset immediately fills all fields below. Full list: Australia, Australia (Narrow), Australia SA/WA/QLD, Czech Republic, EU 433MHz, EU/UK (Long Range), EU/UK (Medium Range), EU/UK (Narrow), New Zealand, New Zealand (Narrow), Portugal 433, Portugal 869, Switzerland, USA Arizona, USA/Canada, Vietnam, Off-Grid 433, Off-Grid 869, Off-Grid 918
+- **Frequency** (MHz): Free text, validated 300–2500 MHz
+- **Bandwidth**: Dropdown (7.8 / 10.4 / 15.6 / 20.8 / 31.25 / 41.7 / 62.5 / 125 / 250 / 500 kHz)
+- **Spreading Factor**: SF5–SF12
+- **Coding Rate**: 4/5, 4/6, 4/7, 4/8
+- **TX Power** (dBm): Validated 0 to device max (typically 22 dBm)
+- **Client Repeat** toggle: Only shown on firmware v9+; requires frequency to be exactly 433.000, 869.000, or 918.000 MHz (the Off-Grid presets). Save is blocked with a warning if enabled on other frequencies
+
+### Location
+Opens a dialog pre-populated with the device's current coordinates (if known):
+- Latitude and longitude fields (decimal, 6 decimal places). If only one field is provided, the other uses the device's current value
+- If GPS-capable hardware (detected via `gps` custom variable):
+ - GPS Update Interval (seconds, 60–86399, default 900 = 15 minutes). Validated and sent separately before lat/lon
+ - Enable GPS toggle (takes effect immediately, not deferred to Save)
+- Validation: lat ±90, lon ±180
+
+### Contact Settings
+Five toggles controlling which node types are auto-added when heard:
+- Auto-add Chat Users
+- Auto-add Repeaters
+- Auto-add Room Servers
+- Auto-add Sensors
+- Overwrite Oldest (when contact list is full)
+
+### Privacy Mode
+Opens a confirmation dialog with three buttons: Cancel, Enable, and Disable. Both states can be set from the same dialog regardless of current state. A snackbar confirms which state was applied. When on, the node stops broadcasting its location in advertisements.
+
+---
+
+## Actions
+
+One-tap device operations:
+
+| Action | Description |
+|---|---|
+| Send Advertisement | Floods the mesh with your node's advertisement |
+| Sync Time | Sends current Unix timestamp to the device |
+| Refresh Contacts | Re-requests the full contact list |
+| Reboot Device | Confirmation dialog → reboots the device (shown in orange) |
+
+---
+
+## Debug
+
+Two log viewers accessible via list tiles:
+
+### BLE Debug Log
+Two views (togglable via segmented button):
+- **Frames view**: Direction icon, description, hex preview, timestamp per frame. Long-press to copy hex.
+- **Raw Log RX view**: Decoded LoRa packets with route type, payload type, path, and summary.
+- Copy-all and Clear buttons in the app bar.
+
+### App Debug Log
+Structured log entries (Info / Warning / Error), with tag, message, and timestamp.
+- Must be enabled first in App Settings → Debug
+- Copy-all and Clear buttons
+
+---
+
+## Export
+
+Three GPX export options (not available on web):
+
+| Option | Exports |
+|---|---|
+| Export Repeaters | Repeaters and Rooms with GPS coordinates |
+| Export Contacts | Chat contacts with GPS coordinates |
+| Export All | All contacts with GPS coordinates |
+
+Each creates a `.gpx` file and opens the OS share sheet. Feedback via snackbar for four outcomes: success, no contacts with coordinates, feature not available (web), or error.
+
+---
+
+## About
+
+Shows the standard Flutter about dialog with app name, version, and legal notice.
diff --git a/flake.lock b/flake.lock
new file mode 100644
index 0000000..4d0355b
--- /dev/null
+++ b/flake.lock
@@ -0,0 +1,61 @@
+{
+ "nodes": {
+ "flake-utils": {
+ "inputs": {
+ "systems": "systems"
+ },
+ "locked": {
+ "lastModified": 1731533236,
+ "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
+ "type": "github"
+ },
+ "original": {
+ "owner": "numtide",
+ "repo": "flake-utils",
+ "type": "github"
+ }
+ },
+ "nixpkgs": {
+ "locked": {
+ "lastModified": 1770562336,
+ "narHash": "sha256-ub1gpAONMFsT/GU2hV6ZWJjur8rJ6kKxdm9IlCT0j84=",
+ "owner": "nixos",
+ "repo": "nixpkgs",
+ "rev": "d6c71932130818840fc8fe9509cf50be8c64634f",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nixos",
+ "ref": "nixos-unstable",
+ "repo": "nixpkgs",
+ "type": "github"
+ }
+ },
+ "root": {
+ "inputs": {
+ "flake-utils": "flake-utils",
+ "nixpkgs": "nixpkgs"
+ }
+ },
+ "systems": {
+ "locked": {
+ "lastModified": 1681028828,
+ "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
+ "owner": "nix-systems",
+ "repo": "default",
+ "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
+ "type": "github"
+ },
+ "original": {
+ "owner": "nix-systems",
+ "repo": "default",
+ "type": "github"
+ }
+ }
+ },
+ "root": "root",
+ "version": 7
+}
diff --git a/flake.nix b/flake.nix
new file mode 100644
index 0000000..1671145
--- /dev/null
+++ b/flake.nix
@@ -0,0 +1,86 @@
+{
+ description = "MeshCore Flutter Application";
+
+ inputs = {
+ nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
+ flake-utils.url = "github:numtide/flake-utils";
+ };
+
+ outputs = { self, nixpkgs, flake-utils }:
+ flake-utils.lib.eachDefaultSystem (system:
+ let
+ pkgs = nixpkgs.legacyPackages.${system};
+ in
+ {
+ devShells.default = pkgs.mkShell {
+ buildInputs = with pkgs; [
+ # Flutter and Dart
+ flutter
+ dart
+
+ # Java (required for Android development)
+ jdk17
+
+ # Android development tools
+ android-tools
+ gradle
+
+ # For the shell hook to set up the environment for Flutter development
+ gtk3
+ glib
+ sysprof
+ libclang
+ cmake
+ ninja
+ pkg-config
+ libdatrie
+
+ # Additional tools for installing Android SDK if not present
+ curl
+ unzip
+ ];
+
+ shellHook = ''
+ echo "MeshCore Flutter Development Environment"
+ export PKG_CONFIG_PATH="${pkgs.gtk3}/lib/pkgconfig:${pkgs.glib}/lib/pkgconfig:${pkgs.sysprof}/lib/pkgconfig:$PKG_CONFIG_PATH"
+ export LD_LIBRARY_PATH="${pkgs.lib.makeLibraryPath [pkgs.gtk3 pkgs.glib pkgs.sysprof pkgs.libdatrie]}:$LD_LIBRARY_PATH"
+ export CMAKE_INSTALL_PREFIX="$PWD/build/bundle"
+
+ # Setup Android SDK in home directory (standard location)
+ export ANDROID_HOME="$HOME/Android/Sdk"
+ export ANDROID_SDK_ROOT="$ANDROID_HOME"
+ export PATH="$ANDROID_HOME/cmdline-tools/latest/bin:$ANDROID_HOME/platform-tools:$ANDROID_HOME/tools/bin:$PATH"
+
+ echo "Android SDK: $ANDROID_HOME"
+ echo ""
+
+ # Check if Android SDK exists and offer to download if not
+ if [ ! -d "$ANDROID_HOME" ]; then
+ echo "WARNING: Android SDK not found at $ANDROID_HOME"
+ echo ""
+ echo "To download and set up the Android SDK, run this command:"
+ echo ""
+ cat << 'EOF'
+mkdir -p ~/Android/Sdk && cd ~/Android/Sdk && \
+curl -o cmdline-tools.zip ${if pkgs.stdenv.isDarwin then "https://dl.google.com/android/repository/commandlinetools-mac-10406996_latest.zip" else "https://dl.google.com/android/repository/commandlinetools-linux-10406996_latest.zip"} && \
+unzip -q cmdline-tools.zip && \
+mkdir -p cmdline-tools/latest && \
+mv cmdline-tools/* cmdline-tools/latest/ 2>/dev/null || echo "Warning: failed to move Android cmdline-tools into 'latest' directory; please check your SDK layout." >&2 && \
+rm cmdline-tools.zip && \
+cd cmdline-tools/latest/bin && \
+yes | ./sdkmanager --sdk_root=~/Android/Sdk 'platform-tools' && \
+echo "Android SDK setup complete!"
+EOF
+ echo ""
+ echo "Then run 'flutter doctor' again to verify."
+ echo ""
+ else
+ echo "Android SDK found at $ANDROID_HOME"
+ fi
+
+ echo "To check that everything is set up correctly, run 'flutter doctor' and ensure there are no issues."
+ '';
+ };
+ }
+ );
+}
diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf7..391a902 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 13.0
diff --git a/ios/Flutter/Debug.xcconfig b/ios/Flutter/Debug.xcconfig
index 592ceee..ec97fc6 100644
--- a/ios/Flutter/Debug.xcconfig
+++ b/ios/Flutter/Debug.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Flutter/Release.xcconfig b/ios/Flutter/Release.xcconfig
index 592ceee..c4855bf 100644
--- a/ios/Flutter/Release.xcconfig
+++ b/ios/Flutter/Release.xcconfig
@@ -1 +1,2 @@
+#include? "Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"
#include "Generated.xcconfig"
diff --git a/ios/Podfile b/ios/Podfile
index 69ed111..24a17c4 100644
--- a/ios/Podfile
+++ b/ios/Podfile
@@ -1,4 +1,4 @@
-platform :ios, '12.0'
+platform :ios, '16.4'
ENV['COCOAPODS_DISABLE_STATS'] = 'true'
@@ -32,5 +32,8 @@ end
post_install do |installer|
installer.pods_project.targets.each do |target|
flutter_additional_ios_build_settings(target)
+ target.build_configurations.each do |config|
+ config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '16.4'
+ end
end
end
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
new file mode 100644
index 0000000..b0e98ca
--- /dev/null
+++ b/ios/Podfile.lock
@@ -0,0 +1,74 @@
+PODS:
+ - Flutter (1.0.0)
+ - flutter_blue_plus_darwin (0.0.2):
+ - Flutter
+ - FlutterMacOS
+ - flutter_foreground_task (0.0.1):
+ - Flutter
+ - flutter_local_notifications (0.0.1):
+ - Flutter
+ - mobile_scanner (7.0.0):
+ - Flutter
+ - FlutterMacOS
+ - package_info_plus (0.4.5):
+ - Flutter
+ - share_plus (0.0.1):
+ - Flutter
+ - shared_preferences_foundation (0.0.1):
+ - Flutter
+ - FlutterMacOS
+ - sqflite_darwin (0.0.4):
+ - Flutter
+ - FlutterMacOS
+ - url_launcher_ios (0.0.1):
+ - Flutter
+
+DEPENDENCIES:
+ - Flutter (from `Flutter`)
+ - flutter_blue_plus_darwin (from `.symlinks/plugins/flutter_blue_plus_darwin/darwin`)
+ - flutter_foreground_task (from `.symlinks/plugins/flutter_foreground_task/ios`)
+ - flutter_local_notifications (from `.symlinks/plugins/flutter_local_notifications/ios`)
+ - mobile_scanner (from `.symlinks/plugins/mobile_scanner/darwin`)
+ - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`)
+ - share_plus (from `.symlinks/plugins/share_plus/ios`)
+ - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`)
+ - sqflite_darwin (from `.symlinks/plugins/sqflite_darwin/darwin`)
+ - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`)
+
+EXTERNAL SOURCES:
+ Flutter:
+ :path: Flutter
+ flutter_blue_plus_darwin:
+ :path: ".symlinks/plugins/flutter_blue_plus_darwin/darwin"
+ flutter_foreground_task:
+ :path: ".symlinks/plugins/flutter_foreground_task/ios"
+ flutter_local_notifications:
+ :path: ".symlinks/plugins/flutter_local_notifications/ios"
+ mobile_scanner:
+ :path: ".symlinks/plugins/mobile_scanner/darwin"
+ package_info_plus:
+ :path: ".symlinks/plugins/package_info_plus/ios"
+ share_plus:
+ :path: ".symlinks/plugins/share_plus/ios"
+ shared_preferences_foundation:
+ :path: ".symlinks/plugins/shared_preferences_foundation/darwin"
+ sqflite_darwin:
+ :path: ".symlinks/plugins/sqflite_darwin/darwin"
+ url_launcher_ios:
+ :path: ".symlinks/plugins/url_launcher_ios/ios"
+
+SPEC CHECKSUMS:
+ Flutter: cabc95a1d2626b1b06e7179b784ebcf0c0cde467
+ flutter_blue_plus_darwin: 20a08bfeaa0f7804d524858d3d8744bcc1b6dbc3
+ flutter_foreground_task: a159d2c2173b33699ddb3e6c2a067045d7cebb89
+ flutter_local_notifications: a5a732f069baa862e728d839dd2ebb904737effb
+ mobile_scanner: 9157936403f5a0644ca3779a38ff8404c5434a93
+ package_info_plus: af8e2ca6888548050f16fa2f1938db7b5a5df499
+ share_plus: 50da8cb520a8f0f65671c6c6a99b3617ed10a58a
+ shared_preferences_foundation: 7036424c3d8ec98dfe75ff1667cb0cd531ec82bb
+ sqflite_darwin: 20b2a3a3b70e43edae938624ce550a3cbf66a3d0
+ url_launcher_ios: 7a95fa5b60cc718a708b8f2966718e93db0cef1b
+
+PODFILE CHECKSUM: e42b502c78c33aa1ed9d42eaea8960ce2139504b
+
+COCOAPODS: 1.16.2
diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj
index 09c8350..7bf9b4c 100644
--- a/ios/Runner.xcodeproj/project.pbxproj
+++ b/ios/Runner.xcodeproj/project.pbxproj
@@ -14,6 +14,7 @@
97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };
97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };
97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };
+ 9A698254711B63C3940A64CB /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 4268181FCF3E12817B700E9C /* libPods-Runner.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -42,9 +43,13 @@
/* Begin PBXFileReference section */
1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; };
1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; };
+ 24A76623340E493BD4C25C5C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; };
331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; };
331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; };
+ 40AC50CE3E1D4278E82498CF /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; };
+ 4268181FCF3E12817B700E9C /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Runner.a"; sourceTree = BUILT_PRODUCTS_DIR; };
+ 718BC7DCCFC5C370705C12E5 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; };
74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; };
74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; };
@@ -62,6 +67,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9A698254711B63C3940A64CB /* libPods-Runner.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -94,6 +100,8 @@
97C146F01CF9000F007C117D /* Runner */,
97C146EF1CF9000F007C117D /* Products */,
331C8082294A63A400263BE5 /* RunnerTests */,
+ DEE6F094D3B70E76087722E1 /* Pods */,
+ DAE613E34DF694C2E33B64C7 /* Frameworks */,
);
sourceTree = "";
};
@@ -121,6 +129,25 @@
path = Runner;
sourceTree = "";
};
+ DAE613E34DF694C2E33B64C7 /* Frameworks */ = {
+ isa = PBXGroup;
+ children = (
+ 4268181FCF3E12817B700E9C /* libPods-Runner.a */,
+ );
+ name = Frameworks;
+ sourceTree = "";
+ };
+ DEE6F094D3B70E76087722E1 /* Pods */ = {
+ isa = PBXGroup;
+ children = (
+ 40AC50CE3E1D4278E82498CF /* Pods-Runner.debug.xcconfig */,
+ 24A76623340E493BD4C25C5C /* Pods-Runner.release.xcconfig */,
+ 718BC7DCCFC5C370705C12E5 /* Pods-Runner.profile.xcconfig */,
+ );
+ name = Pods;
+ path = Pods;
+ sourceTree = "";
+ };
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
@@ -145,12 +172,15 @@
isa = PBXNativeTarget;
buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */;
buildPhases = (
+ DE3B2E091393835C0B38492E /* [CP] Check Pods Manifest.lock */,
9740EEB61CF901F6004384FC /* Run Script */,
97C146EA1CF9000F007C117D /* Sources */,
97C146EB1CF9000F007C117D /* Frameworks */,
97C146EC1CF9000F007C117D /* Resources */,
9705A1C41CF9048500538489 /* Embed Frameworks */,
3B06AD1E1E4923F5004D2608 /* Thin Binary */,
+ F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */,
+ B788CEDB957A87EE8AC593BB /* [CP] Copy Pods Resources */,
);
buildRules = (
);
@@ -253,6 +283,61 @@
shellPath = /bin/sh;
shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build";
};
+ B788CEDB957A87EE8AC593BB /* [CP] Copy Pods Resources */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-input-files.xcfilelist",
+ );
+ name = "[CP] Copy Pods Resources";
+ outputFileListPaths = (
+ "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources-${CONFIGURATION}-output-files.xcfilelist",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-resources.sh\"\n";
+ showEnvVarsInLog = 0;
+ };
+ F0D7F2413C6E4B7A9B1C2D3E /* Fix Native Asset Minimum OS */ = {
+ isa = PBXShellScriptBuildPhase;
+ alwaysOutOfDate = 1;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputPaths = (
+ "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}",
+ );
+ name = "Fix Native Asset Minimum OS";
+ outputPaths = (
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "set -e\nFRAMEWORKS_DIR=\"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\nMIN_OS=\"${IPHONEOS_DEPLOYMENT_TARGET}\"\nif [ ! -d \"$FRAMEWORKS_DIR\" ] || [ -z \"$MIN_OS\" ]; then\n exit 0\nfi\nfind \"$FRAMEWORKS_DIR\" -maxdepth 2 -name Info.plist | while read -r plist; do\n bundle_id=$(/usr/libexec/PlistBuddy -c 'Print :CFBundleIdentifier' \"$plist\" 2>/dev/null || true)\n case \"$bundle_id\" in\n io.flutter.flutter.native-assets.*)\n /usr/libexec/PlistBuddy -c \"Set :MinimumOSVersion $MIN_OS\" \"$plist\" 2>/dev/null || \\\n /usr/libexec/PlistBuddy -c \"Add :MinimumOSVersion string $MIN_OS\" \"$plist\"\n ;;\n esac\ndone\n";
+ };
+ DE3B2E091393835C0B38492E /* [CP] Check Pods Manifest.lock */ = {
+ isa = PBXShellScriptBuildPhase;
+ buildActionMask = 2147483647;
+ files = (
+ );
+ inputFileListPaths = (
+ );
+ inputPaths = (
+ "${PODS_PODFILE_DIR_PATH}/Podfile.lock",
+ "${PODS_ROOT}/Manifest.lock",
+ );
+ name = "[CP] Check Pods Manifest.lock";
+ outputFileListPaths = (
+ );
+ outputPaths = (
+ "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt",
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ shellPath = /bin/sh;
+ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
+ showEnvVarsInLog = 0;
+ };
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -346,7 +431,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -368,7 +453,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
@@ -384,7 +469,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -401,7 +486,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -416,7 +501,7 @@
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen.RunnerTests;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -472,7 +557,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MTL_ENABLE_DEBUG_INFO = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = iphoneos;
@@ -523,7 +608,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- IPHONEOS_DEPLOYMENT_TARGET = 13.0;
+ IPHONEOS_DEPLOYMENT_TARGET = 16.4;
MTL_ENABLE_DEBUG_INFO = NO;
SDKROOT = iphoneos;
SUPPORTED_PLATFORMS = iphoneos;
@@ -547,7 +632,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -569,7 +654,7 @@
"$(inherited)",
"@executable_path/Frameworks",
);
- PRODUCT_BUNDLE_IDENTIFIER = com.meshcore.meshcoreOpen;
+ PRODUCT_BUNDLE_IDENTIFIER = com.monitormx.meshcoreopen;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0;
diff --git a/ios/Runner.xcworkspace/contents.xcworkspacedata b/ios/Runner.xcworkspace/contents.xcworkspacedata
index 1d526a1..21a3cc1 100644
--- a/ios/Runner.xcworkspace/contents.xcworkspacedata
+++ b/ios/Runner.xcworkspace/contents.xcworkspacedata
@@ -4,4 +4,7 @@
+
+
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 6266644..c30b367 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -2,12 +2,15 @@ import Flutter
import UIKit
@main
-@objc class AppDelegate: FlutterAppDelegate {
+@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
+ GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ }
}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index b4e35ed..00d9efa 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -2,6 +2,8 @@
+ CADisableMinimumFrameDurationOnPhone
+
CFBundleDevelopmentRegion
$(DEVELOPMENT_LANGUAGE)
CFBundleDisplayName
@@ -22,8 +24,46 @@
????
CFBundleVersion
$(FLUTTER_BUILD_NUMBER)
+ LSApplicationQueriesSchemes
+
+ http
+ https
+
LSRequiresIPhoneOS
+ NSBluetoothAlwaysUsageDescription
+ This app uses Bluetooth to communicate with MeshCore devices.
+ NSBluetoothPeripheralUsageDescription
+ This app uses Bluetooth to communicate with MeshCore devices.
+ NSCameraUsageDescription
+ This app uses the camera to scan QR codes for joining communities.
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
+ UIApplicationSupportsIndirectInputEvents
+
+ UIBackgroundModes
+
+ bluetooth-central
+
UILaunchStoryboardName
LaunchScreen
UIMainStoryboardFile
@@ -41,19 +81,5 @@
UIInterfaceOrientationLandscapeLeft
UIInterfaceOrientationLandscapeRight
- CADisableMinimumFrameDurationOnPhone
-
- UIApplicationSupportsIndirectInputEvents
-
- UIBackgroundModes
-
- bluetooth-central
-
- NSBluetoothAlwaysUsageDescription
- This app uses Bluetooth to communicate with MeshCore devices.
- NSBluetoothPeripheralUsageDescription
- This app uses Bluetooth to communicate with MeshCore devices.
- NSCameraUsageDescription
- This app uses the camera to scan QR codes for joining communities.
diff --git a/lib/connector/meshcore_connector.dart b/lib/connector/meshcore_connector.dart
index 29f92af..b432277 100644
--- a/lib/connector/meshcore_connector.dart
+++ b/lib/connector/meshcore_connector.dart
@@ -1,40 +1,85 @@
import 'dart:async';
import 'dart:convert';
+import 'dart:math' as math;
import 'package:crypto/crypto.dart' as crypto;
import 'package:pointycastle/export.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_blue_plus/flutter_blue_plus.dart';
-import 'package:wakelock_plus/wakelock_plus.dart';
+import 'package:flutter_blue_plus_platform_interface/flutter_blue_plus_platform_interface.dart';
import '../models/channel.dart';
import '../models/channel_message.dart';
+import '../models/companion_radio_stats.dart';
import '../models/contact.dart';
import '../models/message.dart';
import '../models/path_selection.dart';
+import '../models/translation_support.dart';
import '../helpers/reaction_helper.dart';
import '../helpers/smaz.dart';
import '../services/app_debug_log_service.dart';
import '../services/ble_debug_log_service.dart';
+import '../services/linux_ble_error_classifier.dart';
+import '../services/linux_ble_pairing_service_stub.dart'
+ if (dart.library.io) '../services/linux_ble_pairing_service.dart';
import '../services/message_retry_service.dart';
import '../services/path_history_service.dart';
import '../services/app_settings_service.dart';
import '../services/background_service.dart';
+import '../services/timeout_prediction_service.dart';
+import '../services/translation_service.dart';
import '../services/notification_service.dart';
+import 'meshcore_connector_usb.dart';
+import 'meshcore_connector_tcp.dart';
import '../storage/channel_message_store.dart';
import '../storage/channel_order_store.dart';
import '../storage/channel_settings_store.dart';
+import '../storage/channel_store.dart';
+import '../storage/contact_discovery_store.dart';
import '../storage/contact_settings_store.dart';
import '../storage/contact_store.dart';
import '../storage/message_store.dart';
import '../storage/unread_store.dart';
import '../utils/app_logger.dart';
+import '../utils/battery_utils.dart';
+import '../utils/platform_info.dart';
+import 'meshcore_uuids.dart';
import 'meshcore_protocol.dart';
-class MeshCoreUuids {
- static const String service = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
- static const String rxCharacteristic = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
- static const String txCharacteristic = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
+class DirectRepeater {
+ static const int maxAgeMinutes = 30; // Max age for direct repeater info
+ final int pubkeyFirstByte;
+ double snr;
+ DateTime lastUpdated;
+
+ DirectRepeater({
+ required this.pubkeyFirstByte,
+ required this.snr,
+ DateTime? lastUpdated,
+ }) : lastUpdated = lastUpdated ?? DateTime.now();
+
+ void update(double newSNR) {
+ snr = newSNR;
+ lastUpdated = DateTime.now();
+ }
+
+ int get ranking {
+ if (isStale()) {
+ return -1; // Stale repeaters get lowest rank
+ }
+ // Higher SNR gets higher rank and recency within maxAgeMinutes breaks ties.
+ final ageMs =
+ DateTime.now().millisecondsSinceEpoch -
+ lastUpdated.millisecondsSinceEpoch;
+ final maxAgeMs = maxAgeMinutes * 60 * 1000;
+ final recencyScore = (maxAgeMs - ageMs).clamp(0, maxAgeMs);
+ return ((snr - 31.75) * 1000).round() + recencyScore;
+ }
+
+ bool isStale() {
+ return DateTime.now().difference(lastUpdated) >
+ const Duration(minutes: maxAgeMinutes);
+ }
}
enum MeshCoreConnectionState {
@@ -45,6 +90,36 @@ enum MeshCoreConnectionState {
disconnecting,
}
+enum MeshCoreTransportType { bluetooth, usb, tcp }
+
+class RepeaterBatterySnapshot {
+ final int millivolts;
+ final DateTime updatedAt;
+ final String source;
+
+ const RepeaterBatterySnapshot({
+ required this.millivolts,
+ required this.updatedAt,
+ required this.source,
+ });
+}
+
+class MeshCoreRadioStateSnapshot {
+ final int freqHz;
+ final int bwHz;
+ final int sf;
+ final int cr;
+ final int txPowerDbm;
+
+ const MeshCoreRadioStateSnapshot({
+ required this.freqHz,
+ required this.bwHz,
+ required this.sf,
+ required this.cr,
+ required this.txPowerDbm,
+ });
+}
+
class MeshCoreConnector extends ChangeNotifier {
// Message windowing to limit memory usage
static const int _messageWindowSize = 200;
@@ -59,25 +134,44 @@ class MeshCoreConnector extends ChangeNotifier {
String? _lastDeviceId;
String? _lastDeviceDisplayName;
bool _manualDisconnect = false;
+ final MeshCoreUsbManager _usbManager = MeshCoreUsbManager();
+ final LinuxBlePairingService _linuxBlePairingService =
+ LinuxBlePairingService();
+ StreamSubscription? _usbFrameSubscription;
+ final MeshCoreTcpConnector _tcpConnector = MeshCoreTcpConnector();
+ MeshCoreTransportType _activeTransport = MeshCoreTransportType.bluetooth;
final List _scanResults = [];
+ final List _linuxSystemScanResults = [];
final List _contacts = [];
+ final List _discoveredContacts = [];
final List _channels = [];
final Map> _conversations = {};
final Map> _channelMessages = {};
+ final List _pendingChannelSentQueue = [];
+ final List<_PendingCommandAck> _pendingGenericAckQueue = [];
+ static const String _reactionSendQueuePrefix = '__reaction_send__';
+ int _reactionSendQueueSequence = 0;
final Set _loadedConversationKeys = {};
final Map> _processedChannelReactions =
- {}; // channelIndex -> Set of "reactionKey_emoji"
+ {}; // channelIndex -> Set of "targetHash_emoji"
final Map> _processedContactReactions =
- {}; // contactPubKeyHex -> Set of "reactionKey_emoji"
+ {}; // contactPubKeyHex -> Set of "targetHash_emoji"
StreamSubscription>? _scanSubscription;
StreamSubscription? _connectionSubscription;
StreamSubscription>? _notifySubscription;
+ Timer? _notifyListenersTimer;
Timer? _selfInfoRetryTimer;
Timer? _reconnectTimer;
Timer? _batteryPollTimer;
+ Timer? _radioStatsPollTimer;
+ int _radioStatsPollRefCount = 0;
+ final ValueNotifier radioStatsNotifier =
+ ValueNotifier(null);
int _reconnectAttempts = 0;
+ bool _notifyListenersDirty = false;
+ static const Duration _notifyListenersDebounce = Duration(milliseconds: 50);
final StreamController _receivedFramesController =
StreamController.broadcast();
@@ -90,14 +184,57 @@ class MeshCoreConnector extends ChangeNotifier {
int? _currentBwHz;
int? _currentSf;
int? _currentCr;
+ bool? _clientRepeat;
+ MeshCoreRadioStateSnapshot? _rememberedNonRepeatRadioState;
+ int? _firmwareVerCode;
+ int _pathHashByteWidth = 1;
+ CompanionRadioStats? _latestRadioStats;
+ Stopwatch? _airtimeBumpStopwatch;
+ int _prevTotalAirSecs = 0;
int? _batteryMillivolts;
double? _selfLatitude;
double? _selfLongitude;
+ final List _directRepeaters = List.empty(growable: true);
bool _isLoadingContacts = false;
bool _isLoadingChannels = false;
+ bool _hasLoadedChannels = false;
+ TimeoutPredictionService? _timeoutPredictionService;
+ TranslationService? _translationService;
+ // Intentionally global (not per-contact): tracks overall network activity.
+ // Frequent RX from any source indicates a busy network with more collisions.
+ DateTime _lastRxTime = DateTime.now();
+ DateTime _lastRadioRxTime = DateTime.fromMillisecondsSinceEpoch(0);
+ DateTime _lastContactMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
+ DateTime _lastChannelMsgRxTime = DateTime.fromMillisecondsSinceEpoch(0);
+ static const int _radioQuietMs = 3000;
+ static const int _radioQuietMaxWaitMs = 3000;
+
+ /// When companion radio stats are unavailable, keep the legacy fixed backoff.
+ static const int _contactMsgBackoffFallbackMs = 5000;
+ static const int _contactMsgBackoffMinMs = 500;
+ static const int _contactMsgBackoffMaxMs = 15000;
+ int _pollingInterval = 30;
bool _batteryRequested = false;
bool _awaitingSelfInfo = false;
+ bool _hasReceivedDeviceInfo = false;
+ bool _pendingInitialChannelSync = false;
+ bool _pendingInitialContactsSync = false;
+ bool _bleInitialSyncStarted = false;
+ bool _pendingDeferredChannelSyncAfterContacts = false;
+ bool _webInitialHandshakeRequestSent = false;
bool _preserveContactsOnRefresh = false;
+ bool _autoAddUsers = false;
+ bool _autoAddRepeaters = false;
+ bool _autoAddRoomServers = false;
+ bool _autoAddSensors = false;
+ bool _overwriteOldest = false;
+ bool _manualAddContacts = false;
+ int _telemetryModeBase = 0;
+ int _telemetryModeLoc = 0;
+ int _telemetryModeEnv = 0;
+ int _advertLocPolicy = 0;
+ int _multiAcks = 0;
+
static const int _defaultMaxContacts = 32;
static const int _defaultMaxChannels = 8;
int _maxContacts = _defaultMaxContacts;
@@ -110,6 +247,9 @@ class MeshCoreConnector extends ChangeNotifier {
int _queueSyncRetries = 0;
static const int _maxQueueSyncRetries = 3;
static const int _queueSyncTimeoutMs = 5000; // 5 second timeout
+ // Serializes path operations (setContactPath/clearContactPath) to prevent
+ // interleaved async calls from leaving in-memory state inconsistent with device.
+ Future _pathOpLock = Future.value();
Map? _currentCustomVars;
// Channel syncing state (sequential pattern)
@@ -122,7 +262,7 @@ class MeshCoreConnector extends ChangeNotifier {
List _previousChannelsCache = [];
static const int _maxChannelSyncRetries = 3;
static const int _channelSyncTimeoutMs = 2000; // 2 second timeout per channel
- static const Duration _batteryPollInterval = Duration(seconds: 30);
+ static const Duration _batteryPollInterval = Duration(seconds: 120);
// Services
MessageRetryService? _retryService;
@@ -138,25 +278,45 @@ class MeshCoreConnector extends ChangeNotifier {
final ChannelSettingsStore _channelSettingsStore = ChannelSettingsStore();
final ContactSettingsStore _contactSettingsStore = ContactSettingsStore();
final ContactStore _contactStore = ContactStore();
+ final ContactDiscoveryStore _discoveryContactStore = ContactDiscoveryStore();
+ final ChannelStore _channelStore = ChannelStore();
final UnreadStore _unreadStore = UnreadStore();
+ List _cachedChannels = [];
final Map _channelSmazEnabled = {};
bool _lastSentWasCliCommand =
false; // Track if last sent message was a CLI command
final Map _contactSmazEnabled = {};
final Set _knownContactKeys = {};
- final Map _contactLastReadMs = {};
- final Map _channelLastReadMs = {};
+ final Map _contactUnreadCount = {};
+ final Map _repeaterBatterySnapshots = {};
+ bool _unreadStateLoaded = false;
final Map _pendingRepeaterAcks = {};
String? _activeContactKey;
int? _activeChannelIndex;
List _channelOrder = [];
+ int _storageUsedKb = -1;
+ int _storageTotalKb = -1;
+
// Getters
MeshCoreConnectionState get state => _state;
BluetoothDevice? get device => _device;
String? get deviceId => _deviceId;
String get deviceIdLabel => _deviceId ?? 'Unknown';
+ MeshCoreTransportType get activeTransport => _activeTransport;
+ String? get activeUsbPort => _usbManager.activePortKey;
+ String? get activeUsbPortDisplayLabel => _usbManager.activePortDisplayLabel;
+ bool get isUsbTransportConnected =>
+ _state == MeshCoreConnectionState.connected &&
+ _activeTransport == MeshCoreTransportType.usb;
+ bool get isAutoReconnectScheduled =>
+ _shouldAutoReconnect && (_reconnectTimer?.isActive ?? false);
+ String? get activeTcpEndpoint => _tcpConnector.activeEndpoint;
+ bool get isTcpTransportConnected =>
+ _state == MeshCoreConnectionState.connected &&
+ _activeTransport == MeshCoreTransportType.tcp;
+
String get deviceDisplayName {
if (_selfName != null && _selfName!.isNotEmpty) {
return _selfName!;
@@ -182,25 +342,75 @@ class MeshCoreConnector extends ChangeNotifier {
);
}
+ List get allContacts => List.unmodifiable([
+ ..._contacts,
+ ..._discoveredContacts.where(
+ (c) => !c.isActive && c.publicKeyHex != selfPublicKeyHex,
+ ),
+ ]);
+
+ List get allContactsUnfiltered =>
+ List.unmodifiable([..._contacts, ..._discoveredContacts]);
+
+ List get discoveredContacts {
+ return List.unmodifiable(_discoveredContacts);
+ }
+
List get channels => List.unmodifiable(_channels);
bool get isConnected => _state == MeshCoreConnectionState.connected;
bool get isLoadingContacts => _isLoadingContacts;
bool get isLoadingChannels => _isLoadingChannels;
Stream get receivedFrames => _receivedFramesController.stream;
Uint8List? get selfPublicKey => _selfPublicKey;
+ String get selfPublicKeyHex => pubKeyToHex(_selfPublicKey ?? Uint8List(0));
String? get selfName => _selfName;
double? get selfLatitude => _selfLatitude;
double? get selfLongitude => _selfLongitude;
+ List get directRepeaters => _directRepeaters;
int? get currentTxPower => _currentTxPower;
int? get maxTxPower => _maxTxPower;
+
+ int get pathHashByteWidth => _pathHashByteWidth;
+
+ CompanionRadioStats? get latestRadioStats => _latestRadioStats;
+
+ bool get supportsCompanionRadioStats => (_firmwareVerCode ?? 0) >= 8;
+
+ bool get radioStatsAirActivityPulse {
+ final sw = _airtimeBumpStopwatch;
+ if (sw == null || !sw.isRunning) return false;
+ return sw.elapsed < const Duration(seconds: 2);
+ }
+
int? get currentFreqHz => _currentFreqHz;
int? get currentBwHz => _currentBwHz;
int? get currentSf => _currentSf;
int? get currentCr => _currentCr;
+ MeshCoreRadioStateSnapshot? get rememberedNonRepeatRadioState =>
+ _rememberedNonRepeatRadioState;
+ bool? get autoAddUsers => _autoAddUsers;
+ bool? get autoAddRepeaters => _autoAddRepeaters;
+ bool? get autoAddRoomServers => _autoAddRoomServers;
+ bool? get autoAddSensors => _autoAddSensors;
+ bool? get autoAddOverwriteOldest => _overwriteOldest;
+ int get telemetryModeBase => _telemetryModeBase;
+ int get telemetryModeLoc => _telemetryModeLoc;
+ int get telemetryModeEnv => _telemetryModeEnv;
+ int get advertLocationPolicy => _advertLocPolicy;
+ int get multiAcks => _multiAcks;
+ bool? get clientRepeat => _clientRepeat;
+ void rememberNonRepeatRadioState(MeshCoreRadioStateSnapshot snapshot) {
+ _rememberedNonRepeatRadioState = snapshot;
+ }
+
+ 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);
bool get isSyncingQueuedMessages => _isSyncingQueuedMessages;
bool get isSyncingChannels => _isSyncingChannels;
int get channelSyncProgress =>
@@ -209,10 +419,32 @@ class MeshCoreConnector extends ChangeNotifier {
: 0;
int? get batteryPercent => _batteryMillivolts == null
? null
- : _estimateBatteryPercent(
+ : estimateBatteryPercentFromMillivolts(
_batteryMillivolts!,
_batteryChemistryForDevice(),
);
+ RepeaterBatterySnapshot? getRepeaterBatterySnapshot(String contactKeyHex) =>
+ _repeaterBatterySnapshots[contactKeyHex];
+ int? getRepeaterBatteryMillivolts(String contactKeyHex) =>
+ _repeaterBatterySnapshots[contactKeyHex]?.millivolts;
+
+ void updateRepeaterBatterySnapshot(
+ String contactKeyHex,
+ int millivolts, {
+ String source = 'unknown',
+ }) {
+ if (contactKeyHex.isEmpty || millivolts <= 0) return;
+ final previous = _repeaterBatterySnapshots[contactKeyHex];
+ final snapshot = RepeaterBatterySnapshot(
+ millivolts: millivolts,
+ updatedAt: DateTime.now(),
+ source: source,
+ );
+ _repeaterBatterySnapshots[contactKeyHex] = snapshot;
+ if (previous?.millivolts != millivolts) {
+ notifyListeners();
+ }
+ }
String _batteryChemistryForDevice() {
final deviceId = _device?.remoteId.toString();
@@ -220,27 +452,6 @@ class MeshCoreConnector extends ChangeNotifier {
return _appSettingsService!.batteryChemistryForDevice(deviceId);
}
- int _estimateBatteryPercent(int millivolts, String chemistry) {
- final range = _batteryVoltageRange(chemistry);
- final minMv = range.$1;
- final maxMv = range.$2;
- if (millivolts <= minMv) return 0;
- if (millivolts >= maxMv) return 100;
- return (((millivolts - minMv) * 100) / (maxMv - minMv)).round();
- }
-
- (int, int) _batteryVoltageRange(String chemistry) {
- switch (chemistry) {
- case 'lifepo4':
- return (2600, 3650);
- case 'lipo':
- return (3000, 4200);
- case 'nmc':
- default:
- return (3000, 4200);
- }
- }
-
List getMessages(Contact contact) {
return _conversations[contact.publicKeyHex] ?? [];
}
@@ -266,11 +477,50 @@ class MeshCoreConnector extends ChangeNotifier {
? allMessages.sublist(allMessages.length - _messageWindowSize)
: allMessages;
- _conversations[contactKeyHex] = windowedMessages;
+ final currentMessages =
+ _conversations[contactKeyHex] ?? const [];
+ final mergedMessages = [...windowedMessages];
+ final persistedKeyCounts = {};
+ for (final message in windowedMessages) {
+ final key = _messageMergeKey(message);
+ persistedKeyCounts[key] = (persistedKeyCounts[key] ?? 0) + 1;
+ }
+ final currentKeyCounts = {};
+
+ for (final message in currentMessages) {
+ final key = _messageMergeKey(message);
+ final currentCount = (currentKeyCounts[key] ?? 0) + 1;
+ currentKeyCounts[key] = currentCount;
+ final persistedCount = persistedKeyCounts[key] ?? 0;
+
+ // Preserve distinct duplicates without IDs (for example same text
+ // received multiple times in the same second) by only skipping the
+ // overlapping occurrences that already exist in persisted storage.
+ if (currentCount > persistedCount) {
+ mergedMessages.add(message);
+ }
+ }
+
+ // Re-sort after merging persisted and in-memory messages so the
+ // conversation window remains stable after optimistic inserts.
+ mergedMessages.sort((a, b) => a.timestamp.compareTo(b.timestamp));
+ final windowedMergedMessages = mergedMessages.length > _messageWindowSize
+ ? mergedMessages.sublist(mergedMessages.length - _messageWindowSize)
+ : mergedMessages;
+
+ _conversations[contactKeyHex] = windowedMergedMessages;
notifyListeners();
}
}
+ String _messageMergeKey(Message message) {
+ final messageId = message.messageId;
+ if (messageId.isNotEmpty) {
+ return 'id:$messageId';
+ }
+ return 'fallback:${message.senderKeyHex}:${message.isOutgoing}:${message.isCli}:${message.timestamp.millisecondsSinceEpoch}:${message.text}';
+ }
+
/// Load older messages for a contact (pagination)
Future> loadOlderMessages(
String contactKeyHex, {
@@ -317,18 +567,9 @@ class MeshCoreConnector extends ChangeNotifier {
}
int getUnreadCountForContactKey(String contactKeyHex) {
+ if (!_unreadStateLoaded) return 0;
if (!_shouldTrackUnreadForContactKey(contactKeyHex)) return 0;
- final messages = _conversations[contactKeyHex];
- if (messages == null || messages.isEmpty) return 0;
- final lastReadMs = _contactLastReadMs[contactKeyHex] ?? 0;
- var count = 0;
- for (final message in messages) {
- if (message.isOutgoing || message.isCli) continue;
- if (message.timestamp.millisecondsSinceEpoch > lastReadMs) {
- count++;
- }
- }
- return count;
+ return _contactUnreadCount[contactKeyHex] ?? 0;
}
int getUnreadCountForChannel(Channel channel) {
@@ -336,20 +577,12 @@ class MeshCoreConnector extends ChangeNotifier {
}
int getUnreadCountForChannelIndex(int channelIndex) {
- final messages = _channelMessages[channelIndex];
- if (messages == null || messages.isEmpty) return 0;
- final lastReadMs = _channelLastReadMs[channelIndex] ?? 0;
- var count = 0;
- for (final message in messages) {
- if (message.isOutgoing) continue;
- if (message.timestamp.millisecondsSinceEpoch > lastReadMs) {
- count++;
- }
- }
- return count;
+ if (!_unreadStateLoaded) return 0;
+ return _findChannelByIndex(channelIndex)?.unreadCount ?? 0;
}
int getTotalUnreadCount() {
+ if (!_unreadStateLoaded) return 0;
var total = 0;
// Count unread contact messages
for (final contact in _contacts) {
@@ -375,15 +608,17 @@ class MeshCoreConnector extends ChangeNotifier {
}
Future loadUnreadState() async {
- _contactLastReadMs
+ _contactUnreadCount
..clear()
- ..addAll(await _unreadStore.loadContactLastRead());
- _channelLastReadMs
- ..clear()
- ..addAll(await _unreadStore.loadChannelLastRead());
+ ..addAll(await _unreadStore.loadContactUnreadCount());
+ _unreadStateLoaded = true;
notifyListeners();
}
+ Future loadCachedChannels() async {
+ _cachedChannels = await _channelStore.loadChannels();
+ }
+
void setActiveContact(String? contactKeyHex) {
if (contactKeyHex != null &&
!_shouldTrackUnreadForContactKey(contactKeyHex)) {
@@ -405,17 +640,44 @@ class MeshCoreConnector extends ChangeNotifier {
void markContactRead(String contactKeyHex) {
if (!_shouldTrackUnreadForContactKey(contactKeyHex)) return;
- final markMs = _calculateReadTimestampMs(
- _conversations[contactKeyHex]?.map((m) => m.timestamp),
- );
- _setContactLastReadMs(contactKeyHex, markMs);
+ final previousCount = _contactUnreadCount[contactKeyHex] ?? 0;
+ if (previousCount > 0) {
+ _contactUnreadCount[contactKeyHex] = 0;
+ _appDebugLogService?.info(
+ 'Contact $contactKeyHex marked as read (was $previousCount unread)',
+ tag: 'Unread',
+ );
+ _unreadStore.saveContactUnreadCount(
+ Map.from(_contactUnreadCount),
+ );
+ _notificationService.clearContactNotification(
+ contactKeyHex,
+ getTotalUnreadCount(),
+ );
+ notifyListeners();
+ }
}
void markChannelRead(int channelIndex) {
- final markMs = _calculateReadTimestampMs(
- _channelMessages[channelIndex]?.map((m) => m.timestamp),
- );
- _setChannelLastReadMs(channelIndex, markMs);
+ final channel = _findChannelByIndex(channelIndex);
+ if (channel != null && channel.unreadCount > 0) {
+ final previousCount = channel.unreadCount;
+ channel.unreadCount = 0;
+ _appDebugLogService?.info(
+ 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} marked as read (was $previousCount unread)',
+ tag: 'Unread',
+ );
+ unawaited(
+ _channelStore.saveChannels(
+ _channels.isNotEmpty ? _channels : _cachedChannels,
+ ),
+ );
+ _notificationService.clearChannelNotification(
+ channelIndex,
+ getTotalUnreadCount(),
+ );
+ notifyListeners();
+ }
}
Future setChannelSmazEnabled(int channelIndex, bool enabled) async {
@@ -492,16 +754,22 @@ class MeshCoreConnector extends ChangeNotifier {
required MessageRetryService retryService,
required PathHistoryService pathHistoryService,
AppSettingsService? appSettingsService,
+ TranslationService? translationService,
BleDebugLogService? bleDebugLogService,
AppDebugLogService? appDebugLogService,
BackgroundService? backgroundService,
+ TimeoutPredictionService? timeoutPredictionService,
}) {
_retryService = retryService;
_pathHistoryService = pathHistoryService;
_appSettingsService = appSettingsService;
+ _translationService = translationService;
_bleDebugLogService = bleDebugLogService;
_appDebugLogService = appDebugLogService;
_backgroundService = backgroundService;
+ _timeoutPredictionService = timeoutPredictionService;
+ _usbManager.setDebugLogService(_appDebugLogService);
+ _tcpConnector.setDebugLogService(_appDebugLogService);
// Initialize notification service
_notificationService.initialize();
@@ -509,19 +777,45 @@ class MeshCoreConnector extends ChangeNotifier {
// Initialize retry service callbacks
_retryService?.initialize(
- sendMessageCallback: _sendMessageDirect,
- addMessageCallback: _addMessage,
- updateMessageCallback: _updateMessage,
- clearContactPathCallback: clearContactPath,
- setContactPathCallback: setContactPath,
- calculateTimeoutCallback: (pathLength, messageBytes) =>
- calculateTimeout(pathLength: pathLength, messageBytes: messageBytes),
- getSelfPublicKeyCallback: () => _selfPublicKey,
- prepareContactOutboundTextCallback: prepareContactOutboundText,
- appSettingsService: appSettingsService,
- debugLogService: _appDebugLogService,
- recordPathResultCallback: _recordPathResult,
+ RetryServiceConfig(
+ sendMessage: _sendMessageDirect,
+ addMessage: _addMessage,
+ updateMessage: _updateMessage,
+ clearContactPath: clearContactPath,
+ setContactPath: setContactPath,
+ calculateTimeout: (pathLength, messageBytes, {String? contactKey}) =>
+ calculateTimeout(
+ pathLength: pathLength,
+ messageBytes: messageBytes,
+ contactKey: contactKey,
+ ),
+ getSelfPublicKey: () => _selfPublicKey,
+ prepareContactOutboundText: prepareContactOutboundText,
+ appSettingsService: appSettingsService,
+ debugLogService: _appDebugLogService,
+ recordPathResult: _recordPathResult,
+ selectRetryPath:
+ (contactKey, attemptIndex, maxRetries, recentSelections) =>
+ _selectAutoPathForAttempt(
+ contactKey,
+ attemptIndex: attemptIndex,
+ maxRetries: maxRetries,
+ recentSelections: recentSelections,
+ ),
+ onDeliveryObserved: (contactKey, pathLength, messageBytes, tripTimeMs) {
+ final secSinceRx = DateTime.now().difference(_lastRxTime).inSeconds;
+ _timeoutPredictionService?.recordObservation(
+ contactKey: contactKey,
+ pathLength: pathLength,
+ messageBytes: messageBytes,
+ tripTimeMs: tripTimeMs,
+ secondsSinceLastRx: secSinceRx,
+ );
+ },
+ ),
);
+ final maxRetries = _appSettingsService?.settings.maxMessageRetries ?? 5;
+ _retryService?.setMaxRetries(maxRetries);
}
Future loadContactCache() async {
@@ -529,11 +823,21 @@ class MeshCoreConnector extends ChangeNotifier {
_knownContactKeys
..clear()
..addAll(cached.map((c) => c.publicKeyHex));
+ _contacts
+ ..clear()
+ ..addAll(cached);
for (final contact in cached) {
_ensureContactSmazSettingLoaded(contact.publicKeyHex);
}
}
+ Future _loadDiscoveredContactCache() async {
+ final cached = await _discoveryContactStore.loadContacts();
+ _discoveredContacts
+ ..clear()
+ ..addAll(cached);
+ }
+
Future loadChannelSettings({int? maxChannels}) async {
_channelSmazEnabled.clear();
final channelCount = maxChannels ?? _maxChannels;
@@ -542,22 +846,116 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
- void _sendMessageDirect(
+ /// After an incoming DM or channel message, wait before TX so we do not
+ /// collide with mesh propagation. With companion stats, scale wait by RF
+ /// conditions (up to [_contactMsgBackoffMaxMs]); otherwise use
+ /// [_contactMsgBackoffFallbackMs].
+ int _contactMessageBackoffTargetMs() {
+ if (!supportsCompanionRadioStats || _latestRadioStats == null) {
+ return _contactMsgBackoffFallbackMs;
+ }
+ final stats = _latestRadioStats!;
+ final nf = stats.noiseFloorDbm.toDouble();
+ // Quieter (more negative) → lower score; noisier → higher.
+ const noiseQuietDbm = -118.0;
+ const noiseNoisyDbm = -88.0;
+ final noiseT = ((nf - noiseQuietDbm) / (noiseNoisyDbm - noiseQuietDbm))
+ .clamp(0.0, 1.0);
+
+ final snr = stats.lastSnrDb;
+ const snrGood = 12.0;
+ const snrBad = -2.0;
+ final snrT = (1.0 - ((snr - snrBad) / (snrGood - snrBad))).clamp(0.0, 1.0);
+
+ final airBusy = _recentAirtimeBusyFraction();
+ final severity = (math.max(noiseT, snrT) * 0.82 + airBusy * 0.18).clamp(
+ 0.0,
+ 1.0,
+ );
+
+ return (_contactMsgBackoffMinMs +
+ severity * (_contactMsgBackoffMaxMs - _contactMsgBackoffMinMs))
+ .round();
+ }
+
+ /// 1.0 shortly after TX/RX airtime counters increase, decaying to 0 over ~8s.
+ double _recentAirtimeBusyFraction() {
+ final sw = _airtimeBumpStopwatch;
+ if (sw == null || !sw.isRunning) return 0;
+ final ms = sw.elapsedMilliseconds;
+ const windowMs = 8000;
+ if (ms >= windowMs) return 0;
+ return 1.0 - (ms / windowMs);
+ }
+
+ /// Start of the post-inbound cool-down: the later of BLE message RX time and
+ /// companion airtime bump ([_airtimeBumpStopwatch], same as the activity dot).
+ DateTime _postTxBackoffAnchor(DateTime lastInboundRxTime) {
+ if (!supportsCompanionRadioStats) return lastInboundRxTime;
+ final sw = _airtimeBumpStopwatch;
+ if (sw == null || !sw.isRunning) return lastInboundRxTime;
+ final bumpAt = DateTime.now().subtract(sw.elapsed);
+ return bumpAt.isAfter(lastInboundRxTime) ? bumpAt : lastInboundRxTime;
+ }
+
+ Future _waitForRadioQuiet({required DateTime lastInboundRxTime}) async {
+ // Wait for backoff after inbound traffic / RF airtime (avoid collision with
+ // mesh propagation). Elapsed time uses the dot's airtime bump when newer.
+ final backoffTargetMs = _contactMessageBackoffTargetMs();
+ final anchor = _postTxBackoffAnchor(lastInboundRxTime);
+ final msSinceAnchor = DateTime.now().difference(anchor).inMilliseconds;
+ if (msSinceAnchor < backoffTargetMs) {
+ final waitMs = backoffTargetMs - msSinceAnchor;
+ debugPrint(
+ 'Post-inbound backoff: waiting ${waitMs}ms '
+ '(target=${backoffTargetMs}ms, anchorAge=${msSinceAnchor}ms)',
+ );
+ await Future.delayed(Duration(milliseconds: waitMs));
+ }
+
+ // Then wait for radio silence (no RF activity for 3s)
+ final msSinceRx = DateTime.now()
+ .difference(_lastRadioRxTime)
+ .inMilliseconds;
+ if (msSinceRx >= _radioQuietMs) return;
+
+ final deadline = DateTime.now().add(
+ const Duration(milliseconds: _radioQuietMaxWaitMs),
+ );
+ while (DateTime.now().isBefore(deadline)) {
+ final quiet = DateTime.now().difference(_lastRadioRxTime).inMilliseconds;
+ if (quiet >= _radioQuietMs) {
+ debugPrint('Radio quiet for ${quiet}ms, proceeding with send');
+ return;
+ }
+ await Future.delayed(const Duration(milliseconds: 200));
+ }
+ debugPrint(
+ 'Radio quiet wait exceeded ${_radioQuietMaxWaitMs}ms, sending anyway',
+ );
+ }
+
+ Future _sendMessageDirect(
Contact contact,
String text,
int attempt,
int timestampSeconds,
) async {
if (!isConnected || text.isEmpty) return;
- final outboundText = prepareContactOutboundText(contact, text);
- await sendFrame(
- buildSendTextMsgFrame(
- contact.publicKey,
- outboundText,
- attempt: attempt,
- timestampSeconds: timestampSeconds,
- ),
- );
+ try {
+ await _waitForRadioQuiet(lastInboundRxTime: _lastContactMsgRxTime);
+ final outboundText = prepareContactOutboundText(contact, text);
+ await sendFrame(
+ buildSendTextMsgFrame(
+ contact.publicKey,
+ outboundText,
+ attempt: attempt,
+ timestampSeconds: timestampSeconds,
+ ),
+ );
+ } catch (e) {
+ appLogger.error('Failed to send message: $e', tag: 'Connector');
+ }
}
void _updateMessage(Message message) {
@@ -573,6 +971,140 @@ class MeshCoreConnector extends ChangeNotifier {
notifyListeners();
}
}
+
+ // If this is a reaction message, update the target message's reaction status
+ final reactionInfo = ReactionHelper.parseReaction(message.text);
+ if (reactionInfo != null &&
+ (message.status == MessageStatus.delivered ||
+ message.status == MessageStatus.failed)) {
+ final contactKey2 = pubKeyToHex(message.senderKey);
+ _setReactionStatus(contactKey2, reactionInfo, message.status);
+ _messageStore.saveMessages(
+ contactKey2,
+ _conversations[contactKey2] ?? [],
+ );
+ notifyListeners();
+ }
+ }
+
+ Future _translateIncomingContactMessage(
+ String contactKeyHex,
+ Message message,
+ ) async {
+ try {
+ final service = _translationService;
+ if (service == null ||
+ !service.shouldTranslateIncoming(
+ text: message.text,
+ isCli: message.isCli,
+ isOutgoing: message.isOutgoing,
+ )) {
+ return;
+ }
+ final targetLanguageCode = service.resolvedIncomingLanguageCode(
+ _appSettingsService?.settings.languageOverride,
+ );
+ final result = await service.translateIncomingText(
+ text: message.text,
+ targetLanguageCode: targetLanguageCode,
+ );
+ if (result == null) {
+ return;
+ }
+ final translated = result.status == MessageTranslationStatus.completed
+ ? result.translatedText
+ : null;
+ _updateStoredContactMessage(
+ contactKeyHex,
+ message.messageId,
+ (current) => current.copyWith(
+ translatedText: translated,
+ translatedLanguageCode: result.detectedLanguageCode,
+ translationStatus: result.status,
+ translationModelId: result.modelId,
+ ),
+ );
+ } catch (error) {
+ appLogger.warn('Translation failed for contact message: $error');
+ }
+ }
+
+ Future _translateIncomingChannelMessage(
+ int channelIndex,
+ ChannelMessage message,
+ ) async {
+ try {
+ final service = _translationService;
+ if (service == null ||
+ !service.shouldTranslateIncoming(
+ text: message.text,
+ isCli: false,
+ isOutgoing: message.isOutgoing,
+ )) {
+ return;
+ }
+ final targetLanguageCode = service.resolvedIncomingLanguageCode(
+ _appSettingsService?.settings.languageOverride,
+ );
+ final result = await service.translateIncomingText(
+ text: message.text,
+ targetLanguageCode: targetLanguageCode,
+ );
+ if (result == null) {
+ return;
+ }
+ final translated = result.status == MessageTranslationStatus.completed
+ ? result.translatedText
+ : null;
+ _updateStoredChannelMessage(
+ channelIndex,
+ message.messageId,
+ (current) => current.copyWith(
+ translatedText: translated,
+ translatedLanguageCode: result.detectedLanguageCode,
+ translationStatus: result.status,
+ translationModelId: result.modelId,
+ ),
+ );
+ } catch (error) {
+ appLogger.warn('Translation failed for channel message: $error');
+ }
+ }
+
+ void _updateStoredContactMessage(
+ String contactKeyHex,
+ String messageId,
+ Message Function(Message current) update,
+ ) {
+ final messages = _conversations[contactKeyHex];
+ if (messages == null) {
+ return;
+ }
+ final index = messages.indexWhere((entry) => entry.messageId == messageId);
+ if (index < 0) {
+ return;
+ }
+ messages[index] = update(messages[index]);
+ _messageStore.saveMessages(contactKeyHex, messages);
+ notifyListeners();
+ }
+
+ void _updateStoredChannelMessage(
+ int channelIndex,
+ String messageId,
+ ChannelMessage Function(ChannelMessage current) update,
+ ) {
+ final messages = _channelMessages[channelIndex];
+ if (messages == null) {
+ return;
+ }
+ final index = messages.indexWhere((entry) => entry.messageId == messageId);
+ if (index < 0) {
+ return;
+ }
+ messages[index] = update(messages[index]);
+ _channelMessageStore.saveChannelMessages(channelIndex, messages);
+ notifyListeners();
}
void _recordPathResult(
@@ -582,34 +1114,69 @@ class MeshCoreConnector extends ChangeNotifier {
int? tripTimeMs,
) {
if (_pathHistoryService == null) return;
+ final settings = _appSettingsService?.settings;
_pathHistoryService!.recordPathResult(
contactPubKeyHex,
selection,
success: success,
tripTimeMs: tripTimeMs,
+ successIncrement: settings?.routeWeightSuccessIncrement ?? 0.2,
+ failureDecrement: settings?.routeWeightFailureDecrement ?? 0.2,
+ maxWeight: settings?.maxRouteWeight ?? 5.0,
);
+
+ // Flood path attribution: when a flood delivery succeeds, credit the
+ // contact's current device path so the route the ACK traveled back
+ // through gets a weight boost in the path history.
+ if (selection.useFlood && success) {
+ final contact = _contacts.cast().firstWhere(
+ (c) => c?.publicKeyHex == contactPubKeyHex,
+ orElse: () => null,
+ );
+ if (contact != null &&
+ contact.pathLength >= 0 &&
+ contact.path.isNotEmpty) {
+ _pathHistoryService!.recordFloodPathAttribution(
+ contactPubKeyHex: contactPubKeyHex,
+ pathBytes: contact.path,
+ hopCount: contact.pathLength,
+ tripTimeMs: tripTimeMs,
+ successIncrement: settings?.routeWeightSuccessIncrement ?? 0.2,
+ maxWeight: settings?.maxRouteWeight ?? 5.0,
+ );
+ }
+
+ // Request a fresh contact from the device so the next flood
+ // attribution uses the most up-to-date path.
+ if (contact != null) {
+ unawaited(getContactByKey(contact.publicKey));
+ }
+ }
}
- Contact _applyAutoSelection(Contact contact, PathSelection? selection) {
- if (selection == null ||
- selection.useFlood ||
- selection.pathBytes.isEmpty) {
- return contact;
+ PathSelection? _selectAutoPathForAttempt(
+ String contactPubKeyHex, {
+ required int attemptIndex,
+ required int maxRetries,
+ List recentSelections = const [],
+ }) {
+ final hasKnownPaths =
+ _pathHistoryService?.getRecentPaths(contactPubKeyHex).isNotEmpty ??
+ false;
+ if (!hasKnownPaths) {
+ return null;
}
- return Contact(
- publicKey: contact.publicKey,
- name: contact.name,
- type: contact.type,
- pathLength: selection.hopCount >= 0
- ? selection.hopCount
- : contact.pathLength,
- path: Uint8List.fromList(selection.pathBytes),
- latitude: contact.latitude,
- longitude: contact.longitude,
- lastSeen: contact.lastSeen,
- lastMessageAt: contact.lastMessageAt,
+ final selection = _pathHistoryService?.selectPathForAttempt(
+ contactPubKeyHex,
+ attemptIndex: attemptIndex,
+ maxRetries: maxRetries,
+ recentSelections: recentSelections,
);
+ if (selection != null) {
+ _pathHistoryService?.recordPathAttempt(contactPubKeyHex, selection);
+ }
+ return selection;
}
Future startScan({
@@ -618,30 +1185,146 @@ class MeshCoreConnector extends ChangeNotifier {
if (_state == MeshCoreConnectionState.scanning) return;
_scanResults.clear();
+ _linuxSystemScanResults.clear();
_setState(MeshCoreConnectionState.scanning);
- _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
- _scanResults.clear();
- for (var result in results) {
- if (result.device.platformName.startsWith("MeshCore-") ||
- result.advertisementData.advName.startsWith("MeshCore-")) {
- _scanResults.add(result);
- }
+ // Ensure any previous scan is fully stopped. Guard with isScanningNow to
+ // avoid triggering stale native callbacks when no scan is active.
+ if (FlutterBluePlus.isScanningNow) {
+ try {
+ await FlutterBluePlus.stopScan();
+ } catch (e) {
+ _appDebugLogService?.warn(
+ 'stopScan error in startScan (ignored): $e',
+ tag: 'BLE Scan',
+ );
}
+ }
+ await _scanSubscription?.cancel();
+
+ // On iOS/macOS, wait for Bluetooth to be powered on before scanning
+ if (defaultTargetPlatform == TargetPlatform.iOS ||
+ defaultTargetPlatform == TargetPlatform.macOS) {
+ // Wait for adapter state to be powered on
+ final adapterState = await FlutterBluePlus.adapterState.first;
+ if (adapterState != BluetoothAdapterState.on) {
+ // Wait for the adapter to turn on, with timeout
+ await FlutterBluePlus.adapterState
+ .firstWhere((state) => state == BluetoothAdapterState.on)
+ .timeout(
+ const Duration(seconds: 5),
+ onTimeout: () {
+ _setState(MeshCoreConnectionState.disconnected);
+ throw Exception('Bluetooth adapter not available');
+ },
+ );
+ }
+
+ // Add a small delay to allow BLE stack to fully initialize
+ await Future.delayed(const Duration(milliseconds: 300));
+ }
+
+ if (PlatformInfo.isLinux) {
+ await _loadLinuxSystemDevicesForScan();
+ }
+
+ _scanSubscription = FlutterBluePlus.scanResults.listen((results) {
+ _scanResults
+ ..clear()
+ ..addAll(results);
+ _mergeLinuxSystemScanResults();
notifyListeners();
});
- await FlutterBluePlus.startScan(
- timeout: timeout,
- androidScanMode: AndroidScanMode.lowLatency,
- );
+ try {
+ await FlutterBluePlus.startScan(
+ withKeywords: MeshCoreUuids.deviceNamePrefixes,
+ webOptionalServices: [Guid(MeshCoreUuids.service)],
+ timeout: timeout,
+ androidScanMode: AndroidScanMode.lowLatency,
+ );
+ } catch (error) {
+ _appDebugLogService?.warn('Scan/picker failure: $error', tag: 'BLE Scan');
+ _setState(MeshCoreConnectionState.disconnected);
+ rethrow;
+ }
await Future.delayed(timeout);
await stopScan();
}
+ Future _loadLinuxSystemDevicesForScan() async {
+ try {
+ final systemDevices = await FlutterBluePlus.systemDevices([
+ Guid(MeshCoreUuids.service),
+ ]);
+ _linuxSystemScanResults
+ ..clear()
+ ..addAll(
+ systemDevices
+ .where(
+ (device) => MeshCoreUuids.deviceNamePrefixes.any(
+ device.platformName.startsWith,
+ ),
+ )
+ .map(
+ (device) => ScanResult(
+ device: device,
+ advertisementData: AdvertisementData(
+ advName: device.platformName,
+ txPowerLevel: null,
+ appearance: null,
+ connectable: true,
+ manufacturerData: const >{},
+ serviceData: const >{},
+ serviceUuids: [Guid(MeshCoreUuids.service)],
+ ),
+ rssi: 0,
+ timeStamp: DateTime.now(),
+ ),
+ ),
+ );
+ _mergeLinuxSystemScanResults();
+ notifyListeners();
+ } catch (error) {
+ _appDebugLogService?.warn(
+ 'Failed loading Linux paired/system BLE devices: $error',
+ tag: 'BLE Scan',
+ );
+ }
+ }
+
+ void _mergeLinuxSystemScanResults() {
+ if (!PlatformInfo.isLinux || _linuxSystemScanResults.isEmpty) {
+ return;
+ }
+ final existingIds = _scanResults
+ .map((result) => result.device.remoteId.str)
+ .toSet();
+ for (final result in _linuxSystemScanResults) {
+ if (existingIds.contains(result.device.remoteId.str)) {
+ continue;
+ }
+ _scanResults.add(result);
+ }
+ }
+
Future stopScan() async {
- await FlutterBluePlus.stopScan();
+ // Only call FlutterBluePlus.stopScan() when a scan is actually running.
+ // Calling it when idle triggers a native BLE completion callback even
+ // though no scan was started. After a hot restart Dart has already freed
+ // those callback handles, so the callback crashes with
+ // "Callback invoked after it has been deleted".
+ if (FlutterBluePlus.isScanningNow) {
+ try {
+ await FlutterBluePlus.stopScan();
+ } catch (e) {
+ _appDebugLogService?.warn(
+ 'stopScan error (ignored): $e',
+ tag: 'BLE Scan',
+ );
+ }
+ }
await _scanSubscription?.cancel();
_scanSubscription = null;
@@ -650,12 +1333,252 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
- Future connect(BluetoothDevice device, {String? displayName}) async {
+ Future> listUsbPorts() => _usbManager.listPorts();
+
+ void setUsbRequestPortLabel(String label) {
+ _usbManager.setRequestPortLabel(label);
+ }
+
+ void setUsbFallbackDeviceName(String label) {
+ _usbManager.setFallbackDeviceName(label);
+ }
+
+ Future connectUsb({
+ required String portName,
+ int baudRate = 115200,
+ }) async {
+ if (_state == MeshCoreConnectionState.connecting ||
+ _state == MeshCoreConnectionState.connected) {
+ _appDebugLogService?.warn(
+ 'connectUsb ignored: already $_state',
+ tag: 'USB',
+ );
+ return;
+ }
+
+ _appDebugLogService?.info(
+ 'connectUsb: port=$portName baud=$baudRate',
+ tag: 'USB',
+ );
+
+ await stopScan();
+ _cancelReconnectTimer();
+ _manualDisconnect = false;
+ _resetConnectionHandshakeState();
+ _activeTransport = MeshCoreTransportType.usb;
+ _setState(MeshCoreConnectionState.connecting);
+
+ try {
+ await _usbFrameSubscription?.cancel();
+ _usbFrameSubscription = null;
+ _appDebugLogService?.info('connectUsb: opening serial port…', tag: 'USB');
+ await _usbManager.connect(portName: portName, baudRate: baudRate);
+ _appDebugLogService?.info(
+ 'connectUsb: serial port opened, label=${_usbManager.activePortDisplayLabel}',
+ tag: 'USB',
+ );
+ notifyListeners();
+ if (PlatformInfo.isWeb) {
+ await stopScan();
+ }
+ await Future.delayed(const Duration(milliseconds: 200));
+ _usbFrameSubscription = _usbManager.frameStream.listen(
+ _handleFrame,
+ onError: (error, stackTrace) {
+ _appDebugLogService?.error('USB transport error: $error', tag: 'USB');
+ unawaited(disconnect(manual: false));
+ },
+ onDone: () {
+ _appDebugLogService?.warn('USB frame stream ended', tag: 'USB');
+ unawaited(disconnect(manual: false));
+ },
+ );
+
+ _setState(MeshCoreConnectionState.connected);
+ _pendingInitialChannelSync = true;
+ _appDebugLogService?.info(
+ 'connectUsb: requesting device info…',
+ tag: 'USB',
+ );
+ await _requestDeviceInfo();
+ _startBatteryPolling();
+ if (_radioStatsPollRefCount > 0) _startRadioStatsPolling();
+ var gotSelfInfo = await _waitForSelfInfo(
+ timeout: const Duration(seconds: 3),
+ );
+ if (!gotSelfInfo) {
+ _appDebugLogService?.warn(
+ 'connectUsb: SELF_INFO timeout, retrying…',
+ tag: 'USB',
+ );
+ await refreshDeviceInfo();
+ gotSelfInfo = await _waitForSelfInfo(
+ timeout: const Duration(seconds: 3),
+ );
+ }
+ if (!gotSelfInfo) {
+ throw StateError('Timed out waiting for SELF_INFO during connect');
+ }
+
+ _appDebugLogService?.info('connectUsb: syncing time…', tag: 'USB');
+ await syncTime();
+ _appDebugLogService?.info('connectUsb: complete', tag: 'USB');
+ } catch (error) {
+ _appDebugLogService?.error('USB connection error: $error', tag: 'USB');
+ await disconnect(manual: false);
+ rethrow;
+ }
+ }
+
+ Future connectTcp({required String host, required int port}) async {
+ if (_state == MeshCoreConnectionState.connecting ||
+ _state == MeshCoreConnectionState.connected) {
+ _appDebugLogService?.warn(
+ 'connectTcp ignored: already $_state',
+ tag: 'TCP',
+ );
+ return;
+ }
+
+ _appDebugLogService?.info('connectTcp: endpoint=$host:$port', tag: 'TCP');
+
+ await stopScan();
+ _cancelReconnectTimer();
+ _manualDisconnect = false;
+ _resetConnectionHandshakeState();
+ _activeTransport = MeshCoreTransportType.tcp;
+ _setState(MeshCoreConnectionState.connecting);
+
+ try {
+ Future handleTcpConnectAbort({required String message}) async {
+ _appDebugLogService?.warn(message, tag: 'TCP');
+ final shouldResetState = shouldResetStateAfterTcpConnectAbort(
+ state: _state,
+ activeTransport: _activeTransport,
+ );
+ if (shouldResetState) {
+ await disconnect(manual: false);
+ return;
+ }
+ if (_tcpConnector.isConnected) {
+ await _tcpConnector.disconnect();
+ }
+ }
+
+ await _tcpConnector.cancelFrameSubscription();
+ await _tcpConnector.connect(host: host, port: port);
+ final isTcpConnectCancelled =
+ _activeTransport != MeshCoreTransportType.tcp ||
+ _state != MeshCoreConnectionState.connecting ||
+ !_tcpConnector.isConnected;
+ if (isTcpConnectCancelled) {
+ await handleTcpConnectAbort(
+ message:
+ 'connectTcp aborted before handshake: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}',
+ );
+ return;
+ }
+ notifyListeners();
+
+ await Future.delayed(const Duration(milliseconds: 200));
+ final isTcpConnectCancelledAfterDelay =
+ _activeTransport != MeshCoreTransportType.tcp ||
+ _state != MeshCoreConnectionState.connecting ||
+ !_tcpConnector.isConnected;
+ if (isTcpConnectCancelledAfterDelay) {
+ await handleTcpConnectAbort(
+ message:
+ 'connectTcp aborted after connect delay: state=$_state transport=$_activeTransport connected=${_tcpConnector.isConnected}',
+ );
+ return;
+ }
+ _tcpConnector.listenFrames(
+ onFrame: _handleFrame,
+ onError: (error, stackTrace) {
+ _appDebugLogService?.error('TCP transport error: $error', tag: 'TCP');
+ unawaited(disconnect(manual: false));
+ },
+ onDone: () {
+ _appDebugLogService?.warn('TCP frame stream ended', tag: 'TCP');
+ unawaited(disconnect(manual: false));
+ },
+ );
+
+ _setState(MeshCoreConnectionState.connected);
+ _pendingInitialChannelSync = true;
+ await _requestDeviceInfo();
+ _startBatteryPolling();
+ if (_radioStatsPollRefCount > 0) _startRadioStatsPolling();
+
+ var gotSelfInfo = await _waitForSelfInfo(
+ timeout: const Duration(seconds: 3),
+ );
+ if (!gotSelfInfo) {
+ await refreshDeviceInfo();
+ gotSelfInfo = await _waitForSelfInfo(
+ timeout: const Duration(seconds: 3),
+ );
+ }
+ if (!gotSelfInfo) {
+ throw StateError('Timed out waiting for SELF_INFO during TCP connect');
+ }
+
+ await syncTime();
+ } catch (error) {
+ _appDebugLogService?.error('TCP connection error: $error', tag: 'TCP');
+ final tcpConnectCancelledBeforeHandshake =
+ shouldIgnoreLateTcpConnectError(
+ manualDisconnect: _manualDisconnect,
+ state: _state,
+ activeTransport: _activeTransport,
+ tcpManagerConnected: _tcpConnector.isConnected,
+ );
+ if (tcpConnectCancelledBeforeHandshake) {
+ _appDebugLogService?.info(
+ 'Ignoring late TCP connect error after cancellation/switch: state=$_state transport=$_activeTransport',
+ tag: 'TCP',
+ );
+ return;
+ }
+ await disconnect(manual: false);
+ rethrow;
+ }
+ }
+
+ @visibleForTesting
+ static bool shouldIgnoreLateTcpConnectError({
+ required bool manualDisconnect,
+ required MeshCoreConnectionState state,
+ required MeshCoreTransportType activeTransport,
+ required bool tcpManagerConnected,
+ }) {
+ return manualDisconnect &&
+ (state == MeshCoreConnectionState.disconnected ||
+ state == MeshCoreConnectionState.disconnecting) &&
+ (activeTransport != MeshCoreTransportType.tcp || !tcpManagerConnected);
+ }
+
+ @visibleForTesting
+ static bool shouldResetStateAfterTcpConnectAbort({
+ required MeshCoreConnectionState state,
+ required MeshCoreTransportType activeTransport,
+ }) {
+ return state == MeshCoreConnectionState.connecting &&
+ activeTransport == MeshCoreTransportType.tcp;
+ }
+
+ Future connect(
+ BluetoothDevice device, {
+ String? displayName,
+ Future Function()? linuxPairingPinProvider,
+ }) async {
if (_state == MeshCoreConnectionState.connecting ||
_state == MeshCoreConnectionState.connected) {
return;
}
+ _activeTransport = MeshCoreTransportType.bluetooth;
+
await stopScan();
_setState(MeshCoreConnectionState.connecting);
_device = device;
@@ -670,31 +1593,215 @@ class MeshCoreConnector extends ChangeNotifier {
_lastDeviceDisplayName = _deviceDisplayName;
_manualDisconnect = false;
_cancelReconnectTimer();
+ _bleInitialSyncStarted = false;
+ if (PlatformInfo.isWeb) {
+ _resetConnectionHandshakeState();
+ }
unawaited(_backgroundService?.start());
notifyListeners();
try {
+ final connectLabel = _deviceDisplayName ?? _deviceId;
+ _appDebugLogService?.info(
+ 'Starting connect to $connectLabel',
+ tag: 'BLE Connect',
+ );
+ await _connectionSubscription?.cancel();
+ _connectionSubscription = null;
+ await _notifySubscription?.cancel();
+ _notifySubscription = null;
_connectionSubscription = device.connectionState.listen((state) {
- if (state == BluetoothConnectionState.disconnected) {
+ if (state == BluetoothConnectionState.disconnected && isConnected) {
_handleDisconnection();
}
});
- await device.connect(
- timeout: const Duration(seconds: 15),
- mtu: null,
- license: License.free,
- );
-
- // Request larger MTU for sending larger frames
- try {
- final mtu = await device.requestMtu(185);
- debugPrint('MTU set to: $mtu');
- } catch (e) {
- debugPrint('MTU request failed: $e, using default');
+ if (PlatformInfo.isLinux) {
+ final remoteId = device.remoteId.str;
+ _appDebugLogService?.info(
+ 'Linux pre-connect BlueZ disconnect for $remoteId',
+ tag: 'BLE Connect',
+ );
+ await _linuxBlePairingService.disconnectDevice(
+ remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ );
}
- List services = await device.discoverServices();
+ final connectTimeout = PlatformInfo.isLinux
+ ? const Duration(seconds: 6)
+ : const Duration(seconds: 15);
+ _appDebugLogService?.info(
+ 'device.connect timeout set to ${connectTimeout.inSeconds}s',
+ tag: 'BLE Connect',
+ );
+ if (PlatformInfo.isLinux) {
+ Future attemptConnect() {
+ return device
+ .connect(
+ timeout: connectTimeout,
+ mtu: null,
+ license: License.free,
+ )
+ .timeout(
+ connectTimeout + const Duration(seconds: 2),
+ onTimeout: () {
+ throw TimeoutException(
+ 'Linux connect hard-timeout after ${connectTimeout.inSeconds + 2}s',
+ );
+ },
+ );
+ }
+
+ try {
+ await attemptConnect();
+ } catch (error) {
+ _appDebugLogService?.error(
+ 'device.connect() failure: $error',
+ tag: 'BLE Connect',
+ );
+ final remoteId = device.remoteId.str;
+ _appDebugLogService?.warn(
+ 'Linux immediate retry: forcing BlueZ disconnect before second connect attempt',
+ tag: 'BLE Connect',
+ );
+ await _linuxBlePairingService.disconnectDevice(
+ remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ );
+ await Future.delayed(const Duration(milliseconds: 700));
+ try {
+ await attemptConnect();
+ _appDebugLogService?.info(
+ 'Linux immediate retry connect succeeded',
+ tag: 'BLE Connect',
+ );
+ } catch (retryError, retryStackTrace) {
+ Object finalConnectError = retryError;
+ StackTrace finalConnectStackTrace = retryStackTrace;
+ final retryErrorText = retryError.toString().toLowerCase();
+ final isAbortByLocal = retryErrorText.contains(
+ 'le-connection-abort-by-local',
+ );
+ var recoveredOnThirdAttempt = false;
+ if (isAbortByLocal) {
+ _appDebugLogService?.warn(
+ 'Linux immediate retry aborted by local stack; waiting and retrying once more',
+ tag: 'BLE Connect',
+ );
+ await Future.delayed(const Duration(milliseconds: 1200));
+ try {
+ await attemptConnect();
+ _appDebugLogService?.info(
+ 'Linux third-attempt connect succeeded after local abort',
+ tag: 'BLE Connect',
+ );
+ recoveredOnThirdAttempt = true;
+ } catch (thirdError, thirdStackTrace) {
+ finalConnectError = thirdError;
+ finalConnectStackTrace = thirdStackTrace;
+ _appDebugLogService?.error(
+ 'device.connect() third-attempt failure: $thirdError',
+ tag: 'BLE Connect',
+ );
+ }
+ }
+ if (!recoveredOnThirdAttempt) {
+ final recoveredByPairing = await _recoverLinuxConnectFailure(
+ device,
+ attemptConnect: attemptConnect,
+ onRequestPin: linuxPairingPinProvider,
+ );
+ if (recoveredByPairing) {
+ _appDebugLogService?.info(
+ 'Linux connect succeeded after pairing/trust recovery',
+ tag: 'BLE Connect',
+ );
+ } else {
+ _appDebugLogService?.error(
+ 'device.connect() retry failure: $finalConnectError',
+ tag: 'BLE Connect',
+ );
+ Error.throwWithStackTrace(
+ _wrapLinuxConnectStageError(finalConnectError),
+ finalConnectStackTrace,
+ );
+ }
+ }
+ }
+ }
+ } else {
+ try {
+ await device.connect(
+ timeout: connectTimeout,
+ mtu: null,
+ license: License.free,
+ );
+ } catch (error) {
+ _appDebugLogService?.error(
+ 'device.connect() failure: $error',
+ tag: 'BLE Connect',
+ );
+ rethrow;
+ }
+ }
+
+ if (PlatformInfo.isLinux) {
+ await _ensureLinuxBleBond(
+ device,
+ onRequestPin: linuxPairingPinProvider,
+ );
+ }
+
+ // Request larger MTU only where the platform path supports it.
+ if (!PlatformInfo.isWeb && !PlatformInfo.isLinux) {
+ try {
+ final mtu = await device.requestMtu(185);
+ _appDebugLogService?.info('MTU set to: $mtu', tag: 'BLE Connect');
+ } catch (e) {
+ _appDebugLogService?.warn(
+ 'MTU request failed: $e, using default',
+ tag: 'BLE Connect',
+ );
+ }
+ } else if (PlatformInfo.isLinux) {
+ _appDebugLogService?.info(
+ 'Skipping MTU request on Linux; flutter_blue_plus only supports requestMtu on Android',
+ tag: 'BLE Connect',
+ );
+ }
+
+ late final List services;
+ try {
+ services = await device.discoverServices();
+ } catch (error) {
+ _appDebugLogService?.error(
+ 'service discovery failure: $error',
+ tag: 'BLE Connect',
+ );
+ if (PlatformInfo.isWeb &&
+ error.toString().contains('GATT Server is disconnected')) {
+ // Chrome Web Bluetooth intermittently disconnects between connect()
+ // and service discovery; retry once to recover that transient state.
+ _appDebugLogService?.warn(
+ 'retrying service discovery after transient web disconnect',
+ tag: 'BLE Connect',
+ );
+ await Future.delayed(const Duration(milliseconds: 300));
+ await device.connect(
+ timeout: const Duration(seconds: 15),
+ mtu: null,
+ license: License.free,
+ );
+ services = await device.discoverServices();
+ } else {
+ rethrow;
+ }
+ }
BluetoothService? uartService;
for (var service in services) {
@@ -721,18 +1828,50 @@ class MeshCoreConnector extends ChangeNotifier {
throw Exception("MeshCore characteristics not found");
}
- // Retry setNotifyValue with increasing delays
- bool notifySet = false;
- for (int attempt = 0; attempt < 3 && !notifySet; attempt++) {
- try {
- if (attempt > 0) {
- await Future.delayed(Duration(milliseconds: 500 * attempt));
+ if (PlatformInfo.isWeb) {
+ _appDebugLogService?.info(
+ 'Starting setNotifyValue(true)',
+ tag: 'BLE Connect',
+ );
+ _appDebugLogService?.info(
+ 'Web: Calling setNotifyValue(true) without awaiting',
+ tag: 'BLE Connect',
+ );
+ unawaited(() async {
+ try {
+ await _txCharacteristic!.setNotifyValue(true);
+ } catch (error) {
+ _appDebugLogService?.warn(
+ 'notify failure (web, ignored): $error',
+ tag: 'BLE Connect',
+ );
+ _appDebugLogService?.warn(
+ 'Web setNotifyValue error (ignoring): $error',
+ tag: 'BLE Connect',
+ );
+ }
+ }());
+ _appDebugLogService?.info(
+ 'setNotifyValue(true) configuration completed',
+ tag: 'BLE Connect',
+ );
+ } else {
+ bool notifySet = false;
+ for (int attempt = 0; attempt < 3 && !notifySet; attempt++) {
+ try {
+ if (attempt > 0) {
+ await Future.delayed(Duration(milliseconds: 500 * attempt));
+ }
+ await _txCharacteristic!.setNotifyValue(true);
+ notifySet = true;
+ } catch (e) {
+ _appDebugLogService?.warn('notify failure: $e', tag: 'BLE Connect');
+ _appDebugLogService?.warn(
+ 'setNotifyValue attempt ${attempt + 1}/3 failed: $e',
+ tag: 'BLE Connect',
+ );
+ if (attempt == 2) rethrow;
}
- await _txCharacteristic!.setNotifyValue(true);
- notifySet = true;
- } catch (e) {
- debugPrint('setNotifyValue attempt ${attempt + 1}/3 failed: $e');
- if (attempt == 2) rethrow;
}
}
_notifySubscription = _txCharacteristic!.onValueReceived.listen(
@@ -740,29 +1879,229 @@ class MeshCoreConnector extends ChangeNotifier {
);
_setState(MeshCoreConnectionState.connected);
-
- // Enable wake lock to prevent BLE disconnection when screen turns off
- await WakelockPlus.enable();
-
- await _requestDeviceInfo();
- _startBatteryPolling();
- final gotSelfInfo = await _waitForSelfInfo(
- timeout: const Duration(seconds: 3),
- );
- if (!gotSelfInfo) {
- await refreshDeviceInfo();
- await _waitForSelfInfo(timeout: const Duration(seconds: 3));
+ if (_shouldGateInitialChannelSync) {
+ _hasReceivedDeviceInfo = false;
+ _pendingInitialChannelSync = true;
}
-
- // Keep device clock aligned on every connection.
- await syncTime();
+ await _startBleInitialSync();
} catch (e) {
- debugPrint("Connection error: $e");
- await disconnect(manual: false);
+ _appDebugLogService?.error('Connection error: $e', tag: 'BLE Connect');
+ final errorText = e.toString();
+ final lowerErrorText = errorText.toLowerCase();
+ final isLinuxPairingFailure =
+ PlatformInfo.isLinux && isLinuxBlePairingFailureText(errorText);
+ final isLikelyPairingTimeout = isLikelyLinuxBlePairingTimeoutText(
+ errorText,
+ );
+ final isConnectFailure = isLinuxBleConnectFailureText(errorText);
+ final isConnectTimeoutFailure =
+ isConnectFailure && lowerErrorText.contains('timed out');
+ final isLinuxConnectFailure = PlatformInfo.isLinux && isConnectFailure;
+ // Linux pairing failures should not enter auto-reconnect loops; user
+ // needs to retry manually so they can re-enter PIN / resolve pairing.
+ if (isLinuxPairingFailure) {
+ _appDebugLogService?.warn(
+ isLikelyPairingTimeout
+ ? 'Linux pairing timed out: stopping reconnect until user retries manually'
+ : 'Linux pairing failure: stopping reconnect until user retries manually',
+ tag: 'BLE Connect',
+ );
+ await disconnect(manual: true);
+ } else if (isLinuxConnectFailure) {
+ _appDebugLogService?.warn(
+ isConnectTimeoutFailure
+ ? 'Linux connect timeout: issuing BlueZ disconnect before reconnect'
+ : 'Linux connect failure: issuing BlueZ disconnect before reconnect',
+ tag: 'BLE Connect',
+ );
+ final remoteId = _device?.remoteId.str;
+ if (remoteId != null) {
+ await _linuxBlePairingService.disconnectDevice(
+ remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ );
+ }
+ await disconnect(manual: false, skipBleDeviceDisconnect: true);
+ } else {
+ await disconnect(manual: false);
+ }
rethrow;
}
}
+ Future _recoverLinuxConnectFailure(
+ BluetoothDevice device, {
+ required Future Function() attemptConnect,
+ Future Function()? onRequestPin,
+ }) async {
+ if (!PlatformInfo.isLinux ||
+ !await _linuxBlePairingService.isBluetoothctlAvailable()) {
+ return false;
+ }
+ final remoteId = device.remoteId.str;
+ final pluginBondState = await _getLinuxPluginBondState(device);
+ final trustedByBluez = await _linuxBlePairingService.isPairedAndTrusted(
+ remoteId,
+ );
+ final needsBondRecovery =
+ (pluginBondState != null &&
+ pluginBondState != BmBondStateEnum.bonded) ||
+ !trustedByBluez;
+ if (!needsBondRecovery) {
+ return false;
+ }
+ _appDebugLogService?.warn(
+ pluginBondState == BmBondStateEnum.bonded
+ ? 'Linux connect failed with an untrusted bond; attempting trust/pair recovery'
+ : 'Linux connect failed before bond completed; attempting pairing fallback',
+ tag: 'BLE Connect',
+ );
+ await _ensureLinuxBleBond(device, onRequestPin: onRequestPin);
+ _appDebugLogService?.info(
+ 'Resetting BlueZ connection after Linux pairing/trust recovery',
+ tag: 'BLE Connect',
+ );
+ await _linuxBlePairingService.disconnectDevice(
+ remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ );
+ await Future.delayed(const Duration(milliseconds: 700));
+ try {
+ await attemptConnect();
+ } catch (error, stackTrace) {
+ Error.throwWithStackTrace(_wrapLinuxConnectStageError(error), stackTrace);
+ }
+ return true;
+ }
+
+ Object _wrapLinuxConnectStageError(Object error) {
+ final errorText = error.toString();
+ if (errorText.toLowerCase().contains(linuxConnectStageFailureMarker)) {
+ return error;
+ }
+ return StateError('Linux connect stage failure: $error');
+ }
+
+ Future _getLinuxPluginBondState(
+ BluetoothDevice device,
+ ) async {
+ try {
+ final response = await FlutterBluePlusPlatform.instance.getBondState(
+ BmBondStateRequest(remoteId: device.remoteId),
+ );
+ return response.bondState;
+ } catch (error) {
+ _appDebugLogService?.warn(
+ 'Linux getBondState unavailable for ${device.remoteId.str}: $error',
+ tag: 'BLE Connect',
+ );
+ return null;
+ }
+ }
+
+ Future _ensureLinuxBleBond(
+ BluetoothDevice device, {
+ Future Function()? onRequestPin,
+ }) async {
+ final remoteId = device.remoteId.str;
+ final bluetoothctlAvailable = await _linuxBlePairingService
+ .isBluetoothctlAvailable();
+ final beforeBondState = await _getLinuxPluginBondState(device);
+ if (!bluetoothctlAvailable) {
+ if (beforeBondState == BmBondStateEnum.bonded) {
+ _appDebugLogService?.warn(
+ 'bluetoothctl unavailable; continuing with plugin bonded state',
+ tag: 'BLE Connect',
+ );
+ } else if (beforeBondState == null) {
+ _appDebugLogService?.warn(
+ 'bluetoothctl unavailable and plugin bond state is unknown; skipping Linux pairing fallback',
+ tag: 'BLE Connect',
+ );
+ } else {
+ _appDebugLogService?.warn(
+ 'bluetoothctl unavailable and device is not bonded; skipping Linux pairing fallback',
+ tag: 'BLE Connect',
+ );
+ }
+ return;
+ }
+
+ final trustedByBluez = await _linuxBlePairingService.isPairedAndTrusted(
+ remoteId,
+ );
+ if (trustedByBluez) {
+ _appDebugLogService?.info(
+ 'Linux BLE device already paired/trusted, skipping pairing flow',
+ tag: 'BLE Connect',
+ );
+ return;
+ }
+
+ if (beforeBondState == BmBondStateEnum.bonded && !trustedByBluez) {
+ _appDebugLogService?.warn(
+ 'Linux BLE device is bonded but not trusted in BlueZ; repairing trust',
+ tag: 'BLE Connect',
+ );
+ final trustRepaired = await _linuxBlePairingService.trustDevice(
+ remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ );
+ if (trustRepaired) {
+ _appDebugLogService?.info(
+ 'Linux BLE trust repair succeeded without re-pairing',
+ tag: 'BLE Connect',
+ );
+ return;
+ }
+ _appDebugLogService?.warn(
+ 'Linux BLE trust repair did not stick; retrying pairing flow',
+ tag: 'BLE Connect',
+ );
+ }
+
+ _appDebugLogService?.info(
+ beforeBondState == BmBondStateEnum.bonded
+ ? 'Linux BLE device still untrusted after repair; requesting pair'
+ : beforeBondState == null
+ ? 'Linux BLE device bond state unknown; requesting pair'
+ : 'Linux BLE device not bonded, requesting pair',
+ tag: 'BLE Connect',
+ );
+ final paired = await _linuxBlePairingService.pairAndTrust(
+ remoteId: remoteId,
+ onLog: (message) {
+ _appDebugLogService?.info(message, tag: 'BLE Pair');
+ },
+ onRequestPin: onRequestPin,
+ );
+ if (!paired) {
+ throw StateError('Linux pairing fallback failed');
+ }
+
+ final afterBondState = await _getLinuxPluginBondState(device);
+ if (afterBondState != null && afterBondState != BmBondStateEnum.bonded) {
+ throw StateError('Linux BLE pairing did not complete');
+ } else if (afterBondState == null) {
+ _appDebugLogService?.warn(
+ 'Linux plugin bond state unavailable after pairing; relying on BlueZ trust verification',
+ tag: 'BLE Connect',
+ );
+ }
+ final trustedAfter = await _linuxBlePairingService.isPairedAndTrusted(
+ remoteId,
+ );
+ if (!trustedAfter) {
+ throw StateError('Linux BLE trust repair did not complete');
+ }
+ }
+
Future _waitForSelfInfo({required Duration timeout}) async {
if (_selfPublicKey != null) return true;
if (!isConnected) return false;
@@ -794,7 +2133,57 @@ class MeshCoreConnector extends ChangeNotifier {
return result;
}
- bool get _shouldAutoReconnect => !_manualDisconnect && _lastDeviceId != null;
+ Future _startBleInitialSync() async {
+ if (_bleInitialSyncStarted ||
+ !isConnected ||
+ _activeTransport != MeshCoreTransportType.bluetooth) {
+ return;
+ }
+ _bleInitialSyncStarted = true;
+
+ await _requestDeviceInfo();
+ _startBatteryPolling();
+ if (_radioStatsPollRefCount > 0) _startRadioStatsPolling();
+
+ final gotSelfInfo = await _waitForSelfInfo(
+ timeout: const Duration(seconds: 3),
+ );
+ if (!gotSelfInfo) {
+ await refreshDeviceInfo();
+ await _waitForSelfInfo(timeout: const Duration(seconds: 3));
+ }
+
+ await syncTime();
+ unawaited(getChannels());
+ }
+
+ void _resetConnectionHandshakeState() {
+ _selfPublicKey = null;
+ _selfName = null;
+ _selfLatitude = null;
+ _selfLongitude = null;
+ _awaitingSelfInfo = false;
+ _webInitialHandshakeRequestSent = false;
+ _selfInfoRetryTimer?.cancel();
+ _selfInfoRetryTimer = null;
+ _hasReceivedDeviceInfo = false;
+ _pendingInitialChannelSync = false;
+ _pendingInitialContactsSync = false;
+ _bleInitialSyncStarted = false;
+ _pendingDeferredChannelSyncAfterContacts = false;
+ _pathHashByteWidth = 1;
+ }
+
+ bool get _shouldAutoReconnect =>
+ !_manualDisconnect &&
+ _lastDeviceId != null &&
+ _activeTransport == MeshCoreTransportType.bluetooth;
+
+ bool get _shouldGateInitialChannelSync =>
+ _activeTransport == MeshCoreTransportType.usb ||
+ _activeTransport == MeshCoreTransportType.tcp ||
+ (_activeTransport == MeshCoreTransportType.bluetooth &&
+ PlatformInfo.isWeb);
void _cancelReconnectTimer() {
_reconnectTimer?.cancel();
@@ -836,8 +2225,22 @@ class MeshCoreConnector extends ChangeNotifier {
});
}
- Future disconnect({bool manual = true}) async {
+ Future disconnect({
+ bool manual = true,
+ bool skipBleDeviceDisconnect = false,
+ }) async {
if (_state == MeshCoreConnectionState.disconnecting) return;
+ final transportAtDisconnect = _activeTransport;
+ final transportLabel = switch (transportAtDisconnect) {
+ MeshCoreTransportType.bluetooth => 'BLE',
+ MeshCoreTransportType.usb => 'USB',
+ MeshCoreTransportType.tcp => 'TCP',
+ };
+
+ _appDebugLogService?.info(
+ 'Starting disconnect transport=$transportLabel manual=$manual',
+ tag: 'Connection',
+ );
if (manual) {
_manualDisconnect = true;
@@ -848,9 +2251,12 @@ class MeshCoreConnector extends ChangeNotifier {
}
_setState(MeshCoreConnectionState.disconnecting);
_stopBatteryPolling();
+ _stopRadioStatsPolling();
- // Disable wake lock when disconnecting
- await WakelockPlus.disable();
+ await _usbFrameSubscription?.cancel();
+ _usbFrameSubscription = null;
+ await _usbManager.disconnect();
+ await _tcpConnector.disconnect();
await _notifySubscription?.cancel();
_notifySubscription = null;
@@ -865,12 +2271,20 @@ class MeshCoreConnector extends ChangeNotifier {
_channelSyncTimeout?.cancel();
_channelSyncTimeout = null;
_channelSyncRetries = 0;
+ await _translationService?.releaseModel();
- try {
- // Skip queued BLE operations so disconnect doesn't get stuck behind them.
- await _device?.disconnect(queue: false);
- } catch (e) {
- debugPrint("Disconnect error: $e");
+ if (!skipBleDeviceDisconnect) {
+ try {
+ // Skip queued BLE operations so disconnect doesn't get stuck behind them.
+ await _device?.disconnect(queue: false);
+ } catch (e) {
+ _appDebugLogService?.warn('Disconnect error: $e', tag: 'BLE Connect');
+ }
+ } else {
+ _appDebugLogService?.info(
+ 'Skipping plugin BLE disconnect and continuing cleanup',
+ tag: 'BLE Connect',
+ );
}
_device = null;
@@ -879,15 +2293,23 @@ class MeshCoreConnector extends ChangeNotifier {
_deviceDisplayName = null;
_deviceId = null;
_contacts.clear();
+ _discoveredContacts.clear();
_conversations.clear();
_loadedConversationKeys.clear();
_selfPublicKey = null;
_selfName = null;
_selfLatitude = null;
_selfLongitude = null;
+ _clientRepeat = null;
+ _rememberedNonRepeatRadioState = null;
+ _firmwareVerCode = null;
_batteryMillivolts = null;
+ _repeaterBatterySnapshots.clear();
_batteryRequested = false;
_awaitingSelfInfo = false;
+ _hasReceivedDeviceInfo = false;
+ _pendingInitialChannelSync = false;
+ _pendingInitialContactsSync = false;
_maxContacts = _defaultMaxContacts;
_maxChannels = _defaultMaxChannels;
_isSyncingQueuedMessages = false;
@@ -896,31 +2318,61 @@ class MeshCoreConnector extends ChangeNotifier {
_pendingQueueSync = false;
_isSyncingChannels = false;
_channelSyncInFlight = false;
+ _hasLoadedChannels = false;
+ _pendingChannelSentQueue.clear();
+ _pendingGenericAckQueue.clear();
+ _reactionSendQueueSequence = 0;
+
+ _activeTransport = MeshCoreTransportType.bluetooth;
_setState(MeshCoreConnectionState.disconnected);
- if (!manual) {
+ _appDebugLogService?.info(
+ 'Disconnect complete transport=$transportLabel manual=$manual',
+ tag: 'Connection',
+ );
+ if (!manual && transportAtDisconnect == MeshCoreTransportType.bluetooth) {
_scheduleReconnect();
}
}
- Future sendFrame(Uint8List data) async {
- if (!isConnected || _rxCharacteristic == null) {
+ Future sendFrame(
+ Uint8List data, {
+ String? channelSendQueueId,
+ bool expectsGenericAck = false,
+ }) async {
+ if (!isConnected) {
throw Exception("Not connected to a MeshCore device");
}
-
_bleDebugLogService?.logFrame(data, outgoing: true);
- // Prefer write without response when supported; fall back to write with response.
- final properties = _rxCharacteristic!.properties;
- final canWriteWithoutResponse = properties.writeWithoutResponse;
- final canWriteWithResponse = properties.write;
- if (!canWriteWithoutResponse && !canWriteWithResponse) {
- throw Exception("MeshCore RX characteristic does not support write");
+ if (_activeTransport == MeshCoreTransportType.usb) {
+ await _usbManager.write(data);
+ // Brief pause so the device firmware can process each frame before the
+ // next arrives. Without this, rapid-fire frames over USB can cause the
+ // device to miss responses (especially on reconnect).
+ await Future.delayed(const Duration(milliseconds: 10));
+ } else if (_activeTransport == MeshCoreTransportType.tcp) {
+ await _tcpConnector.write(data);
+ } else {
+ if (_rxCharacteristic == null) {
+ throw Exception("MeshCore RX characteristic not available");
+ }
+ // Prefer write without response when supported; fall back to write with response.
+ final properties = _rxCharacteristic!.properties;
+ final canWriteWithoutResponse = properties.writeWithoutResponse;
+ final canWriteWithResponse = properties.write;
+ if (!canWriteWithoutResponse && !canWriteWithResponse) {
+ throw Exception("MeshCore RX characteristic does not support write");
+ }
+ await _rxCharacteristic!.write(
+ data.toList(),
+ withoutResponse: canWriteWithoutResponse,
+ );
}
-
- await _rxCharacteristic!.write(
- data.toList(),
- withoutResponse: canWriteWithoutResponse,
+ _trackPendingGenericAck(
+ data,
+ channelSendQueueId: channelSendQueueId,
+ expectsGenericAck: expectsGenericAck,
);
}
@@ -947,29 +2399,127 @@ class MeshCoreConnector extends ChangeNotifier {
_batteryPollTimer = null;
}
+ void setPollingInterval(int i) {
+ _pollingInterval = i.clamp(1, 60);
+ if (isConnected) {
+ _startRadioStatsPolling();
+ }
+ }
+
+ void _startRadioStatsPolling() {
+ _radioStatsPollTimer?.cancel();
+ _radioStatsPollTimer = Timer.periodic(Duration(seconds: _pollingInterval), (
+ _,
+ ) {
+ if (!isConnected) {
+ _stopRadioStatsPolling();
+ return;
+ }
+ unawaited(requestRadioStats());
+ });
+ }
+
+ void _stopRadioStatsPolling() {
+ _radioStatsPollTimer?.cancel();
+ _radioStatsPollTimer = null;
+ }
+
+ void acquireRadioStatsPolling() {
+ _radioStatsPollRefCount++;
+ if (_radioStatsPollRefCount == 1 && isConnected) {
+ _startRadioStatsPolling();
+ }
+ }
+
+ void releaseRadioStatsPolling() {
+ _radioStatsPollRefCount = (_radioStatsPollRefCount - 1).clamp(0, 999);
+ if (_radioStatsPollRefCount == 0) {
+ _stopRadioStatsPolling();
+ }
+ }
+
+ Future requestRadioStats() async {
+ if (!isConnected) return;
+ if (!supportsCompanionRadioStats) return;
+ try {
+ await sendFrame(buildGetStatsFrame(statsTypeRadio));
+ } catch (_) {}
+ }
+
+ Future setPathHashMode(int mode) async {
+ if (!isConnected) return;
+ await sendFrame(buildSetPathHashModeFrame(mode.clamp(0, 2)));
+ }
+
Future refreshDeviceInfo() async {
if (!isConnected) return;
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ _webInitialHandshakeRequestSent &&
+ _selfPublicKey == null) {
+ return;
+ }
_awaitingSelfInfo = true;
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ _selfPublicKey == null) {
+ _webInitialHandshakeRequestSent = true;
+ }
await sendFrame(buildDeviceQueryFrame());
await sendFrame(buildAppStartFrame());
await requestBatteryStatus(force: true);
- await sendFrame(buildGetRadioSettingsFrame());
await sendFrame(buildGetCustomVarsFrame());
+ await sendFrame(buildGetAutoAddFlagsFrame());
+
_scheduleSelfInfoRetry();
}
Future _requestDeviceInfo() async {
+ if (!isConnected || _awaitingSelfInfo) return;
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ _webInitialHandshakeRequestSent &&
+ _selfPublicKey == null) {
+ return;
+ }
_awaitingSelfInfo = true;
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ _selfPublicKey == null) {
+ _webInitialHandshakeRequestSent = true;
+ }
await sendFrame(buildDeviceQueryFrame());
await sendFrame(buildAppStartFrame());
await sendFrame(buildGetCustomVarsFrame());
await requestBatteryStatus();
-
+ await sendFrame(buildGetAutoAddFlagsFrame());
_scheduleSelfInfoRetry();
}
void _scheduleSelfInfoRetry() {
_selfInfoRetryTimer?.cancel();
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth) {
+ var attempts = 0;
+ const maxAttempts = 3;
+ _selfInfoRetryTimer = Timer.periodic(const Duration(seconds: 10), (
+ timer,
+ ) {
+ if (!isConnected || !_awaitingSelfInfo) {
+ timer.cancel();
+ return;
+ }
+ if (_isLoadingContacts || _isSyncingChannels || _channelSyncInFlight) {
+ return;
+ }
+ attempts += 1;
+ unawaited(sendFrame(buildAppStartFrame()));
+ if (attempts >= maxAttempts) {
+ timer.cancel();
+ }
+ });
+ return;
+ }
_selfInfoRetryTimer = Timer.periodic(const Duration(milliseconds: 3500), (
timer,
) {
@@ -985,6 +2535,18 @@ class MeshCoreConnector extends ChangeNotifier {
});
}
+ Contact getFromDiscovered(Contact contact) {
+ final tmp = _discoveredContacts.firstWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ orElse: () => contact,
+ );
+ return contact.copyWith(
+ rawPacket: tmp.rawPacket,
+ latitude: tmp.latitude,
+ longitude: tmp.longitude,
+ );
+ }
+
Future getContacts({int? since, bool preserveExisting = false}) async {
if (!isConnected) return;
@@ -1011,50 +2573,61 @@ class MeshCoreConnector extends ChangeNotifier {
await sendFrame(buildGetContactByKeyFrame(pubKey));
}
- Future sendMessage(Contact contact, String text) async {
+ Future sendMessage(
+ Contact contact,
+ String text, {
+ String? originalText,
+ String? translatedLanguageCode,
+ String? translationModelId,
+ }) async {
if (!isConnected || text.isEmpty) return;
- // Handle auto-rotation if enabled
- PathSelection? autoSelection;
- if (_appSettingsService?.settings.autoRouteRotationEnabled == true) {
- autoSelection = _pathHistoryService?.getNextAutoPathSelection(
+ // Check if this is a reaction - apply locally with pending status and route through retry service
+ final reactionInfo = ReactionHelper.parseReaction(text);
+ if (reactionInfo != null) {
+ _conversations.putIfAbsent(contact.publicKeyHex, () => []);
+ final messages = _conversations[contact.publicKeyHex]!;
+
+ // Apply reaction locally with pending status
+ _processOutgoingContactReaction(messages, reactionInfo, contact);
+ _setReactionStatus(
contact.publicKeyHex,
+ reactionInfo,
+ MessageStatus.pending,
);
- if (autoSelection != null) {
- _pathHistoryService?.recordPathAttempt(
- contact.publicKeyHex,
- autoSelection,
- );
- if (!autoSelection.useFlood && autoSelection.pathBytes.isNotEmpty) {
- await setContactPath(
- contact,
- Uint8List.fromList(autoSelection.pathBytes),
- autoSelection.pathBytes.length,
- );
- }
+ _messageStore.saveMessages(contact.publicKeyHex, messages);
+ notifyListeners();
+
+ // Route through retry service (same as normal messages)
+ // Don't use auto-rotation for reactions — just send directly
+ if (_retryService != null) {
+ _retryService!.sendMessageWithRetry(contact: contact, text: text);
+ } else {
+ final outboundText = prepareContactOutboundText(contact, text);
+ await sendFrame(buildSendTextMsgFrame(contact.publicKey, outboundText));
}
+ return;
}
if (_retryService != null) {
- final pathBytes = _resolveOutgoingPathBytes(contact, autoSelection);
- final pathLength = _resolveOutgoingPathLength(contact, autoSelection);
- final selectedContact = _applyAutoSelection(contact, autoSelection);
await _retryService!.sendMessageWithRetry(
- contact: selectedContact,
+ contact: contact,
text: text,
- pathSelection: autoSelection,
- pathBytes: pathBytes,
- pathLength: pathLength,
+ originalText: originalText,
+ translatedLanguageCode: translatedLanguageCode,
+ translationModelId: translationModelId,
);
} else {
// Fallback to old behavior if retry service not initialized
- final pathBytes = _resolveOutgoingPathBytes(contact, autoSelection);
- final pathLength = _resolveOutgoingPathLength(contact, autoSelection);
+ final resolved = resolvePathSelection(contact);
final message = Message.outgoing(
contact.publicKey,
text,
- pathLength: pathLength,
- pathBytes: pathBytes,
+ pathLength: resolved.useFlood ? -1 : resolved.hopCount,
+ pathBytes: Uint8List.fromList(resolved.pathBytes),
+ originalText: originalText,
+ translatedLanguageCode: translatedLanguageCode,
+ translationModelId: translationModelId,
);
_addMessage(contact.publicKeyHex, message);
notifyListeners();
@@ -1068,17 +2641,132 @@ class MeshCoreConnector extends ChangeNotifier {
Uint8List customPath,
int pathLen,
) async {
+ // Serialize path operations to prevent interleaved async calls from
+ // leaving in-memory state inconsistent with the device.
+ final prev = _pathOpLock;
+ final completer = Completer();
+ _pathOpLock = completer.future;
+ await prev;
+ try {
+ if (!isConnected) return;
+
+ await sendFrame(
+ buildUpdateContactPathFrame(
+ contact.publicKey,
+ customPath,
+ pathLen,
+ type: contact.type,
+ flags: contact.flags,
+ name: contact.name,
+ ),
+ );
+ // USB writes return instantly (no BLE flow control), so give the firmware
+ // time to persist the path change before subsequent commands.
+ if (_activeTransport == MeshCoreTransportType.usb) {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ final idx = _contacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+ if (idx != -1) {
+ _contacts[idx] = _contacts[idx].copyWith(
+ pathLength: customPath.length,
+ path: customPath,
+ );
+ notifyListeners();
+ }
+ } finally {
+ completer.complete();
+ }
+ }
+
+ Future setContactFlags(
+ Contact contact, {
+ bool? isFavorite,
+ bool? teleBase,
+ bool? teleLoc,
+ bool? teleEnv,
+ }) async {
if (!isConnected) return;
+ final latestContact =
+ await _fetchContactSnapshotFromDevice(contact.publicKey) ?? contact;
+ int updatedFlags = isFavorite != null
+ ? (isFavorite
+ ? (latestContact.flags | contactFlagFavorite)
+ : (latestContact.flags & ~contactFlagFavorite))
+ : latestContact.flags;
+ updatedFlags = teleBase != null
+ ? (teleBase
+ ? (updatedFlags | contactFlagTeleBase)
+ : (updatedFlags & ~contactFlagTeleBase))
+ : updatedFlags;
+ updatedFlags = teleLoc != null
+ ? (teleLoc
+ ? (updatedFlags | contactFlagTeleLoc)
+ : (updatedFlags & ~contactFlagTeleLoc))
+ : updatedFlags;
+ updatedFlags = teleEnv != null
+ ? (teleEnv
+ ? (updatedFlags | contactFlagTeleEnv)
+ : (updatedFlags & ~contactFlagTeleEnv))
+ : updatedFlags;
await sendFrame(
buildUpdateContactPathFrame(
- contact.publicKey,
- customPath,
- pathLen,
- type: contact.type,
- name: contact.name,
+ latestContact.publicKey,
+ latestContact.path,
+ latestContact.pathLength,
+ type: latestContact.type,
+ flags: updatedFlags,
+ name: latestContact.name,
),
);
+
+ final index = _contacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+ if (index >= 0) {
+ _contacts[index] = _contacts[index].copyWith(
+ type: latestContact.type,
+ name: latestContact.name,
+ pathLength: latestContact.pathLength,
+ path: latestContact.path,
+ flags: updatedFlags,
+ );
+ notifyListeners();
+ unawaited(_persistContacts());
+ }
+ }
+
+ Future _fetchContactSnapshotFromDevice(
+ Uint8List pubKey, {
+ Duration timeout = const Duration(seconds: 3),
+ }) async {
+ if (!isConnected) return null;
+ final expectedKeyHex = pubKeyToHex(pubKey);
+ final completer = Completer();
+
+ void finish(Contact? result) {
+ if (!completer.isCompleted) {
+ completer.complete(result);
+ }
+ }
+
+ final subscription = receivedFrames.listen((frame) {
+ if (frame.isEmpty || frame[0] != respCodeContact) return;
+ final parsed = Contact.fromFrame(frame);
+ if (parsed == null || parsed.publicKeyHex != expectedKeyHex) return;
+ finish(parsed);
+ });
+
+ final timer = Timer(timeout, () => finish(null));
+ try {
+ await getContactByKey(pubKey);
+ return await completer.future;
+ } finally {
+ timer.cancel();
+ await subscription.cancel();
+ }
}
/// Set path override for a contact (persists across contact refreshes)
@@ -1126,6 +2814,9 @@ class MeshCoreConnector extends ChangeNotifier {
await _contactStore.saveContacts(_contacts);
appLogger.info('Saved contacts to storage', tag: 'Connector');
+ // Update any in-flight retries so they use the new path override
+ _retryService?.updatePendingContact(_contacts[index]);
+
// If setting a specific path (not flood, not auto), also sync with device
if (pathLen != null && pathLen >= 0 && pathBytes != null) {
appLogger.info('Sending path to device...', tag: 'Connector');
@@ -1144,27 +2835,27 @@ class MeshCoreConnector extends ChangeNotifier {
final autoRotationEnabled =
_appSettingsService?.settings.autoRouteRotationEnabled == true;
if (autoRotationEnabled && contact.pathOverride == null) {
- autoSelection = _pathHistoryService?.getNextAutoPathSelection(
+ final maxRetries = _appSettingsService?.settings.maxMessageRetries ?? 5;
+ autoSelection = _selectAutoPathForAttempt(
contact.publicKeyHex,
+ attemptIndex: 0,
+ maxRetries: maxRetries,
);
- if (autoSelection != null) {
- _pathHistoryService?.recordPathAttempt(
- contact.publicKeyHex,
- autoSelection,
- );
- }
}
- final pathBytes = _resolveOutgoingPathBytes(contact, autoSelection);
- final pathLength = _resolveOutgoingPathLength(contact, autoSelection) ?? -1;
+ final resolved = resolvePathSelection(contact, selection: autoSelection);
- if (pathLength < 0) {
+ if (resolved.useFlood) {
await clearContactPath(contact);
} else {
- await setContactPath(contact, pathBytes, pathLength);
+ await setContactPath(
+ contact,
+ Uint8List.fromList(resolved.pathBytes),
+ resolved.hopCount,
+ );
}
- return _selectionFromPath(pathLength, pathBytes);
+ return resolved;
}
void trackRepeaterAck({
@@ -1184,9 +2875,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(
@@ -1244,7 +2933,13 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
- Future sendChannelMessage(Channel channel, String text) async {
+ Future sendChannelMessage(
+ Channel channel,
+ String text, {
+ String? originalText,
+ String? translatedLanguageCode,
+ String? translationModelId,
+ }) async {
if (!isConnected || text.isEmpty) return;
// Check if this is a reaction - if so, process it immediately instead of adding as a message
@@ -1252,15 +2947,12 @@ class MeshCoreConnector extends ChangeNotifier {
if (reactionInfo != null) {
// Check if we've already processed this reaction
_processedChannelReactions.putIfAbsent(channel.index, () => {});
- final reactionKey = reactionInfo.reactionKey;
- final reactionIdentifier = reactionKey != null
- ? '${reactionKey}_${reactionInfo.emoji}'
- : null;
+ final reactionIdentifier =
+ '${reactionInfo.targetHash}_${reactionInfo.emoji}';
- if (reactionIdentifier != null &&
- _processedChannelReactions[channel.index]!.contains(
- reactionIdentifier,
- )) {
+ if (_processedChannelReactions[channel.index]!.contains(
+ reactionIdentifier,
+ )) {
// Already processed, don't process again
return;
}
@@ -1274,14 +2966,19 @@ class MeshCoreConnector extends ChangeNotifier {
await _channelMessageStore.saveChannelMessages(channel.index, messages);
// Mark this reaction as processed
- if (reactionIdentifier != null) {
- _processedChannelReactions[channel.index]!.add(reactionIdentifier);
- }
+ _processedChannelReactions[channel.index]!.add(reactionIdentifier);
notifyListeners();
// Send the reaction to the device (don't add as a visible message)
- await sendFrame(buildSendChannelTextMsgFrame(channel.index, text));
+ final reactionQueueId = _nextReactionSendQueueId();
+ _pendingChannelSentQueue.add(reactionQueueId);
+ await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
+ await sendFrame(
+ buildSendChannelTextMsgFrame(channel.index, text),
+ channelSendQueueId: reactionQueueId,
+ expectsGenericAck: true,
+ );
return;
}
@@ -1289,8 +2986,12 @@ class MeshCoreConnector extends ChangeNotifier {
text,
_selfName ?? 'Me',
channel.index,
+ originalText: originalText,
+ translatedLanguageCode: translatedLanguageCode,
+ translationModelId: translationModelId,
);
_addChannelMessage(channel.index, message);
+ _pendingChannelSentQueue.add(message.messageId);
notifyListeners();
final trimmed = text.trim();
@@ -1300,42 +3001,130 @@ class MeshCoreConnector extends ChangeNotifier {
(isChannelSmazEnabled(channel.index) && !isStructuredPayload)
? Smaz.encodeIfSmaller(text)
: text;
- await sendFrame(buildSendChannelTextMsgFrame(channel.index, outboundText));
+ await _waitForRadioQuiet(lastInboundRxTime: _lastChannelMsgRxTime);
+ await sendFrame(
+ buildSendChannelTextMsgFrame(channel.index, outboundText),
+ channelSendQueueId: message.messageId,
+ expectsGenericAck: true,
+ );
}
Future removeContact(Contact contact) async {
if (!isConnected) return;
+ _handleDiscovery(
+ contact,
+ contact.rawPacket ?? Uint8List(0),
+ noNotify: true,
+ );
+
await sendFrame(buildRemoveContactFrame(contact.publicKey));
_contacts.removeWhere((c) => c.publicKeyHex == contact.publicKeyHex);
_knownContactKeys.remove(contact.publicKeyHex);
unawaited(_persistContacts());
_conversations.remove(contact.publicKeyHex);
_loadedConversationKeys.remove(contact.publicKeyHex);
- _contactLastReadMs.remove(contact.publicKeyHex);
- _unreadStore.saveContactLastRead(Map.from(_contactLastReadMs));
+ _contactUnreadCount.remove(contact.publicKeyHex);
+ _unreadStore.saveContactUnreadCount(
+ Map.from(_contactUnreadCount),
+ );
_messageStore.clearMessages(contact.publicKeyHex);
notifyListeners();
}
- Future clearContactPath(Contact contact) async {
+ Future updateKnownDiscovered() async {
if (!isConnected) return;
+ for (int i = 0; i < _discoveredContacts.length; i++) {
+ _discoveredContacts[i] = _discoveredContacts[i].copyWith(
+ isActive: _knownContactKeys.contains(
+ _discoveredContacts[i].publicKeyHex,
+ ),
+ );
+ }
+ unawaited(_persistDiscoveredContacts());
+ notifyListeners();
+ }
- await sendFrame(buildResetPathFrame(contact.publicKey));
- final existingIndex = _contacts.indexWhere(
+ Future removeDiscoveredContact(Contact contact) async {
+ if (!isConnected) return;
+ _discoveredContacts.removeWhere(
(c) => c.publicKeyHex == contact.publicKeyHex,
);
- if (existingIndex >= 0) {
- final existing = _contacts[existingIndex];
- // Use copyWith to preserve pathOverride and pathOverrideBytes
- _contacts[existingIndex] = existing.copyWith(
- pathLength: -1,
- path: Uint8List(0),
- );
- notifyListeners();
- unawaited(_persistContacts());
+ unawaited(_persistDiscoveredContacts());
+ notifyListeners();
+ }
+
+ Future importDiscoveredContact(Contact contact) async {
+ if (!isConnected) return;
+
+ await sendFrame(
+ buildUpdateContactPathFrame(
+ contact.publicKey,
+ contact.path,
+ contact.pathLength,
+ type: contact.type,
+ flags: contact.flags,
+ name: contact.name,
+ lat: contact.latitude,
+ lon: contact.longitude,
+ lastModified: contact.lastSeen,
+ ),
+ );
+
+ // Update the discovered contact to mark it as active (imported)
+ final discoveredIndex = _discoveredContacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+ if (discoveredIndex >= 0) {
+ _discoveredContacts[discoveredIndex] =
+ _discoveredContacts[discoveredIndex].copyWith(isActive: true);
+ }
+
+ _handleContactAdvert(
+ Contact(
+ publicKey: contact.publicKey,
+ name: contact.name,
+ type: contact.type,
+ pathLength: contact.pathLength,
+ path: contact.path,
+ latitude: contact.latitude,
+ longitude: contact.longitude,
+ lastSeen: DateTime.now(),
+ flags: contact.flags,
+ ),
+ );
+ notifyListeners();
+ }
+
+ Future clearContactPath(Contact contact) async {
+ // Serialize path operations to prevent interleaved async calls.
+ final prev = _pathOpLock;
+ final completer = Completer();
+ _pathOpLock = completer.future;
+ await prev;
+ try {
+ if (!isConnected) return;
+
+ await sendFrame(buildResetPathFrame(contact.publicKey));
+ if (_activeTransport == MeshCoreTransportType.usb) {
+ await Future.delayed(const Duration(milliseconds: 100));
+ }
+ final existingIndex = _contacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+ if (existingIndex >= 0) {
+ final existing = _contacts[existingIndex];
+ // Preserve pathOverride and pathOverrideBytes — only reset device path
+ _contacts[existingIndex] = existing.copyWith(
+ pathLength: -1,
+ path: Uint8List(0),
+ );
+ notifyListeners();
+ unawaited(_persistContacts());
+ }
+ } finally {
+ completer.complete();
}
- // The device will send updated contact info with path_len = -1
}
void updateContactInMemory(
@@ -1469,13 +3258,46 @@ class MeshCoreConnector extends ChangeNotifier {
await sendCliCommand('set privacy ${enabled ? 'on' : 'off'}');
}
- Future getChannels({int? maxChannels}) async {
+ Future setTelemetryModeBase(
+ int base,
+ int location,
+ int env,
+ int advert,
+ int multiAcks,
+ ) async {
+ if (!isConnected) return;
+ _telemetryModeBase = base.clamp(teleModeDeny, teleModeAllowAll).toInt();
+ _telemetryModeLoc = location.clamp(teleModeDeny, teleModeAllowAll).toInt();
+ _telemetryModeEnv = env.clamp(teleModeDeny, teleModeAllowAll).toInt();
+ _advertLocPolicy = advert.clamp(0, 1).toInt();
+ _multiAcks = multiAcks.clamp(0, 2).toInt();
+ await sendFrame(
+ buildSetOtherParamsFrame(
+ (_telemetryModeEnv << 4) |
+ (_telemetryModeLoc << 2) |
+ _telemetryModeBase,
+ _advertLocPolicy,
+ _multiAcks,
+ ),
+ );
+ notifyListeners();
+ }
+
+ Future getChannels({int? maxChannels, bool force = false}) async {
if (!isConnected) return;
if (_isSyncingChannels) {
debugPrint('[ChannelSync] Already syncing channels, ignoring request');
return;
}
+ // Skip fetching if already loaded and not forced
+ if (_hasLoadedChannels && !force) {
+ debugPrint(
+ '[ChannelSync] Channels already loaded, skipping fetch (use force=true to reload)',
+ );
+ return;
+ }
+
_isLoadingChannels = true;
_isSyncingChannels = true;
_previousChannelsCache = List.from(_channels);
@@ -1580,6 +3402,10 @@ class MeshCoreConnector extends ChangeNotifier {
_cleanupChannelSync(completed: true);
+ // Cache channels for offline use
+ _cachedChannels = List.from(_channels);
+ unawaited(_channelStore.saveChannels(_channels));
+
// Apply ordering and notify UI
_applyChannelOrder();
notifyListeners();
@@ -1595,8 +3421,17 @@ class MeshCoreConnector extends ChangeNotifier {
_totalChannelsToRequest = 0;
if (completed) {
+ _hasLoadedChannels = true;
_previousChannelsCache.clear();
}
+
+ // Fallback: if contact sync was deferred waiting for channel 0 but
+ // channel sync finished without triggering it, start contacts now.
+ if (_pendingInitialContactsSync && isConnected) {
+ _pendingInitialContactsSync = false;
+ unawaited(getContacts());
+ }
+
// Keep cache on failure/disconnection for future attempts
}
@@ -1605,7 +3440,7 @@ class MeshCoreConnector extends ChangeNotifier {
await sendFrame(buildSetChannelFrame(index, name, psk));
// Refresh channels after setting
- await getChannels();
+ await getChannels(force: true);
}
Future deleteChannel(int index) async {
@@ -1613,27 +3448,29 @@ class MeshCoreConnector extends ChangeNotifier {
// Delete by setting empty name and zero PSK
await sendFrame(buildSetChannelFrame(index, '', Uint8List(16)));
- _channelLastReadMs.remove(index);
- _unreadStore.saveChannelLastRead(Map.from(_channelLastReadMs));
// Clear stored messages for this channel
await _channelMessageStore.clearChannelMessages(index);
// Clear in-memory messages for this channel
_channelMessages.remove(index);
// Refresh channels after deleting
- await getChannels();
+ await getChannels(force: true);
}
void _handleFrame(List data) {
if (data.isEmpty) return;
+ _lastRxTime = DateTime.now();
final frame = Uint8List.fromList(data);
_receivedFramesController.add(frame);
_bleDebugLogService?.logFrame(frame, outgoing: false);
final code = frame[0];
- debugPrint('RX frame: code=$code len=${frame.length}');
+ // debugPrint('RX frame: code=$code len=${frame.length}');
switch (code) {
+ case respCodeOk:
+ _handleOk();
+ break;
case respCodeDeviceInfo:
_handleDeviceInfo(frame);
break;
@@ -1649,6 +3486,14 @@ class MeshCoreConnector extends ChangeNotifier {
_isLoadingContacts = true;
notifyListeners();
break;
+ case pushCodeAdvert:
+ // Known contact was seen again - just a pub key, no action needed
+ break;
+ case pushCodeNewAdvert:
+ debugPrint('Got New CONTACT');
+ // It's the same format as respCodeContact, so we can reuse the handler
+ _handleContact(frame, isContact: false);
+ break;
case respCodeContact:
debugPrint('Got CONTACT');
_handleContact(frame);
@@ -1657,13 +3502,28 @@ class MeshCoreConnector extends ChangeNotifier {
debugPrint('Got END_OF_CONTACTS');
_isLoadingContacts = false;
_preserveContactsOnRefresh = false;
+ unawaited(updateKnownDiscovered());
notifyListeners();
unawaited(_persistContacts());
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ _isSyncingChannels &&
+ !_channelSyncInFlight) {
+ unawaited(_requestNextChannel());
+ }
if (!_didInitialQueueSync || _pendingQueueSync) {
_didInitialQueueSync = true;
_pendingQueueSync = false;
unawaited(syncQueuedMessages(force: true));
}
+ if (_pendingDeferredChannelSyncAfterContacts &&
+ (_activeTransport == MeshCoreTransportType.bluetooth ||
+ _activeTransport == MeshCoreTransportType.usb ||
+ _activeTransport == MeshCoreTransportType.tcp)) {
+ _pendingDeferredChannelSyncAfterContacts = false;
+ _pendingInitialChannelSync = false;
+ unawaited(getChannels());
+ }
break;
case respCodeContactMsgRecv:
case respCodeContactMsgRecvV3:
@@ -1693,24 +3553,54 @@ class MeshCoreConnector extends ChangeNotifier {
case pushCodeStatusResponse:
break;
case pushCodeLogRxData:
+ _lastRadioRxTime = DateTime.now();
+ _handleRxData(frame);
_handleLogRxData(frame);
break;
case respCodeChannelInfo:
_handleChannelInfo(frame);
break;
- case respCodeRadioSettings:
- _handleRadioSettings(frame);
+ case respCodeAutoAddConfig:
+ _handleAutoAddConfig(frame);
+ _checkManualAddContacts();
break;
case respCodeBattAndStorage:
_handleBatteryAndStorage(frame);
break;
+ case respCodeStats:
+ _handleStatsFrame(frame);
+ break;
case respCodeCustomVars:
_handleCustomVars(frame);
+ break;
+ // RESP_CODE_ERR is a defined firmware response (code 1), not an unknown frame.
+ case respCodeErr:
+ _handleErrorFrame(frame);
+ break;
default:
debugPrint('Unknown frame code: $code');
}
}
+ void _handleErrorFrame(Uint8List frame) {
+ final errCode = frame.length > 1 ? frame[1] : -1;
+ _appDebugLogService?.warn(
+ 'Firmware responded with error code: $errCode',
+ tag: 'Protocol',
+ );
+
+ if (_pendingGenericAckQueue.isEmpty) {
+ return;
+ }
+
+ final failedAck = _pendingGenericAckQueue.removeAt(0);
+ if (failedAck.commandCode != cmdSendChannelTxtMsg ||
+ failedAck.channelSendQueueId == null) {
+ return;
+ }
+ _pendingChannelSentQueue.remove(failedAck.channelSendQueueId);
+ }
+
void _handlePathUpdated(Uint8List frame) {
// Frame format: [0]=code, [1-32]=pub_key
if (frame.length >= 33 && _pathHistoryService != null) {
@@ -1748,37 +3638,113 @@ class MeshCoreConnector extends ChangeNotifier {
// [56] = sf
// [57] = cr
// [58+] = node_name
- if (frame.length < 4 + pubKeySize) return;
+ final wasAwaitingSelfInfo = _awaitingSelfInfo;
+ final reader = BufferReader(frame);
+ try {
+ reader.skipBytes(2);
+ _currentTxPower = reader.readInt8();
+ _maxTxPower = reader.readInt8();
+ _selfPublicKey = reader.readBytes(pubKeySize);
+ _selfLatitude = reader.readInt32LE() / 1000000.0;
+ _selfLongitude = reader.readInt32LE() / 1000000.0;
+ _multiAcks = reader.readByte();
+ _advertLocPolicy = reader.readByte();
+ final telemetryFlag = reader.readByte();
+ _telemetryModeBase = telemetryFlag & 0x03;
+ _telemetryModeEnv = telemetryFlag >> 2 & 0x03;
+ _telemetryModeLoc = telemetryFlag >> 4 & 0x03;
- _currentTxPower = frame[2];
- _maxTxPower = frame[3];
- _selfPublicKey = Uint8List.fromList(frame.sublist(4, 4 + pubKeySize));
- _selfLatitude = readInt32LE(frame, 36) / 1000000.0;
- _selfLongitude = readInt32LE(frame, 40) / 1000000.0;
+ _manualAddContacts = reader.readByte() & 0x01 == 0x00;
- // Radio settings (if frame is long enough)
- if (frame.length >= 58) {
- _currentFreqHz = readUint32LE(frame, 48);
- _currentBwHz = readUint32LE(frame, 52);
- _currentSf = frame[56];
- _currentCr = frame[57];
+ _currentFreqHz = reader.readUInt32LE();
+ _currentBwHz = reader.readUInt32LE();
+ _currentSf = reader.readByte();
+ _currentCr = reader.readByte();
+
+ _selfName = reader.readCString();
+ } catch (e) {
+ _appDebugLogService?.error(
+ 'Error parsing SELF_INFO frame: $e',
+ tag: 'Connector',
+ );
+ }
+ final selfName = _selfName?.trim();
+ if (_activeTransport == MeshCoreTransportType.usb &&
+ selfName != null &&
+ selfName.isNotEmpty) {
+ _usbManager.updateConnectedLabel(selfName);
}
- // Node name starts at offset 58 if frame is long enough
- if (frame.length > 58) {
- _selfName = readCString(frame, 58, frame.length - 58);
- }
+ //set all the stores' public key so they can load the correct data
+ _channelMessageStore.setPublicKeyHex = selfPublicKeyHex;
+ _messageStore.setPublicKeyHex = selfPublicKeyHex;
+ _channelOrderStore.setPublicKeyHex = selfPublicKeyHex;
+ _channelSettingsStore.setPublicKeyHex = selfPublicKeyHex;
+ _contactSettingsStore.setPublicKeyHex = selfPublicKeyHex;
+ _contactStore.setPublicKeyHex = selfPublicKeyHex;
+ _channelStore.setPublicKeyHex = selfPublicKeyHex;
+ _unreadStore.setPublicKeyHex = selfPublicKeyHex;
+
+ // Now that we have self info, we can load all the persisted data for this node
+ _loadChannelOrder();
+ loadContactCache();
+ loadChannelSettings();
+ loadCachedChannels();
+
+ // Load persisted channel messages
+ loadAllChannelMessages();
+ loadUnreadState();
+ _loadDiscoveredContactCache();
+
_awaitingSelfInfo = false;
_selfInfoRetryTimer?.cancel();
_selfInfoRetryTimer = null;
notifyListeners();
- // Auto-fetch contacts after getting self info
- getContacts();
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ !wasAwaitingSelfInfo) {
+ return;
+ }
+
+ // Auto-fetch contacts after getting self info. On web BLE, defer this
+ // until after channel 0 so startup writes stay serialized.
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth) {
+ _pendingInitialContactsSync = true;
+ } else if (_activeTransport == MeshCoreTransportType.usb ||
+ _activeTransport == MeshCoreTransportType.tcp) {
+ _pendingDeferredChannelSyncAfterContacts = true;
+ getContacts();
+ } else {
+ getContacts();
+ }
+ if (_shouldGateInitialChannelSync &&
+ _activeTransport != MeshCoreTransportType.usb &&
+ _activeTransport != MeshCoreTransportType.tcp) {
+ _maybeStartInitialChannelSync();
+ }
}
void _handleDeviceInfo(Uint8List frame) {
if (frame.length < 4) return;
+ if (_shouldGateInitialChannelSync) {
+ _hasReceivedDeviceInfo = true;
+ }
+ _firmwareVerCode = frame[1];
+
+ // Parse client_repeat from firmware v9+ (byte 80)
+ if (frame.length >= 81) {
+ _clientRepeat = frame[80] != 0;
+ }
+ // Path hash mode v10+ (byte 81): width = mode + 1 byte(s) per hop
+ if (frame.length >= 82) {
+ final mode = (frame[81] & 0xFF).clamp(0, 2);
+ _pathHashByteWidth = mode + 1;
+ } else {
+ _pathHashByteWidth = 1;
+ }
+
// Firmware reports MAX_CONTACTS / 2 for v3+ device info.
final reportedContacts = frame[2];
final reportedChannels = frame[3];
@@ -1795,12 +3761,29 @@ class MeshCoreConnector extends ChangeNotifier {
if (nextMaxChannels > previousMaxChannels) {
unawaited(loadChannelSettings(maxChannels: nextMaxChannels));
unawaited(loadAllChannelMessages(maxChannels: nextMaxChannels));
- if (isConnected) {
+ if (isConnected &&
+ _selfPublicKey != null &&
+ (!_shouldGateInitialChannelSync || !_pendingInitialChannelSync)) {
unawaited(getChannels(maxChannels: nextMaxChannels));
}
}
- notifyListeners();
}
+ notifyListeners();
+ if (_shouldGateInitialChannelSync) {
+ _maybeStartInitialChannelSync();
+ }
+ }
+
+ void _maybeStartInitialChannelSync() {
+ if (!_pendingInitialChannelSync || !isConnected) {
+ return;
+ }
+ if (_selfPublicKey == null || !_hasReceivedDeviceInfo) {
+ return;
+ }
+
+ _pendingInitialChannelSync = false;
+ unawaited(getChannels(maxChannels: _maxChannels));
}
void _handleNoMoreMessages() {
@@ -1820,23 +3803,17 @@ class MeshCoreConnector extends ChangeNotifier {
unawaited(_requestNextQueuedMessage());
}
- void _handleRadioSettings(Uint8List frame) {
- // Frame format from C++:
- // [0] = RESP_CODE_RADIO_SETTINGS
- // [1-4] = freq (uint32 LE, in Hz)
- // [5-8] = bw (uint32 LE, in Hz)
- // [9] = sf
- // [10] = cr
- if (frame.length >= 11) {
- _currentFreqHz = readUint32LE(frame, 1);
- _currentBwHz = readUint32LE(frame, 5);
- _currentSf = frame[9];
- _currentCr = frame[10];
- debugPrint(
- 'Radio settings: freq=$_currentFreqHz bw=$_currentBwHz sf=$_currentSf cr=$_currentCr',
- );
- notifyListeners();
+ void _handleStatsFrame(Uint8List frame) {
+ final stats = CompanionRadioStats.tryParse(frame);
+ if (stats == null) return;
+ final total = stats.txAirSecs + stats.rxAirSecs;
+ if (total > _prevTotalAirSecs) {
+ (_airtimeBumpStopwatch ??= Stopwatch()).reset();
+ _airtimeBumpStopwatch!.start();
}
+ _prevTotalAirSecs = total;
+ _latestRadioStats = stats;
+ radioStatsNotifier.value = stats;
}
void _handleBatteryAndStorage(Uint8List frame) {
@@ -1845,56 +3822,140 @@ 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.readUInt16LE();
+ _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',
+ );
}
}
- /// Calculate timeout for a message based on radio settings and path length
- /// Returns timeout in milliseconds, considering number of hops
- int calculateTimeout({required int pathLength, int messageBytes = 100}) {
- // If we have radio settings, use them for accurate calculation
+ void _checkManualAddContacts() async {
+ // If manual add contacts is enabled, set auto add config and other params.
+ // and disable it after
+ if (_manualAddContacts) {
+ await sendFrame(
+ buildSetAutoAddConfigFrame(
+ autoAddChat: true,
+ autoAddRepeater: true,
+ autoAddRoomServer: true,
+ autoAddSensor: true,
+ overwriteOldest: _overwriteOldest,
+ ),
+ );
+ await sendFrame(
+ buildSetOtherParamsFrame(
+ (_telemetryModeEnv << 4) |
+ (_telemetryModeLoc << 2) |
+ (_telemetryModeBase),
+ _advertLocPolicy,
+ _multiAcks,
+ ),
+ );
+ _manualAddContacts = false;
+ }
+ }
+
+ /// Estimate single-packet airtime in ms from radio settings, or a fallback.
+ int _estimateAirtimeMs(int messageBytes) {
if (_currentFreqHz != null &&
_currentBwHz != null &&
_currentSf != null &&
_currentCr != null) {
final cr = _currentCr! <= 4 ? _currentCr! : _currentCr! - 4;
- return calculateMessageTimeout(
- freqHz: _currentFreqHz!,
- bwHz: _currentBwHz!,
- sf: _currentSf!,
- cr: cr,
- pathLength: pathLength,
- messageBytes: messageBytes,
+ return calculateLoRaAirtime(
+ payloadBytes: messageBytes,
+ spreadingFactor: _currentSf!,
+ bandwidthHz: _currentBwHz!,
+ codingRate: cr,
+ lowDataRateOptimize: _currentSf! >= 11,
);
}
+ return 50; // fallback: ~SF7/BW125 for 100 bytes
+ }
- // Fallback: Conservative estimates based on typical settings
- // Assume SF7, BW125, which gives ~50ms airtime for 100 bytes
- const estimatedAirtime = 50;
-
+ /// Physics-based worst-case timeout (ceiling).
+ int _physicsMaxTimeout(int pathLength, int airtime) {
if (pathLength < 0) {
- // Flood mode: Base delay + 16× airtime
- return 500 + (16 * estimatedAirtime);
+ // Match firmware: SEND_TIMEOUT_BASE_MILLIS + (FLOOD_SEND_TIMEOUT_FACTOR * airtime)
+ return 500 + (16 * airtime);
} else {
- // Direct path: Base delay + ((airtime×6 + 250ms)×(hops+1))
- return 500 + ((estimatedAirtime * 6 + 250) * (pathLength + 1));
+ return 500 + ((airtime * 6 + 250) * (pathLength + 1));
}
}
- void _handleContact(Uint8List frame) {
- final contact = Contact.fromFrame(frame);
- if (contact != null) {
+ int _physicsMinTimeout(int pathLength, int airtime) {
+ if (pathLength < 0) {
+ // Same as max for flood — firmware uses a single formula
+ return 500 + (16 * airtime);
+ } else {
+ return airtime * (pathLength + 1);
+ }
+ }
+
+ /// Calculate timeout for a message based on radio settings and path length.
+ /// Returns timeout in milliseconds, considering number of hops.
+ int calculateTimeout({
+ required int pathLength,
+ int messageBytes = 100,
+ String? contactKey,
+ }) {
+ final airtime = _estimateAirtimeMs(messageBytes);
+ final physicsMin = _physicsMinTimeout(pathLength, airtime);
+ final physicsMax = _physicsMaxTimeout(pathLength, airtime);
+
+ // Try ML-based prediction
+ final secSinceRx = DateTime.now().difference(_lastRxTime).inSeconds;
+ final mlTimeout = _timeoutPredictionService?.predictTimeout(
+ contactKey: contactKey,
+ pathLength: pathLength,
+ messageBytes: messageBytes,
+ secondsSinceLastRx: secSinceRx,
+ );
+ if (mlTimeout != null) {
+ if (pathLength < 0) {
+ // Flood: trust ML, only enforce firmware formula as floor
+ if (mlTimeout < physicsMin) {
+ return physicsMin;
+ }
+ }
+ return mlTimeout.clamp(physicsMin, physicsMax);
+ }
+
+ // No ML data — use firmware formula
+ return physicsMax;
+ }
+
+ void _handleContact(Uint8List frame, {bool isContact = true}) {
+ final contactTmp = Contact.fromFrame(frame);
+ if (contactTmp != null) {
+ if (listEquals(contactTmp.publicKey, _selfPublicKey)) {
+ appLogger.info(
+ 'Ignoring contact with self public key: ${contactTmp.name}',
+ tag: 'Connector',
+ );
+ removeContact(contactTmp);
+ return;
+ }
+ final contact = getFromDiscovered(contactTmp);
+ _handleDiscovery(contact, frame, noNotify: true, addActive: true);
+
if (contact.type == advTypeRepeater) {
- _contactLastReadMs.remove(contact.publicKeyHex);
- _unreadStore.saveContactLastRead(
- Map.from(_contactLastReadMs),
+ _contactUnreadCount.remove(contact.publicKeyHex);
+ _unreadStore.saveContactUnreadCount(
+ Map.from(_contactUnreadCount),
);
}
// Check if this is a new contact
@@ -1915,11 +3976,14 @@ class MeshCoreConnector extends ChangeNotifier {
tag: 'Connector',
);
- // CRITICAL: Preserve user's path override when contact is refreshed from device
+ // Preserve user-selected path settings and previously known GPS when
+ // refreshed frames omit coordinates (lat/lon encoded as 0,0).
_contacts[existingIndex] = contact.copyWith(
lastMessageAt: mergedLastMessageAt,
pathOverride: existing.pathOverride, // Preserve user's path choice
pathOverrideBytes: existing.pathOverrideBytes,
+ latitude: contact.latitude ?? existing.latitude,
+ longitude: contact.longitude ?? existing.longitude,
);
appLogger.info(
@@ -1927,11 +3991,23 @@ class MeshCoreConnector extends ChangeNotifier {
tag: 'Connector',
);
} else {
- _contacts.add(contact);
- appLogger.info(
- 'Added new contact ${contact.name}: pathLen=${contact.pathLength}',
- tag: 'Connector',
- );
+ if ((_autoAddUsers && contact.type == advTypeChat) ||
+ (_autoAddRepeaters && contact.type == advTypeRepeater) ||
+ (_autoAddRoomServers && contact.type == advTypeRoom) ||
+ (_autoAddSensors && contact.type == advTypeSensor) ||
+ isContact) {
+ _contacts.add(contact);
+ appLogger.info(
+ 'Added new contact ${contact.name}: pathLen=${contact.pathLength}',
+ tag: 'Connector',
+ );
+ } else {
+ appLogger.info(
+ "Discovered contact ${contact.name} (type ${contact.typeLabel}) not added due to auto-add settings",
+ tag: 'Connector',
+ );
+ return;
+ }
}
_knownContactKeys.add(contact.publicKeyHex);
_loadMessagesForContact(contact.publicKeyHex);
@@ -1961,10 +4037,88 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
+ void _handleContactAdvert(Contact contact) {
+ if (listEquals(contact.publicKey, _selfPublicKey)) {
+ return;
+ }
+
+ if (contact.type == advTypeRepeater) {
+ _contactUnreadCount.remove(contact.publicKeyHex);
+ _unreadStore.saveContactUnreadCount(
+ Map.from(_contactUnreadCount),
+ );
+ }
+ // Check if this is a new contact
+ final isNewContact = !_knownContactKeys.contains(contact.publicKeyHex);
+ final existingIndex = _contacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+
+ if (existingIndex >= 0) {
+ final existing = _contacts[existingIndex];
+ final mergedLastMessageAt =
+ existing.lastMessageAt.isAfter(contact.lastMessageAt)
+ ? existing.lastMessageAt
+ : contact.lastMessageAt;
+
+ appLogger.info(
+ 'Refreshing contact ${contact.name}: devicePath=${contact.pathLength}, existingOverride=${existing.pathOverride}',
+ tag: 'Connector',
+ );
+
+ // CRITICAL: Preserve user's path override when contact is refreshed from device
+ _contacts[existingIndex] = contact.copyWith(
+ lastMessageAt: mergedLastMessageAt,
+ pathOverride: existing.pathOverride, // Preserve user's path choice
+ pathOverrideBytes: existing.pathOverrideBytes,
+ );
+
+ appLogger.info(
+ 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}',
+ tag: 'Connector',
+ );
+ } else {
+ _contacts.add(contact);
+ appLogger.info(
+ 'Added new contact ${contact.name}: pathLen=${contact.pathLength}',
+ tag: 'Connector',
+ );
+ }
+ _knownContactKeys.add(contact.publicKeyHex);
+ _loadMessagesForContact(contact.publicKeyHex);
+
+ // Add path to history if we have a valid path
+ if (_pathHistoryService != null && contact.pathLength >= 0) {
+ _pathHistoryService!.handlePathUpdated(contact);
+ }
+
+ notifyListeners();
+
+ // Show notification for new contact (advertisement)
+ if (isNewContact && _appSettingsService != null) {
+ final settings = _appSettingsService!.settings;
+ if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
+ _notificationService.showAdvertNotification(
+ contactName: contact.name,
+ contactType: contact.typeLabel,
+ contactId: contact.publicKeyHex,
+ );
+ }
+ }
+
+ if (!_isLoadingContacts) {
+ unawaited(_persistContacts());
+ }
+ }
+
Future _persistContacts() async {
await _contactStore.saveContacts(_contacts);
}
+ Future _persistDiscoveredContacts() async {
+ await _discoveryContactStore.saveContacts(_discoveredContacts);
+ }
+
int _latestContactLastmod() {
if (_contacts.isEmpty) return 0;
var latest = 0;
@@ -2044,9 +4198,10 @@ class MeshCoreConnector extends ChangeNotifier {
}
bool _pathMatchesContact(Uint8List pathBytes, Uint8List publicKey) {
- if (pathBytes.isEmpty || publicKey.length < pathHashSize) return false;
- for (int i = 0; i + pathHashSize <= pathBytes.length; i += pathHashSize) {
- final prefix = pathBytes.sublist(i, i + pathHashSize);
+ final w = _pathHashByteWidth;
+ if (pathBytes.isEmpty || publicKey.length < w) return false;
+ for (int i = 0; i + w <= pathBytes.length; i += w) {
+ final prefix = pathBytes.sublist(i, i + w);
if (_matchesPrefix(publicKey, prefix)) {
return true;
}
@@ -2081,6 +4236,18 @@ class MeshCoreConnector extends ChangeNotifier {
}
if (message != null) {
+ if (!message.isOutgoing) {
+ _lastContactMsgRxTime = DateTime.now();
+ }
+ // Ignore messages from self (device hearing its own broadcast)
+ // BUT allow repeated messages (pathLength indicates it went through repeater)
+ if (_selfPublicKey != null &&
+ message.senderKeyHex == pubKeyToHex(_selfPublicKey!) &&
+ (message.pathLength == null || message.pathLength == 0)) {
+ debugPrint('Ignoring direct message from self');
+ return;
+ }
+
final contact = _contacts.cast().firstWhere(
(c) => c?.publicKeyHex == message!.senderKeyHex,
orElse: () => null,
@@ -2110,7 +4277,12 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
_addMessage(message.senderKeyHex, message);
- _maybeMarkActiveContactRead(message);
+ if (!message.isOutgoing) {
+ unawaited(
+ _translateIncomingContactMessage(message.senderKeyHex, message),
+ );
+ }
+ _maybeIncrementContactUnread(message);
notifyListeners();
// Show notification for new incoming message
@@ -2119,7 +4291,6 @@ class MeshCoreConnector extends ChangeNotifier {
_appSettingsService != null) {
final settings = _appSettingsService!.settings;
if (settings.notificationsEnabled && settings.notifyOnNewMessage) {
- // Find the contact name
if (contact?.type == advTypeChat) {
_notificationService.showMessageNotification(
contactName: contact?.name ?? 'Unknown',
@@ -2130,7 +4301,9 @@ class MeshCoreConnector extends ChangeNotifier {
} else if (contact?.type == advTypeRoom) {
_notificationService.showMessageNotification(
contactName: contact?.name ?? 'Unknown Room',
- message: message.text.substring(4),
+ message: message.text.length > 4
+ ? message.text.substring(4)
+ : message.text,
contactId: message.senderKeyHex,
badgeCount: getTotalUnreadCount(),
);
@@ -2144,70 +4317,93 @@ class MeshCoreConnector extends ChangeNotifier {
}
Message? _parseContactMessage(Uint8List frame) {
- if (frame.isEmpty) return null;
- final code = frame[0];
- if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) {
+ if (frame.isEmpty) {
+ appLogger.warn('Received empty frame, ignoring');
return null;
}
+ final reader = BufferReader(frame);
- // Companion radio layout:
- // [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...]
- final prefixOffset = code == respCodeContactMsgRecvV3 ? 4 : 1;
- const prefixLen = 6;
- final pathLenOffset = prefixOffset + prefixLen;
- final txtTypeOffset = pathLenOffset + 1;
- final timestampOffset = txtTypeOffset + 1;
- final baseTextOffset = timestampOffset + 4;
+ try {
+ final code = reader.readByte();
+ if (code != respCodeContactMsgRecv && code != respCodeContactMsgRecvV3) {
+ appLogger.warn(
+ 'Unexpected message code: $code, expected contact message receive codes',
+ );
+ return null;
+ }
- if (frame.length <= baseTextOffset) return null;
- final fourBytePubMSG = frame.sublist(baseTextOffset, baseTextOffset + 4);
- final senderPrefix = frame.sublist(prefixOffset, prefixOffset + prefixLen);
- 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;
- }
+ // Companion radio layout:
+ // [code][snr?][res?][res?][prefix x6][path_len][txt_type][timestamp x4][extra?][text...]
+ // double snr = 0;
+ if (code == respCodeContactMsgRecvV3) {
+ // Older firmware layout with SNR as a signed byte after the code
+ // snr = reader.readInt8().toDouble() * 4; // SNR in dB, scaled by 4
+ reader.skipBytes(1); // Skip SNR byte
+ reader.skipBytes(2); // Skip reserved bytes
+ }
- // Try base text offset; if empty and there is room for the optional 4-byte extra
- // (used by signed/plain variants), try again skipping those bytes.
- var text = readCString(
- frame,
- baseTextOffset,
- frame.length - baseTextOffset,
- );
- if (text.isEmpty && frame.length > baseTextOffset + 4) {
- text = readCString(
- frame,
- baseTextOffset + 4,
- frame.length - (baseTextOffset + 4),
+ final senderPrefix = reader.readBytes(6);
+ final pathLength = reader.readByte();
+ final txtType = reader.readByte();
+ final timestampRaw = reader.readUInt32LE();
+ final timestamp = DateTime.fromMillisecondsSinceEpoch(
+ timestampRaw * 1000,
);
+
+ if (txtType == 2) {
+ reader.skipBytes(4); // Skip extra 4 bytes for signed/plain variants
+ }
+
+ final msgText = reader.readCString();
+
+ final flags = txtType;
+ final shiftedType = flags >> 2;
+ final rawType = flags;
+ final isPlain = shiftedType == txtTypePlain || rawType == txtTypePlain;
+ final isCli = shiftedType == txtTypeCliData || rawType == txtTypeCliData;
+ if (!isPlain && !isCli) {
+ appLogger.warn(
+ 'Unknown message type received: txtType=$txtType, shifted=$shiftedType, raw=$rawType',
+ );
+ return null;
+ }
+
+ if (msgText.isEmpty) {
+ appLogger.warn('Received message with empty text, ignoring');
+ return null;
+ }
+ final decodedText = isCli
+ ? msgText
+ : (Smaz.tryDecodePrefixed(msgText) ?? msgText);
+
+ final contact = _contacts.cast().firstWhere(
+ (c) => c != null && _matchesPrefix(c.publicKey, senderPrefix),
+ orElse: () => null,
+ );
+ if (contact == null) {
+ appLogger.warn(
+ 'Received message from unknown contact with prefix: ${senderPrefix.map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase()).join('')}',
+ );
+ return null;
+ }
+
+ return Message(
+ senderKey: contact.publicKey,
+ text: decodedText,
+ timestamp: timestamp,
+ isOutgoing: false,
+ isCli: isCli,
+ status: MessageStatus.delivered,
+ pathLength: pathLength == 0xFF ? 0 : pathLength,
+ pathBytes: Uint8List(0),
+ fourByteRoomContactKey: msgText.length >= 4
+ ? Uint8List.fromList(msgText.substring(0, 4).codeUnits)
+ : null,
+ );
+ } catch (e) {
+ appLogger.warn('Error parsing contact direct message: $e');
+ return null;
}
- if (text.isEmpty) return null;
- final decodedText = isCli ? text : (Smaz.tryDecodePrefixed(text) ?? text);
-
- final timestampRaw = readUint32LE(frame, timestampOffset);
- final pathLenByte = frame[pathLenOffset];
-
- final contact = _contacts.cast().firstWhere(
- (c) => c != null && _matchesPrefix(c.publicKey, senderPrefix),
- orElse: () => null,
- );
- if (contact == null) return null;
-
- return Message(
- senderKey: contact.publicKey,
- text: decodedText,
- timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000),
- isOutgoing: false,
- isCli: isCli,
- status: MessageStatus.delivered,
- pathLength: pathLenByte == 0xFF ? 0 : pathLenByte,
- pathBytes: Uint8List(0),
- fourByteRoomContactKey: fourBytePubMSG,
- );
}
bool _matchesPrefix(Uint8List fullKey, Uint8List prefix) {
@@ -2278,8 +4474,11 @@ class MeshCoreConnector extends ChangeNotifier {
}
final label = channelName ?? _channelDisplayName(channelIndex);
+ if (_appSettingsService!.isChannelMuted(label)) return;
+
_notificationService.showChannelMessageNotification(
channelName: label,
+ senderName: message.senderName,
message: message.text,
channelIndex: channelIndex,
badgeCount: getTotalUnreadCount(),
@@ -2287,21 +4486,30 @@ class MeshCoreConnector extends ChangeNotifier {
}
void _handleIncomingChannelMessage(Uint8List frame) {
- final message = ChannelMessage.fromFrame(frame);
- if (message != null && message.channelIndex != null) {
- if (_shouldDropSelfChannelMessage(
- message.senderName,
- message.pathBytes,
- )) {
+ final parsed = ChannelMessage.fromFrame(frame);
+ if (parsed != null && parsed.channelIndex != null) {
+ if (_shouldDropSelfChannelMessage(parsed.senderName, parsed.pathBytes)) {
return;
}
+ _lastChannelMsgRxTime = DateTime.now();
+ final contentHash = _computeContentHash(
+ parsed.channelIndex!,
+ parsed.timestamp.millisecondsSinceEpoch ~/ 1000,
+ '${parsed.senderName}: ${parsed.text}',
+ );
+ final message = parsed.copyWith(packetHash: contentHash);
_updateContactLastMessageAtByName(
message.senderName,
message.timestamp,
pathBytes: message.pathBytes,
);
final isNew = _addChannelMessage(message.channelIndex!, message);
- _maybeMarkActiveChannelRead(message);
+ if (isNew && !message.isOutgoing) {
+ unawaited(
+ _translateIncomingChannelMessage(message.channelIndex!, message),
+ );
+ }
+ _maybeIncrementChannelUnread(message, isNew: isNew);
notifyListeners();
if (isNew) {
_maybeNotifyChannelMessage(message);
@@ -2314,63 +4522,90 @@ 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;
- for (final channel in _channels) {
- if (channel.isEmpty) continue;
- final hash = _computeChannelHash(channel.psk);
- if (hash != channelHash) continue;
+ final payload = BufferReader(packet.payload);
+ final channelHash = payload.readByte();
+ final encrypted = Uint8List.fromList(payload.readRemainingBytes());
- 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);
- final txtType = decrypted[4];
- if ((txtType >> 2) != 0) {
- return;
+ final timestampRaw = decrypted.readUInt32LE();
+ final txtType = decrypted.readByte();
+ if ((txtType >> 2) != 0) {
+ return;
+ }
+
+ final text = decrypted.readCString();
+ 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);
+ if (isNew && !message.isOutgoing) {
+ unawaited(_translateIncomingChannelMessage(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 message = ChannelMessage(
- senderKey: null,
- senderName: parsed.senderName,
- text: decodedText,
- timestamp: DateTime.fromMillisecondsSinceEpoch(timestampRaw * 1000),
- isOutgoing: false,
- status: ChannelMessageStatus.sent,
- pathLength: packet.isFlood ? packet.pathBytes.length : 0,
- pathBytes: packet.pathBytes,
- channelIndex: channel.index,
- );
-
- _updateContactLastMessageAtByName(
- parsed.senderName,
- message.timestamp,
- pathBytes: message.pathBytes,
- );
- final isNew = _addChannelMessage(channel.index, message);
- _maybeMarkActiveChannelRead(message);
- 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');
}
}
@@ -2381,15 +4616,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); //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
- .map((b) => b.toRadixString(16).padLeft(2, '0'))
- .join();
+ final ackHashHex = ackHashToHex(ackHash);
debugPrint('Ignoring CLI command ACK (sent): $ackHashHex');
_lastSentWasCliCommand = false;
return;
@@ -2399,10 +4634,17 @@ class MeshCoreConnector extends ChangeNotifier {
return;
}
- if (_retryService != null) {
- _retryService!.updateMessageFromSent(ackHash, timeoutMs);
+ final retryService = _retryService;
+ if (retryService != null &&
+ retryService.updateMessageFromSent(ackHash, timeoutMs)) {
+ return;
}
- } else {
+
+ if (_markNextPendingChannelMessageSent()) {
+ return;
+ }
+ } 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--) {
@@ -2417,15 +4659,75 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
+ bool _markNextPendingChannelMessageSent() {
+ while (_pendingChannelSentQueue.isNotEmpty) {
+ final queuedMessageId = _pendingChannelSentQueue.removeAt(0);
+ if (_isReactionSendQueueId(queuedMessageId)) {
+ return true;
+ }
+ if (_markPendingChannelMessageSentById(queuedMessageId)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool _markPendingChannelMessageSentById(String messageId) {
+ for (final entry in _channelMessages.entries) {
+ final channelMessages = entry.value;
+ for (int i = channelMessages.length - 1; i >= 0; i--) {
+ final message = channelMessages[i];
+ if (message.messageId != messageId) {
+ continue;
+ }
+ if (!message.isOutgoing ||
+ message.status != ChannelMessageStatus.pending) {
+ return false;
+ }
+ channelMessages[i] = message.copyWith(
+ status: ChannelMessageStatus.sent,
+ );
+ _pendingChannelSentQueue.remove(messageId);
+ unawaited(
+ _channelMessageStore.saveChannelMessages(entry.key, channelMessages),
+ );
+ notifyListeners();
+ return true;
+ }
+ }
+ return false;
+ }
+
+ void _handleOk() {
+ if (_pendingGenericAckQueue.isEmpty) {
+ return;
+ }
+
+ final pendingAck = _pendingGenericAckQueue.removeAt(0);
+ if (pendingAck.commandCode != cmdSendChannelTxtMsg ||
+ pendingAck.channelSendQueueId == null) {
+ return;
+ }
+
+ final queueId = pendingAck.channelSendQueueId!;
+ _pendingChannelSentQueue.remove(queueId);
+ if (_isReactionSendQueueId(queueId)) {
+ return;
+ }
+ _markPendingChannelMessageSentById(queueId);
+ }
+
void _handleSendConfirmed(Uint8List frame) {
// Frame format from C++:
// [0] = PUSH_CODE_SEND_CONFIRMED
// [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
@@ -2437,7 +4739,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--) {
@@ -2452,10 +4755,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 = ackHashToHex(ackHash);
final entry = _pendingRepeaterAcks[ackHashHex];
if (entry == null) return false;
@@ -2473,10 +4774,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 = ackHashToHex(ackHash);
final entry = _pendingRepeaterAcks.remove(ackHashHex);
if (entry == null) return false;
entry.timeout?.cancel();
@@ -2492,6 +4791,15 @@ class MeshCoreConnector extends ChangeNotifier {
'[ChannelSync] Received channel ${channel.index}: ${channel.isEmpty ? "empty" : channel.name}',
);
+ // Preserve unread count from cached channel
+ final cachedChannel = _cachedChannels.cast().firstWhere(
+ (c) => c?.index == channel.index,
+ orElse: () => null,
+ );
+ if (cachedChannel != null) {
+ channel.unreadCount = cachedChannel.unreadCount;
+ }
+
// If we're syncing and this is the channel we're waiting for
if (_isSyncingChannels && _channelSyncInFlight) {
if (channel.index == _nextChannelIndexToRequest) {
@@ -2507,6 +4815,14 @@ class MeshCoreConnector extends ChangeNotifier {
// Move to next channel
_nextChannelIndexToRequest++;
+ if (PlatformInfo.isWeb &&
+ _activeTransport == MeshCoreTransportType.bluetooth &&
+ channel.index == 0 &&
+ _pendingInitialContactsSync) {
+ _pendingInitialContactsSync = false;
+ unawaited(getContacts());
+ return;
+ }
unawaited(_requestNextChannel());
return;
} else {
@@ -2531,6 +4847,8 @@ class MeshCoreConnector extends ChangeNotifier {
(c) => c.index == channel.index,
);
if (existingIndex >= 0) {
+ // Preserve unread count from existing channel
+ channel.unreadCount = _channels[existingIndex].unreadCount;
_channels[existingIndex] = channel;
} else {
_channels.add(channel);
@@ -2581,67 +4899,98 @@ class MeshCoreConnector extends ChangeNotifier {
return contact.type != advTypeRepeater;
}
- int _calculateReadTimestampMs(Iterable? timestamps) {
- var latestMs = 0;
- if (timestamps != null) {
- for (final timestamp in timestamps) {
- final ms = timestamp.millisecondsSinceEpoch;
- if (ms > latestMs) {
- latestMs = ms;
- }
- }
- }
- return latestMs;
+ Channel? _findChannelByIndex(int index) {
+ return _channels.cast().firstWhere(
+ (c) => c?.index == index,
+ orElse: () => null,
+ ) ??
+ _cachedChannels.cast().firstWhere(
+ (c) => c?.index == index,
+ orElse: () => null,
+ );
}
- void _setContactLastReadMs(
- String contactKeyHex,
- int timestampMs, {
- bool notify = true,
+ void _maybeIncrementChannelUnread(
+ ChannelMessage message, {
+ required bool isNew,
}) {
- if (!_shouldTrackUnreadForContactKey(contactKeyHex)) return;
- final existing = _contactLastReadMs[contactKeyHex] ?? 0;
- if (timestampMs <= existing) return;
- _contactLastReadMs[contactKeyHex] = timestampMs;
- _unreadStore.saveContactLastRead(Map.from(_contactLastReadMs));
- if (notify) {
- notifyListeners();
+ if (!isNew || message.isOutgoing) {
+ _appDebugLogService?.info(
+ 'Skip unread increment: isNew=$isNew, isOutgoing=${message.isOutgoing}',
+ tag: 'Unread',
+ );
+ return;
}
- }
-
- void _setChannelLastReadMs(
- int channelIndex,
- int timestampMs, {
- bool notify = true,
- }) {
- final existing = _channelLastReadMs[channelIndex] ?? 0;
- if (timestampMs <= existing) return;
- _channelLastReadMs[channelIndex] = timestampMs;
- _unreadStore.saveChannelLastRead(Map.from(_channelLastReadMs));
- if (notify) {
- notifyListeners();
- }
- }
-
- void _maybeMarkActiveContactRead(Message message) {
- if (message.isOutgoing || message.isCli) return;
- if (_activeContactKey != message.senderKeyHex) return;
- if (!_shouldTrackUnreadForContactKey(message.senderKeyHex)) return;
- _setContactLastReadMs(
- message.senderKeyHex,
- message.timestamp.millisecondsSinceEpoch,
- notify: false,
- );
- }
-
- void _maybeMarkActiveChannelRead(ChannelMessage message) {
- if (message.isOutgoing) return;
final channelIndex = message.channelIndex;
- if (channelIndex == null || _activeChannelIndex != channelIndex) return;
- _setChannelLastReadMs(
- channelIndex,
- message.timestamp.millisecondsSinceEpoch,
- notify: false,
+ if (channelIndex == null) {
+ _appDebugLogService?.info(
+ 'Skip unread increment: channelIndex is null',
+ tag: 'Unread',
+ );
+ return;
+ }
+ // Don't increment if user is viewing this channel
+ if (_activeChannelIndex == channelIndex) {
+ _appDebugLogService?.info(
+ 'Skip unread increment: channel $channelIndex is active',
+ tag: 'Unread',
+ );
+ return;
+ }
+
+ final channel = _findChannelByIndex(channelIndex);
+ if (channel != null) {
+ channel.unreadCount++;
+ _appDebugLogService?.info(
+ 'Channel ${channel.name.isNotEmpty ? channel.name : channelIndex} unread count incremented to ${channel.unreadCount}',
+ tag: 'Unread',
+ );
+ unawaited(
+ _channelStore.saveChannels(
+ _channels.isNotEmpty ? _channels : _cachedChannels,
+ ),
+ );
+ } else {
+ _appDebugLogService?.info(
+ 'Channel $channelIndex not found in _channels (${_channels.length}) or _cachedChannels (${_cachedChannels.length})',
+ tag: 'Unread',
+ );
+ }
+ }
+
+ void _maybeIncrementContactUnread(Message message) {
+ if (message.isOutgoing || message.isCli) {
+ _appDebugLogService?.info(
+ 'Skip contact unread increment: isOutgoing=${message.isOutgoing}, isCli=${message.isCli}',
+ tag: 'Unread',
+ );
+ return;
+ }
+ final contactKey = message.senderKeyHex;
+ if (!_shouldTrackUnreadForContactKey(contactKey)) {
+ _appDebugLogService?.info(
+ 'Skip contact unread increment: should not track for $contactKey',
+ tag: 'Unread',
+ );
+ return;
+ }
+ // Don't increment if user is viewing this contact
+ if (_activeContactKey == contactKey) {
+ _appDebugLogService?.info(
+ 'Skip contact unread increment: contact $contactKey is active',
+ tag: 'Unread',
+ );
+ return;
+ }
+
+ final currentCount = _contactUnreadCount[contactKey] ?? 0;
+ _contactUnreadCount[contactKey] = currentCount + 1;
+ _appDebugLogService?.info(
+ 'Contact $contactKey unread count incremented to ${currentCount + 1}',
+ tag: 'Unread',
+ );
+ _unreadStore.saveContactUnreadCount(
+ Map.from(_contactUnreadCount),
);
}
@@ -2652,26 +5001,22 @@ class MeshCoreConnector extends ChangeNotifier {
// Parse reaction info
final reactionInfo = Message.parseReaction(message.text);
if (reactionInfo != null) {
- // Check if we've already processed this exact reaction using lightweight key
+ // Check if we've already processed this exact reaction
_processedContactReactions.putIfAbsent(pubKeyHex, () => {});
- final reactionKey = reactionInfo.reactionKey;
- final reactionIdentifier = reactionKey != null
- ? '${reactionKey}_${reactionInfo.emoji}'
- : null;
+ final reactionIdentifier =
+ '${reactionInfo.targetHash}_${reactionInfo.emoji}';
- final isDuplicate =
- reactionIdentifier != null &&
- _processedContactReactions[pubKeyHex]!.contains(reactionIdentifier);
+ final isDuplicate = _processedContactReactions[pubKeyHex]!.contains(
+ reactionIdentifier,
+ );
if (!isDuplicate) {
// New reaction - process it
- _processContactReaction(messages, reactionInfo);
+ _processContactReaction(messages, reactionInfo, pubKeyHex);
_messageStore.saveMessages(pubKeyHex, messages);
// Mark as processed
- if (reactionIdentifier != null) {
- _processedContactReactions[pubKeyHex]!.add(reactionIdentifier);
- }
+ _processedContactReactions[pubKeyHex]!.add(reactionIdentifier);
notifyListeners();
}
@@ -2686,47 +5031,130 @@ class MeshCoreConnector extends ChangeNotifier {
void _processContactReaction(
List messages,
ReactionInfo reactionInfo,
+ String contactPubKeyHex,
) {
- // Find target message by messageId
- for (int i = 0; i < messages.length; i++) {
- if (messages[i].messageId == reactionInfo.targetMessageId) {
- final currentReactions = Map.from(messages[i].reactions);
- currentReactions[reactionInfo.emoji] =
- (currentReactions[reactionInfo.emoji] ?? 0) + 1;
+ final contact = _contacts.cast().firstWhere(
+ (c) => c?.publicKeyHex == contactPubKeyHex,
+ orElse: () => null,
+ );
+ final isRoomServer = contact?.type == advTypeRoom;
- messages[i] = messages[i].copyWith(reactions: currentReactions);
+ ReactionHelper.applyReaction(
+ messages: messages,
+ reactionInfo: reactionInfo,
+ // Incoming reactions in 1:1: match against outgoing messages only
+ shouldSkip: (msg) => isRoomServer != true && !msg.isOutgoing,
+ getTimestampSecs: (msg) => msg.timestamp.millisecondsSinceEpoch ~/ 1000,
+ getSenderName: (msg) =>
+ _resolveContactSenderName(msg, contact, isRoomServer == true),
+ getMessageText: (msg) => msg.text,
+ getReactions: (msg) => msg.reactions,
+ updateMessage: (i, reactions) {
+ messages[i] = messages[i].copyWith(reactions: reactions);
+ },
+ );
+ }
+
+ void _processOutgoingContactReaction(
+ List messages,
+ ReactionInfo reactionInfo,
+ Contact contact,
+ ) {
+ final isRoomServer = contact.type == advTypeRoom;
+
+ ReactionHelper.applyReaction(
+ messages: messages,
+ reactionInfo: reactionInfo,
+ // Outgoing reactions in 1:1: match against incoming messages
+ shouldSkip: (msg) => !isRoomServer && msg.isOutgoing,
+ getTimestampSecs: (msg) => msg.timestamp.millisecondsSinceEpoch ~/ 1000,
+ getSenderName: (msg) =>
+ _resolveContactSenderName(msg, contact, isRoomServer),
+ getMessageText: (msg) => msg.text,
+ getReactions: (msg) => msg.reactions,
+ updateMessage: (i, reactions) {
+ messages[i] = messages[i].copyWith(reactions: reactions);
+ },
+ );
+ }
+
+ void _setReactionStatus(
+ String pubKeyHex,
+ ReactionInfo reactionInfo,
+ MessageStatus status,
+ ) {
+ final messages = _conversations[pubKeyHex];
+ if (messages == null) return;
+ final contact = _contacts.cast().firstWhere(
+ (c) => c?.publicKeyHex == pubKeyHex,
+ orElse: () => null,
+ );
+ final isRoomServer = contact?.type == advTypeRoom;
+ for (int i = messages.length - 1; i >= 0; i--) {
+ final msg = messages[i];
+ final timestampSecs = msg.timestamp.millisecondsSinceEpoch ~/ 1000;
+ final msgHash = ReactionHelper.computeReactionHash(
+ timestampSecs,
+ _resolveContactSenderName(msg, contact, isRoomServer == true),
+ msg.text,
+ );
+ if (msgHash == reactionInfo.targetHash) {
+ final statuses = Map.from(msg.reactionStatuses);
+ statuses[reactionInfo.emoji] = status;
+ messages[i] = msg.copyWith(reactionStatuses: statuses);
break;
}
}
}
- _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;
+ String? _resolveContactSenderName(
+ Message msg,
+ Contact? contact,
+ bool isRoomServer,
+ ) {
+ if (!isRoomServer) return null;
+ if (!msg.isOutgoing) {
+ final senderContact = _contacts.cast().firstWhere(
+ (c) =>
+ c != null &&
+ _matchesPrefix(c.publicKey, msg.fourByteRoomContactKey),
+ orElse: () => null,
+ );
+ return senderContact?.name;
}
- if (raw.length <= index) return null;
- final pathLen = raw[index++];
- if (raw.length < index + pathLen) return null;
- final pathBytes = Uint8List.fromList(raw.sublist(index, index + pathLen));
- index += pathLen;
- if (raw.length <= index) return null;
- final payload = Uint8List.fromList(raw.sublist(index));
+ return selfName;
+ }
- return _RawPacket(
- header: header,
- routeType: routeType,
- payloadType: (header >> _phTypeShift) & _phTypeMask,
- payloadVer: (header >> _phVerShift) & _phVerMask,
- pathBytes: pathBytes,
- payload: payload,
- );
+ _RawPacket? _parseRawPacket(Uint8List raw) {
+ try {
+ final reader = BufferReader(raw);
+ final header = reader.readByte();
+ final routeType = header & _phRouteMask;
+ final hasTransport =
+ routeType == _routeTransportFlood ||
+ routeType == _routeTransportDirect;
+ if (hasTransport) {
+ // Skip reserved bytes in transport header made up of two u16 fields
+ reader.skipBytes(4);
+ }
+ 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,
+ );
+ } catch (e) {
+ appLogger.warn('Error parsing raw packet: $e');
+ return null;
+ }
}
int _computeChannelHash(Uint8List psk) {
@@ -2734,6 +5162,37 @@ class MeshCoreConnector extends ChangeNotifier {
return digest[0];
}
+ /// Firmware-compatible packet hash: SHA256(payloadType + payload) -> first 8 bytes as hex.
+ String _computePacketHash(int payloadType, Uint8List payload) {
+ final input = Uint8List(1 + payload.length);
+ input[0] = payloadType;
+ input.setRange(1, input.length, payload);
+ final digest = crypto.sha256.convert(input).bytes;
+ return digest
+ .sublist(0, 8)
+ .map((b) => b.toRadixString(16).padLeft(2, '0'))
+ .join();
+ }
+
+ /// Content-based dedup hash for sync queue messages (no raw payload available).
+ /// Prefixed with 'c:' to avoid collisions with packet hashes.
+ String _computeContentHash(
+ int channelIdx,
+ int timestampSecs,
+ String fullText,
+ ) {
+ final textBytes = utf8.encode(fullText);
+ final input = Uint8List(5 + textBytes.length);
+ input[0] = channelIdx;
+ input[1] = timestampSecs & 0xFF;
+ input[2] = (timestampSecs >> 8) & 0xFF;
+ input[3] = (timestampSecs >> 16) & 0xFF;
+ input[4] = (timestampSecs >> 24) & 0xFF;
+ input.setRange(5, 5 + textBytes.length, textBytes);
+ final digest = crypto.sha256.convert(input).bytes;
+ return 'c:${digest.sublist(0, 8).map((b) => b.toRadixString(16).padLeft(2, '0')).join()}';
+ }
+
Uint8List? _decryptPayload(Uint8List psk, Uint8List encrypted) {
if (encrypted.length <= _cipherMacSize) return null;
final mac = encrypted.sublist(0, _cipherMacSize);
@@ -2781,63 +5240,6 @@ class MeshCoreConnector extends ChangeNotifier {
return _ParsedText(senderName: 'Unknown', text: text);
}
- Uint8List _resolveOutgoingPathBytes(
- Contact contact,
- PathSelection? selection,
- ) {
- // Priority 1: Check user's path override
- if (contact.pathOverride != null) {
- if (contact.pathOverride! < 0) {
- return Uint8List(0); // Force flood
- }
- return contact.pathOverrideBytes ?? Uint8List(0);
- }
-
- // Priority 2: Check device flood mode or PathSelection flood
- if (contact.pathLength < 0 || selection?.useFlood == true) {
- return Uint8List(0);
- }
-
- // Priority 3: Check PathSelection (auto-rotation)
- if (selection != null && selection.pathBytes.isNotEmpty) {
- return Uint8List.fromList(selection.pathBytes);
- }
-
- // Priority 4: Use device's discovered path
- return contact.path;
- }
-
- int? _resolveOutgoingPathLength(Contact contact, PathSelection? selection) {
- // Priority 1: Check user's path override
- if (contact.pathOverride != null) {
- return contact.pathOverride;
- }
-
- // Priority 2: Check device flood mode or PathSelection flood
- if (contact.pathLength < 0 || selection?.useFlood == true) {
- return -1;
- }
-
- // Priority 3: Check PathSelection (auto-rotation)
- if (selection != null && selection.pathBytes.isNotEmpty) {
- return selection.hopCount;
- }
-
- // Priority 4: Use device's discovered path
- return contact.pathLength;
- }
-
- PathSelection _selectionFromPath(int pathLength, Uint8List pathBytes) {
- if (pathLength < 0) {
- return const PathSelection(pathBytes: [], hopCount: -1, useFlood: true);
- }
- return PathSelection(
- pathBytes: pathBytes,
- hopCount: pathLength,
- useFlood: false,
- );
- }
-
bool _addChannelMessage(int channelIndex, ChannelMessage message) {
_channelMessages.putIfAbsent(channelIndex, () => []);
final messages = _channelMessages[channelIndex]!;
@@ -2845,18 +5247,14 @@ class MeshCoreConnector extends ChangeNotifier {
// Parse reaction info
final reactionInfo = ChannelMessage.parseReaction(message.text);
if (reactionInfo != null) {
- // Check if we've already processed this exact reaction using lightweight key
+ // Check if we've already processed this exact reaction
_processedChannelReactions.putIfAbsent(channelIndex, () => {});
- final reactionKey = reactionInfo.reactionKey;
- final reactionIdentifier = reactionKey != null
- ? '${reactionKey}_${reactionInfo.emoji}'
- : null;
+ final reactionIdentifier =
+ '${reactionInfo.targetHash}_${reactionInfo.emoji}';
- final isDuplicate =
- reactionIdentifier != null &&
- _processedChannelReactions[channelIndex]!.contains(
- reactionIdentifier,
- );
+ final isDuplicate = _processedChannelReactions[channelIndex]!.contains(
+ reactionIdentifier,
+ );
if (!isDuplicate) {
// New reaction - process it
@@ -2865,9 +5263,7 @@ class MeshCoreConnector extends ChangeNotifier {
_channelMessageStore.saveChannelMessages(channelIndex, messages);
// Mark as processed
- if (reactionIdentifier != null) {
- _processedChannelReactions[channelIndex]!.add(reactionIdentifier);
- }
+ _processedChannelReactions[channelIndex]!.add(reactionIdentifier);
}
return false; // Don't add reaction as a visible message
}
@@ -2889,6 +5285,11 @@ class MeshCoreConnector extends ChangeNotifier {
senderKey: message.senderKey,
senderName: message.senderName,
text: replyInfo.actualMessage,
+ originalText: message.originalText,
+ translatedText: message.translatedText,
+ translatedLanguageCode: message.translatedLanguageCode,
+ translationStatus: message.translationStatus,
+ translationModelId: message.translationModelId,
timestamp: message.timestamp,
isOutgoing: message.isOutgoing,
status: message.status,
@@ -2925,18 +5326,23 @@ class MeshCoreConnector extends ChangeNotifier {
mergedPathBytes.length,
);
final newRepeatCount = existing.repeatCount + 1;
+ final promotedFromPending =
+ newRepeatCount == 1 &&
+ existing.status == ChannelMessageStatus.pending;
messages[existingIndex] = existing.copyWith(
repeatCount: newRepeatCount,
pathLength: mergedPathLength,
pathBytes: mergedPathBytes,
pathVariants: mergedPathVariants,
+ packetHash: existing.packetHash ?? processedMessage.packetHash,
// Mark as sent when first repeat is heard
- status:
- newRepeatCount == 1 &&
- existing.status == ChannelMessageStatus.pending
+ status: promotedFromPending
? ChannelMessageStatus.sent
: existing.status,
);
+ if (promotedFromPending) {
+ _pendingChannelSentQueue.remove(existing.messageId);
+ }
} else {
messages.add(processedMessage);
}
@@ -2963,27 +5369,38 @@ class MeshCoreConnector extends ChangeNotifier {
List messages,
ReactionInfo reactionInfo,
) {
- // Find target message by messageId
- for (int i = 0; i < messages.length; i++) {
- if (messages[i].messageId == reactionInfo.targetMessageId) {
- final currentReactions = Map.from(messages[i].reactions);
- currentReactions[reactionInfo.emoji] =
- (currentReactions[reactionInfo.emoji] ?? 0) + 1;
-
- messages[i] = messages[i].copyWith(reactions: currentReactions);
+ ReactionHelper.applyReaction(
+ messages: messages,
+ reactionInfo: reactionInfo,
+ shouldSkip: (_) => false,
+ getTimestampSecs: (msg) => msg.timestamp.millisecondsSinceEpoch ~/ 1000,
+ getSenderName: (msg) => msg.senderName,
+ getMessageText: (msg) => msg.text,
+ getReactions: (msg) => msg.reactions,
+ updateMessage: (i, reactions) {
+ messages[i] = messages[i].copyWith(reactions: reactions);
notifyListeners();
- break;
- }
- }
+ },
+ );
}
int _findChannelRepeatIndex(
List messages,
ChannelMessage incoming,
) {
+ // First pass: match by packet hash (exact dedup)
+ final incomingHash = incoming.packetHash;
+ if (incomingHash != null) {
+ for (int i = messages.length - 1; i >= 0; i--) {
+ final existingHash = messages[i].packetHash;
+ if (existingHash != null && existingHash == incomingHash) {
+ return i;
+ }
+ }
+ }
+ // Second pass: heuristic fallback (outgoing echo, old messages without hash)
for (int i = messages.length - 1; i >= 0; i--) {
- final existing = messages[i];
- if (_isChannelRepeat(existing, incoming)) {
+ if (_isChannelRepeat(messages[i], incoming)) {
return i;
}
}
@@ -2997,7 +5414,7 @@ class MeshCoreConnector extends ChangeNotifier {
(existing.timestamp.millisecondsSinceEpoch -
incoming.timestamp.millisecondsSinceEpoch)
.abs();
- if (diffMs > 5000) return false;
+ if (diffMs > 30000) return false;
if (existing.senderName == incoming.senderName) return true;
@@ -3012,28 +5429,19 @@ class MeshCoreConnector extends ChangeNotifier {
}
bool _shouldDropSelfChannelMessage(String senderName, Uint8List pathBytes) {
- final selfKey = _selfPublicKey;
- if (selfKey == null) return false;
- if (pathBytes.length < pathHashSize) return false;
final trimmed = senderName.trim();
if (trimmed.isEmpty) return false;
+
final selfName = _selfName?.trim();
if (selfName == null || selfName.isEmpty) return false;
+
+ // If sender name doesn't match, keep the message
if (trimmed != selfName) return false;
- final prefix = selfKey.sublist(0, pathHashSize);
- for (int i = 0; i + pathHashSize <= pathBytes.length; i += pathHashSize) {
- var match = true;
- for (int j = 0; j < pathHashSize; j++) {
- if (pathBytes[i + j] != prefix[j]) {
- match = false;
- break;
- }
- }
- if (match) {
- return true;
- }
- }
- return false;
+
+ // Name matches - this is from self
+ // Drop only if pathBytes is empty (direct broadcast)
+ // Keep if pathBytes has data (repeated through another node)
+ return pathBytes.isEmpty;
}
Uint8List _selectPreferredPathBytes(Uint8List existing, Uint8List incoming) {
@@ -3087,9 +5495,13 @@ class MeshCoreConnector extends ChangeNotifier {
}
void _handleDisconnection() {
- // Disable wake lock when connection is lost
- WakelockPlus.disable();
_stopBatteryPolling();
+ _stopRadioStatsPolling();
+ _latestRadioStats = null;
+ radioStatsNotifier.value = null;
+ _prevTotalAirSecs = 0;
+ _airtimeBumpStopwatch?.stop();
+ _airtimeBumpStopwatch = null;
for (final entry in _pendingRepeaterAcks.values) {
entry.timeout?.cancel();
@@ -3106,17 +5518,46 @@ class MeshCoreConnector extends ChangeNotifier {
_txCharacteristic = null;
// Preserve deviceId and displayName for UI display during reconnection
// They're only cleared on manual disconnect via disconnect() method
+ _hasReceivedDeviceInfo = false;
+ _pendingInitialChannelSync = false;
+ _pendingInitialContactsSync = false;
_maxContacts = _defaultMaxContacts;
_maxChannels = _defaultMaxChannels;
_isSyncingQueuedMessages = false;
_queuedMessageSyncInFlight = false;
_isSyncingChannels = false;
_channelSyncInFlight = false;
+ _pendingChannelSentQueue.clear();
+ _pendingGenericAckQueue.clear();
+ _reactionSendQueueSequence = 0;
_setState(MeshCoreConnectionState.disconnected);
_scheduleReconnect();
}
+ void _trackPendingGenericAck(
+ Uint8List data, {
+ String? channelSendQueueId,
+ required bool expectsGenericAck,
+ }) {
+ if (!expectsGenericAck || data.isEmpty) return;
+ _pendingGenericAckQueue.add(
+ _PendingCommandAck(
+ commandCode: data[0],
+ channelSendQueueId: channelSendQueueId,
+ ),
+ );
+ }
+
+ String _nextReactionSendQueueId() {
+ _reactionSendQueueSequence++;
+ return '$_reactionSendQueuePrefix$_reactionSendQueueSequence';
+ }
+
+ bool _isReactionSendQueueId(String queueId) {
+ return queueId.startsWith(_reactionSendQueuePrefix);
+ }
+
Map _parseKeyValueString(String input) {
final result = {};
@@ -3142,7 +5583,11 @@ class MeshCoreConnector extends ChangeNotifier {
void _handleCustomVars(Uint8List frame) {
final buf = BufferReader(frame.sublist(1));
- _currentCustomVars = _parseKeyValueString(buf.readString());
+ try {
+ _currentCustomVars = _parseKeyValueString(buf.readCString());
+ } catch (e) {
+ appLogger.warn('Malformed custom vars frame: $e', tag: 'Connector');
+ }
}
void _setState(MeshCoreConnectionState newState) {
@@ -3152,20 +5597,465 @@ class MeshCoreConnector extends ChangeNotifier {
}
}
+ void markNotifyDirty() {
+ if (_notifyListenersDirty && _notifyListenersTimer != null) {
+ return;
+ }
+
+ _notifyListenersDirty = true;
+ _notifyListenersTimer ??= Timer(
+ _notifyListenersDebounce,
+ _flushBatchedNotify,
+ );
+ }
+
+ void _flushBatchedNotify() {
+ _notifyListenersTimer = null;
+ if (!_notifyListenersDirty) {
+ return;
+ }
+
+ _notifyListenersDirty = false;
+ super.notifyListeners();
+
+ if (_notifyListenersDirty && _notifyListenersTimer == null) {
+ _notifyListenersTimer = Timer(
+ _notifyListenersDebounce,
+ _flushBatchedNotify,
+ );
+ }
+ }
+
+ @override
+ void notifyListeners() {
+ markNotifyDirty();
+ }
+
@override
void dispose() {
_scanSubscription?.cancel();
_connectionSubscription?.cancel();
+ _usbFrameSubscription?.cancel();
_notifySubscription?.cancel();
+ _notifyListenersTimer?.cancel();
_reconnectTimer?.cancel();
_batteryPollTimer?.cancel();
+ _radioStatsPollTimer?.cancel();
+ radioStatsNotifier.dispose();
_receivedFramesController.close();
+ _usbManager.dispose();
+ _tcpConnector.dispose();
// Flush pending unread writes before disposal
_unreadStore.flush();
super.dispose();
}
+
+ void _handleRxData(Uint8List frame) {
+ final packet = BufferReader(frame);
+ try {
+ packet.skipBytes(1); // Skip frame type byte
+ final snr = packet.readInt8() / 4.0;
+ packet.skipBytes(1); // Skip RSSI byte
+ //final rssi = packet.readByte();
+ final header = packet.readByte();
+ final routeType = header & 0x03;
+ final payloadType = (header >> 2) & 0x0F;
+ if (routeType == _routeTransportFlood ||
+ routeType == _routeTransportDirect) {
+ packet.skipBytes(4); // Skip transport-specific bytes
+ }
+ //final payloadVer = (header >> 6) & 0x03;
+ final pathLenRaw = packet.readByte();
+ final pathByteLen = _decodePathByteLen(pathLenRaw);
+ final pathBytes = packet.readBytes(pathByteLen);
+ final payload = packet.readBytes(packet.remaining);
+
+ final rawPacket = frame.sublist(3);
+ switch (payloadType) {
+ case payloadTypeADVERT:
+ _handlePayloadAdvertReceived(
+ rawPacket,
+ payload,
+ pathBytes,
+ routeType,
+ snr,
+ );
+ break;
+ default:
+ }
+ } catch (e) {
+ appLogger.warn('Malformed RX frame: $e', tag: 'Connector');
+ return;
+ }
+ }
+
+ void importContact(Uint8List frame) {
+ final packet = BufferReader(frame);
+ int payloadType = 0;
+ Uint8List pathBytes = Uint8List(0);
+ try {
+ packet.skipBytes(1); // Skip frame type byte
+ packet.skipBytes(1); // Skip SNR byte
+ packet.skipBytes(1); // Skip RSSI byte
+ final header = packet.readByte();
+ final routeType = header & 0x03;
+ payloadType = (header >> 2) & 0x0F;
+ if (routeType == _routeTransportFlood ||
+ routeType == _routeTransportDirect) {
+ packet.skipBytes(4); // Skip transport-specific bytes
+ }
+ //final payloadVer = (header >> 6) & 0x03;
+ final pathLenRaw = packet.readByte();
+ final pathByteLen = _decodePathByteLen(pathLenRaw);
+ pathBytes = packet.readBytes(pathByteLen);
+ } catch (e) {
+ appLogger.warn('Malformed RX frame: $e', tag: 'Connector');
+ return;
+ }
+ double? latitude;
+ double? longitude;
+ String name = '';
+ Uint8List publicKey = Uint8List(0);
+ int type = 0;
+ int timestamp = 0;
+ bool hasLocation = false;
+ bool hasName = false;
+ if (payloadType != payloadTypeADVERT) {
+ appLogger.warn('Unexpected payload type: $payloadType', tag: 'Connector');
+ return;
+ }
+ try {
+ publicKey = packet.readBytes(32);
+ timestamp = packet.readInt32LE();
+ //TODO add signature verification
+ packet.skipBytes(64); // Skip signature for now
+ final flags = packet.readByte();
+ type = flags & 0x0F;
+ hasLocation = (flags & 0x10) != 0;
+ // For future use:
+ //final hasFeature1 = (flags & 0x20) != 0;
+ //final hasFeature2 = (flags & 0x40) != 0;
+ hasName = (flags & 0x80) != 0;
+ if (hasLocation && packet.remaining >= 8) {
+ latitude = packet.readInt32LE() / 1e6;
+ longitude = packet.readInt32LE() / 1e6;
+ }
+ if (hasName && packet.remaining > 0) {
+ name = packet.readCString();
+ }
+ } catch (e) {
+ appLogger.warn('Malformed advert frame: $e', tag: 'Connector');
+ return;
+ }
+
+ importDiscoveredContact(
+ Contact(
+ rawPacket: frame,
+ publicKey: publicKey,
+ name: name,
+ type: type,
+ pathLength: pathBytes.isEmpty ? -1 : pathBytes.length,
+ path: Uint8List.fromList(
+ pathBytes.reversed.toList(),
+ ), // Store path in reverse for easier use in outgoing messages
+ latitude: latitude,
+ longitude: longitude,
+ lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
+ ),
+ );
+ }
+
+ bool hasValidLocation(double? latitude, double? longitude) {
+ const double epsilon = 1e-6;
+ final lat = latitude ?? 0.0;
+ final lon = longitude ?? 0.0;
+ return (lat.abs() > epsilon || lon.abs() > epsilon) &&
+ lat >= -90.0 &&
+ lat <= 90.0 &&
+ lon >= -180.0 &&
+ lon <= 180.0;
+ }
+
+ void _handlePayloadAdvertReceived(
+ Uint8List rawPacket,
+ Uint8List payload,
+ Uint8List path,
+ int routeType,
+ double snr,
+ ) {
+ final advert = BufferReader(payload);
+ double? latitude;
+ double? longitude;
+ String name = '';
+ String contactKeyHex = '';
+ Uint8List publicKey = Uint8List(0);
+ int type = 0;
+ int timestamp = 0;
+ bool hasLocation = false;
+ bool hasName = false;
+ try {
+ publicKey = advert.readBytes(32);
+ contactKeyHex = publicKey
+ .map((b) => b.toRadixString(16).padLeft(2, '0'))
+ .join();
+
+ timestamp = advert.readInt32LE();
+ //TODO add signature verification
+ advert.skipBytes(64); // Skip signature for now
+ final flags = advert.readByte();
+ type = flags & 0x0F;
+ hasLocation = (flags & 0x10) != 0;
+ // For future use:
+ //final hasFeature1 = (flags & 0x20) != 0;
+ //final hasFeature2 = (flags & 0x40) != 0;
+ hasName = (flags & 0x80) != 0;
+ if (hasLocation && advert.remaining >= 8) {
+ latitude = advert.readInt32LE() / 1e6;
+ longitude = advert.readInt32LE() / 1e6;
+ }
+ // Validate location values if present
+ hasLocation = hasValidLocation(latitude, longitude);
+
+ if (hasName && advert.remaining > 0) {
+ name = advert.readCString();
+ }
+ } catch (e) {
+ appLogger.warn('Malformed advert frame: $e', tag: 'Connector');
+ return;
+ }
+
+ //We ignore our own adverts
+ if (listEquals(publicKey, _selfPublicKey)) {
+ return;
+ }
+
+ // Check if this is a new contact
+ final isNewContact = !_knownContactKeys.contains(contactKeyHex);
+
+ if (isNewContact) {
+ final newContact = Contact(
+ rawPacket: rawPacket,
+ publicKey: publicKey,
+ name: name,
+ type: type,
+ pathLength: path.length,
+ path: Uint8List.fromList(
+ path.reversed.toList(),
+ ), // Store path in reverse for easier use in outgoing messages
+ latitude: latitude,
+ longitude: longitude,
+ lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
+ );
+ if ((_autoAddUsers && type == advTypeChat) ||
+ (_autoAddRepeaters && type == advTypeRepeater) ||
+ (_autoAddRoomServers && type == advTypeRoom) ||
+ (_autoAddSensors && type == advTypeSensor)) {
+ _handleContactAdvert(newContact);
+ _handleDiscovery(
+ newContact,
+ rawPacket,
+ noNotify: true,
+ addActive: true,
+ );
+ } else {
+ _handleDiscovery(newContact, rawPacket);
+ }
+ _updateDirectRepeater(newContact, snr, path);
+ return;
+ }
+
+ final existingIndex = _contacts.indexWhere(
+ (c) => c.publicKeyHex == contactKeyHex,
+ );
+
+ if (existingIndex >= 0) {
+ final existing = _contacts[existingIndex];
+ final mergedLastMessageAt = existing.lastMessageAt.isAfter(DateTime.now())
+ ? DateTime.now()
+ : existing.lastMessageAt;
+
+ appLogger.info(
+ 'Refreshing contact ${existing.name}: devicePath=${existing.pathLength}, existingOverride=${existing.pathOverride}',
+ tag: 'Connector',
+ );
+
+ // CRITICAL: Preserve user's path override when contact is refreshed from device
+ _contacts[existingIndex] = existing.copyWith(
+ latitude: hasLocation ? latitude : existing.latitude,
+ longitude: hasLocation ? longitude : existing.longitude,
+ name: hasName ? name : existing.name,
+ path: Uint8List.fromList(path.reversed.toList()),
+ pathLength: path.length,
+ lastMessageAt: mergedLastMessageAt,
+ lastSeen: DateTime.fromMillisecondsSinceEpoch(timestamp * 1000),
+ pathOverride: existing.pathOverride, // Preserve user's path choice
+ pathOverrideBytes: existing.pathOverrideBytes,
+ );
+
+ // Add path to history if we have a valid path
+ if (_pathHistoryService != null &&
+ _contacts[existingIndex].pathLength >= 0) {
+ _pathHistoryService!.handlePathUpdated(_contacts[existingIndex]);
+ }
+
+ _updateDirectRepeater(_contacts[existingIndex], snr, path);
+
+ appLogger.info(
+ 'After merge: pathOverride=${_contacts[existingIndex].pathOverride}, devicePath=${_contacts[existingIndex].pathLength}',
+ tag: 'Connector',
+ );
+ }
+ }
+
+ void _updateDirectRepeater(Contact contact, double snr, Uint8List path) {
+ final pubkeyFirstByte = path.isNotEmpty
+ ? path.last
+ : contact.publicKey.first;
+
+ _directRepeaters.removeWhere((r) => r.isStale());
+
+ //We can use adverts from chat and sensor nodes, but only if the advert has a path to get the last hop.
+ if ((contact.type == advTypeChat || contact.type == advTypeSensor) &&
+ path.isEmpty) {
+ notifyListeners();
+ return;
+ }
+
+ final isTracked = _directRepeaters.where(
+ (r) => r.pubkeyFirstByte == pubkeyFirstByte,
+ );
+
+ final sortedRepeaters = List.from(_directRepeaters)
+ ..sort((a, b) => b.snr.compareTo(a.snr));
+ final weakestRepeater = sortedRepeaters.isNotEmpty
+ ? sortedRepeaters.last
+ : null;
+
+ if (_directRepeaters.length >= 5 &&
+ weakestRepeater != null &&
+ isTracked.isEmpty) {
+ _directRepeaters.remove(weakestRepeater);
+ }
+
+ if (isTracked.isNotEmpty) {
+ final repeater = isTracked.first;
+ repeater.update(snr);
+ } else if (_directRepeaters.length < 5) {
+ _directRepeaters.add(
+ DirectRepeater(pubkeyFirstByte: pubkeyFirstByte, snr: snr),
+ );
+ }
+ notifyListeners();
+ }
+
+ void _handleAutoAddConfig(Uint8List frame) {
+ final reader = BufferReader(frame);
+ try {
+ reader.skipBytes(1); // Skip the response code byte
+ final flags = reader.readByte();
+ _autoAddUsers = (flags & autoAddChatFlag) != 0;
+ _autoAddRepeaters = (flags & autoAddRepeaterFlag) != 0;
+ _autoAddRoomServers = (flags & autoAddRoomServerFlag) != 0;
+ _autoAddSensors = (flags & autoAddSensorFlag) != 0;
+ _overwriteOldest = (flags & autoAddOverwriteOldestFlag) != 0;
+ } catch (e) {
+ appLogger.error('Failed to parse auto-add config: $e', tag: 'Connector');
+ }
+ }
+
+ void _handleDiscovery(
+ Contact contact,
+ Uint8List rawPacket, {
+ bool noNotify = false,
+ bool addActive = false,
+ }) {
+ appLogger.info('Discovered new contact: ${contact.name}', tag: 'Connector');
+
+ final existingIndex = _discoveredContacts.indexWhere(
+ (c) => c.publicKeyHex == contact.publicKeyHex,
+ );
+
+ // Update existing contact
+ if (existingIndex >= 0) {
+ _discoveredContacts[existingIndex] = _discoveredContacts[existingIndex]
+ .copyWith(
+ rawPacket: rawPacket,
+ name: contact.name,
+ type: contact.type,
+ pathLength: contact.pathLength,
+ path: contact.path,
+ latitude: contact.latitude,
+ longitude: contact.longitude,
+ lastSeen: contact.lastSeen,
+ flags: 0,
+ isActive: addActive,
+ );
+ notifyListeners();
+ unawaited(_persistDiscoveredContacts());
+ return;
+ }
+
+ final disContact = Contact(
+ rawPacket: rawPacket,
+ publicKey: contact.publicKey,
+ name: contact.name,
+ type: contact.type,
+ pathLength: contact.pathLength,
+ path: contact.path,
+ latitude: contact.latitude,
+ longitude: contact.longitude,
+ lastSeen: contact.lastSeen,
+ lastMessageAt: contact.lastMessageAt,
+ isActive: addActive,
+ flags: 0,
+ );
+ _discoveredContacts.add(disContact);
+
+ unawaited(_persistDiscoveredContacts());
+
+ // Show notification for new contact (advertisement)
+ if (_appSettingsService != null && !noNotify) {
+ final settings = _appSettingsService!.settings;
+ if (settings.notificationsEnabled && settings.notifyOnNewAdvert) {
+ _notificationService.showAdvertNotification(
+ contactName: contact.name,
+ contactType: contact.typeLabel,
+ contactId: contact.publicKeyHex,
+ );
+ }
+ }
+ }
+
+ void removeAllDiscoveredContacts() {
+ _discoveredContacts.clear();
+ unawaited(_persistDiscoveredContacts());
+ notifyListeners();
+ }
+
+ void clearMessagesForContact(Contact contact) {
+ final contactKeyHex = contact.publicKeyHex;
+ final messages = _conversations[contactKeyHex];
+ if (messages == null) return;
+ messages.clear();
+ unawaited(_messageStore.saveMessages(contactKeyHex, messages));
+ markContactRead(contactKeyHex);
+ notifyListeners();
+ }
+
+ void clearMessagesForChannel(int channelIndex) {
+ final messages = _channelMessages[channelIndex];
+ if (messages == null) return;
+ messages.clear();
+ unawaited(_channelMessageStore.saveChannelMessages(channelIndex, messages));
+ markChannelRead(channelIndex);
+ notifyListeners();
+ }
+
+ void deleteAllPaths() {
+ _pathHistoryService?.clearAllHistories();
+ }
}
const int _phRouteMask = 0x03;
@@ -3181,11 +6071,20 @@ const int _routeTransportDirect = 0x03;
const int _payloadTypeGroupText = 0x05;
const int _cipherMacSize = 2;
+/// Decodes the firmware's encoded path_len byte into actual byte length.
+/// Bits 0-5: hash count (0-63), Bits 6-7: hash size code (0=1byte, 1=2bytes, 2=3bytes).
+int _decodePathByteLen(int pathLenRaw) {
+ final hashCount = pathLenRaw & 63;
+ final hashSize = ((pathLenRaw >> 6) & 0x03) + 1;
+ return hashCount * hashSize;
+}
+
class _RawPacket {
final int header;
final int routeType;
final int payloadType;
final int payloadVer;
+ final int pathLenRaw;
final Uint8List pathBytes;
final Uint8List payload;
@@ -3194,12 +6093,15 @@ class _RawPacket {
required this.routeType,
required this.payloadType,
required this.payloadVer,
+ required this.pathLenRaw,
required this.pathBytes,
required this.payload,
});
bool get isFlood =>
routeType == _routeFlood || routeType == _routeTransportFlood;
+
+ int get hopCount => pathLenRaw & 63;
}
class _ParsedText {
@@ -3223,3 +6125,10 @@ class _RepeaterAckContext {
required this.messageBytes,
});
}
+
+class _PendingCommandAck {
+ final int commandCode;
+ final String? channelSendQueueId;
+
+ _PendingCommandAck({required this.commandCode, this.channelSendQueueId});
+}
diff --git a/lib/connector/meshcore_connector_tcp.dart b/lib/connector/meshcore_connector_tcp.dart
new file mode 100644
index 0000000..7c93d9f
--- /dev/null
+++ b/lib/connector/meshcore_connector_tcp.dart
@@ -0,0 +1,70 @@
+import 'dart:async';
+import 'dart:typed_data';
+
+import '../services/app_debug_log_service.dart';
+import '../services/tcp_transport_service.dart';
+
+/// Manages TCP transport for MeshCore devices.
+///
+/// Owns the [TcpTransportService] and TCP-specific connection state.
+/// The main [MeshCoreConnector] delegates all TCP operations here.
+class MeshCoreTcpConnector {
+ final TcpTransportService _service = TcpTransportService();
+ AppDebugLogService? _debugLog;
+ StreamSubscription? _frameSubscription;
+
+ // --- Getters ---
+ String? get activeEndpoint => _service.activeEndpoint;
+ bool get isConnected => _service.isConnected;
+
+ // --- Configuration ---
+ void setDebugLogService(AppDebugLogService? service) {
+ _debugLog = service;
+ _service.setDebugLogService(service);
+ }
+
+ // --- Connection lifecycle ---
+ Future connect({required String host, required int port}) async {
+ _debugLog?.info('TcpConnector.connect endpoint=$host:$port', tag: 'TCP');
+ await _frameSubscription?.cancel();
+ _frameSubscription = null;
+ await _service.connect(host: host, port: port);
+ _debugLog?.info(
+ 'TcpConnector.connect done, endpoint=${_service.activeEndpoint}',
+ tag: 'TCP',
+ );
+ }
+
+ StreamSubscription listenFrames({
+ required void Function(Uint8List) onFrame,
+ required void Function(Object, StackTrace?) onError,
+ required void Function() onDone,
+ }) {
+ _frameSubscription = _service.frameStream.listen(
+ onFrame,
+ onError: onError,
+ onDone: onDone,
+ );
+ return _frameSubscription!;
+ }
+
+ Future cancelFrameSubscription() async {
+ await _frameSubscription?.cancel();
+ _frameSubscription = null;
+ }
+
+ Future disconnect() async {
+ if (!_service.isConnected && _frameSubscription == null) return;
+ _debugLog?.info('TcpConnector.disconnect', tag: 'TCP');
+ await _frameSubscription?.cancel();
+ _frameSubscription = null;
+ await _service.disconnect();
+ }
+
+ Future write(Uint8List data) => _service.write(data);
+
+ void dispose() {
+ _frameSubscription?.cancel();
+ _service.dispose();
+ }
+}
diff --git a/lib/connector/meshcore_connector_usb.dart b/lib/connector/meshcore_connector_usb.dart
new file mode 100644
index 0000000..56718bc
--- /dev/null
+++ b/lib/connector/meshcore_connector_usb.dart
@@ -0,0 +1,78 @@
+import 'dart:typed_data';
+
+import '../services/app_debug_log_service.dart';
+import '../services/usb_serial_service.dart';
+
+/// Manages USB serial transport for MeshCore devices.
+///
+/// Owns the [UsbSerialService] and USB-specific connection state.
+/// The main [MeshCoreConnector] delegates all USB operations here.
+class MeshCoreUsbManager {
+ MeshCoreUsbManager();
+
+ final UsbSerialService _service = UsbSerialService();
+ AppDebugLogService? _debugLog;
+ String? _activePortKey;
+ String? _activePortLabel;
+
+ // --- Getters ---
+ String? get activePortKey => _activePortKey;
+ String? get activePortDisplayLabel => _activePortLabel ?? _activePortKey;
+ bool get isConnected => _service.isConnected;
+ Stream get frameStream => _service.frameStream;
+
+ // --- Configuration ---
+ Future> listPorts() => _service.listPorts();
+
+ void setRequestPortLabel(String label) => _service.setRequestPortLabel(label);
+
+ void setFallbackDeviceName(String label) =>
+ _service.setFallbackDeviceName(label);
+
+ void setDebugLogService(AppDebugLogService? service) {
+ _debugLog = service;
+ _service.setDebugLogService(service);
+ }
+
+ // --- Connection lifecycle ---
+ Future connect({
+ required String portName,
+ int baudRate = 115200,
+ }) async {
+ _debugLog?.info(
+ 'UsbManager.connect: portName=$portName baud=$baudRate',
+ tag: 'USB',
+ );
+ await _service.connect(portName: portName, baudRate: baudRate);
+ _activePortKey = _service.activePortKey ?? portName;
+ _activePortLabel = _service.activePortDisplayLabel ?? portName;
+ _debugLog?.info(
+ 'UsbManager.connect: done, key=$_activePortKey label=$_activePortLabel',
+ tag: 'USB',
+ );
+ }
+
+ Future disconnect() async {
+ if (!_service.isConnected && _activePortKey == null) {
+ return;
+ }
+ _debugLog?.info('UsbManager.disconnect', tag: 'USB');
+ await _service.disconnect();
+ _activePortKey = null;
+ _activePortLabel = null;
+ }
+
+ Future write(Uint8List data) => _service.write(data);
+
+ Future writeRaw(Uint8List data) => _service.writeRaw(data);
+
+ // --- Label management ---
+ void updateConnectedLabel(String selfName) {
+ _service.updateConnectedLabel(selfName);
+ _activePortLabel = _service.activePortDisplayLabel ?? _activePortLabel;
+ }
+
+ void dispose() {
+ _service.dispose();
+ }
+}
diff --git a/lib/connector/meshcore_protocol.dart b/lib/connector/meshcore_protocol.dart
index 8469d61..396d78b 100644
--- a/lib/connector/meshcore_protocol.dart
+++ b/lib/connector/meshcore_protocol.dart
@@ -1,9 +1,12 @@
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;
+ int _lastPointer = 0;
final Uint8List _buffer;
BufferReader(Uint8List data) : _buffer = Uint8List.fromList(data);
@@ -13,17 +16,31 @@ class BufferReader {
int readByte() => readBytes(1)[0];
Uint8List readBytes(int count) {
+ _lastPointer = _pointer;
+ if (_pointer + count > _buffer.length) {
+ throw RangeError(
+ 'Attempted to read $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}',
+ );
+ }
final data = _buffer.sublist(_pointer, _pointer + count);
_pointer += count;
return data;
}
+ void skipBytes(int count) {
+ _lastPointer = _pointer;
+ if (_pointer + count > _buffer.length) {
+ throw RangeError(
+ 'Attempted to skip $count bytes at offset $_pointer, but only $remaining bytes remaining in buffer of length ${_buffer.length}',
+ );
+ }
+ _pointer += count;
+ }
+
Uint8List readRemainingBytes() => readBytes(remaining);
- String readString() =>
- utf8.decode(readRemainingBytes(), allowMalformed: true);
-
- String readCString(int maxLength) {
+ String readCStringGreedy(int maxLength) {
+ _lastPointer = _pointer;
final value = [];
final bytes = readBytes(maxLength);
for (final byte in bytes) {
@@ -37,6 +54,25 @@ class BufferReader {
}
}
+ String readCString({int maxLength = -1}) {
+ final backupPointer = _pointer;
+ final value = [];
+ int counter = 0;
+ final maxLen = maxLength >= 0 ? maxLength : remaining;
+ while (counter < maxLen) {
+ final byte = readByte();
+ if (byte == 0) break;
+ value.add(byte);
+ counter++;
+ }
+ _lastPointer = backupPointer;
+ try {
+ return utf8.decode(Uint8List.fromList(value), allowMalformed: true);
+ } catch (e) {
+ return String.fromCharCodes(value); // Latin-1 fallback
+ }
+ }
+
int readUInt8() => readBytes(1).buffer.asByteData().getUint8(0);
int readInt8() => readBytes(1).buffer.asByteData().getInt8(0);
int readUInt16LE() =>
@@ -58,6 +94,9 @@ class BufferReader {
if ((value & 0x800000) != 0) value -= 0x1000000;
return value;
}
+
+ void resetPointer() => _pointer = 0;
+ void rewind() => _pointer = _lastPointer;
}
// Buffer Writer - accumulating binary data builder
@@ -98,6 +137,40 @@ class BufferWriter {
}
writeBytes(bytes);
}
+
+ void writeHex(String hex) {
+ writeBytes(hex2Uint8List(hex));
+ }
+
+ void writeBytesPadded(Uint8List bytes, int totalLength) {
+ // Path data (64 bytes, zero-padded)
+ final bytesPadded = Uint8List(totalLength);
+ final len = bytes.length < totalLength ? bytes.length : totalLength;
+ if (bytes.isNotEmpty && len > 0) {
+ final copyLen = bytes.length < totalLength ? bytes.length : totalLength;
+ for (int i = 0; i < copyLen; i++) {
+ bytesPadded[i] = bytes[i];
+ }
+ }
+ writeBytes(bytesPadded);
+ }
+}
+
+Uint8List hex2Uint8List(String hex) {
+ // Validate hex string length is even and not empty
+ if (hex.isEmpty || hex.length % 2 != 0) {
+ throw FormatException('Invalid hex string length: ${hex.length}');
+ }
+ List result = [];
+ for (int i = 0; i < hex.length ~/ 2; i++) {
+ final hexByte = hex.substring(i * 2, i * 2 + 2);
+ final byte = int.tryParse(hexByte, radix: 16);
+ if (byte == null) {
+ throw FormatException('Invalid hex characters at position $i: $hexByte');
+ }
+ result.add(byte);
+ }
+ return Uint8List.fromList(result);
}
// Command codes (to device)
@@ -127,22 +200,29 @@ const int cmdSendStatusReq = 27;
const int cmdGetContactByKey = 30;
const int cmdGetChannel = 31;
const int cmdSetChannel = 32;
-const int cmdGetRadioSettings = 57;
-const int cmdGetTelemetryReq = 39;
+const int cmdSendTracePath = 36;
+const int cmdSetOtherParams = 38;
+const int cmdSendTelemetryReq = 39;
const int cmdGetCustomVar = 40;
const int cmdSetCustomVar = 41;
const int cmdSendBinaryReq = 50;
+const int cmdGetStats = 56;
+const int cmdSendAnonReq = 57;
+const int cmdSetAutoAddConfig = 58;
+const int cmdGetAutoAddConfig = 59;
+const int cmdSetPathHashMode = 61;
// 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;
const int reqTypeKeepAlive = 0x02;
const int reqTypeGetTelemetry = 0x03;
const int reqTypeGetAccessList = 0x05;
-const int reqTypeGetNeighbours = 0x06;
+const int reqTypeGetNeighbors = 0x06;
// Repeater response codes
const int respServerLoginOk = 0;
@@ -159,13 +239,19 @@ const int respCodeContactMsgRecv = 7;
const int respCodeChannelMsgRecv = 8;
const int respCodeCurrTime = 9;
const int respCodeNoMoreMessages = 10;
+const int respCodeExportContact = 11;
const int respCodeBattAndStorage = 12;
const int respCodeDeviceInfo = 13;
const int respCodeContactMsgRecvV3 = 16;
const int respCodeChannelMsgRecvV3 = 17;
const int respCodeChannelInfo = 18;
-const int respCodeRadioSettings = 25;
const int respCodeCustomVars = 21;
+const int respCodeAutoAddConfig = 25;
+const int respCodeStats = 24;
+
+const int statsTypeCore = 0;
+const int statsTypeRadio = 1;
+const int statsTypePackets = 2;
// Push codes (async from device)
const int pushCodeAdvert = 0x80;
@@ -176,6 +262,7 @@ const int pushCodeLoginSuccess = 0x85;
const int pushCodeLoginFail = 0x86;
const int pushCodeStatusResponse = 0x87;
const int pushCodeLogRxData = 0x88;
+const int pushCodeTraceData = 0x89;
const int pushCodeNewAdvert = 0x8A;
const int pushCodeTelemetryResponse = 0x8B;
const int pushCodeBinaryResponse = 0x8C;
@@ -186,8 +273,49 @@ const int advTypeRepeater = 2;
const int advTypeRoom = 3;
const int advTypeSensor = 4;
+const int teleModeDeny = 0;
+const int teleModeAllowFlags = 1; // use contact.flags
+const int teleModeAllowAll = 2;
+
+// Payload Types
+const int payloadTypeREQ =
+ 0x00; // request (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
+const int payloadTypeRESPONSE =
+ 0x01; // response to REQ or ANON_REQ (prefixed with dest/src hashes, MAC) (enc data: timestamp, blob)
+const int payloadTypeTXTMSG =
+ 0x02; // a plain text message (prefixed with dest/src hashes, MAC) (enc data: timestamp, text)
+const int payloadTypeACK = 0x03; // a simple ack
+const int payloadTypeADVERT = 0x04; // a node advertising its Identity
+const int payloadTypeGRPTXT =
+ 0x05; // an (unverified) group text message (prefixed with channel hash, MAC) (enc data: timestamp, "name: msg")
+const int payloadTypeGRPDATA =
+ 0x06; // an (unverified) group datagram (prefixed with channel hash, MAC) (enc data: timestamp, blob)
+const int payloadTypeANONREQ =
+ 0x07; // generic request (prefixed with dest_hash, ephemeral pub_key, MAC) (enc data: ...)
+const int payloadTypePATH =
+ 0x08; // returned path (prefixed with dest/src hashes, MAC) (enc data: path, extra)
+const int payloadTypeTRACE = 0x09; // trace a path, collecting SNI for each hop
+const int payloadTypeMULTIPART = 0x0A; // packet is one of a set of packets
+const int payloadTypeCONTROL = 0x0B; // a control/discovery packet
+//...
+const int payloadTypeRawCustom =
+ 0x0F; // custom packet as raw bytes, for applications with custom encryption, payloads, etc
+
+//auto-add flags
+const int autoAddOverwriteOldestFlag =
+ 1 << 0; // 0x01 - overwrite oldest non-favourite when full
+const int autoAddChatFlag =
+ 1 << 1; // 0x02 - auto-add Chat (Companion) (ADV_TYPE_CHAT)
+const int autoAddRepeaterFlag =
+ 1 << 2; // 0x04 - auto-add Repeater (ADV_TYPE_REPEATER)
+const int autoAddRoomServerFlag =
+ 1 << 3; // 0x08 - auto-add Room Server (ADV_TYPE_ROOM)
+const int autoAddSensorFlag =
+ 1 << 4; // 0x10 - auto-add Sensor (ADV_TYPE_SENSOR)
+
// Sizes
const int pubKeySize = 32;
+const int signatureSize = 64;
const int maxPathSize = 64;
const int pathHashSize = 1;
const int maxNameSize = 32;
@@ -195,8 +323,10 @@ const int maxFrameSize = 172;
const int appProtocolVersion = 3;
// Matches firmware MAX_TEXT_LEN (10 * CIPHER_BLOCK_SIZE).
const int maxTextPayloadBytes = 160;
-const int _sendTextMsgOverheadBytes = 1 + 1 + 1 + 4 + 6 + 1;
-const int _sendChannelTextMsgOverheadBytes = 1 + 1 + 1 + 4 + 1;
+const int _sendTextMsgOverheadBytes =
+ 1 + 1 + 1 + 4 + 6 + 1 + 2; // +2 safety margin
+const int _sendChannelTextMsgOverheadBytes =
+ 1 + 1 + 1 + 4 + 1 + 2; // +2 safety margin
int maxContactMessageBytes() {
final byFrame = maxFrameSize - _sendTextMsgOverheadBytes;
@@ -227,13 +357,17 @@ int _minPositive(int a, int b) {
const int contactPubKeyOffset = 1;
const int contactTypeOffset = 33;
const int contactFlagsOffset = 34;
+const int contactFlagFavorite = 0x01;
+const int contactFlagTeleBase = 0x02; // 'base' permission includes battery
+const int contactFlagTeleLoc = 0x04;
+const int contactFlagTeleEnv = 0x08; //access environment sensors
const int contactPathLenOffset = 35;
const int contactPathOffset = 36;
const int contactNameOffset = 100;
const int contactTimestampOffset = 132;
const int contactLatOffset = 136;
const int contactLonOffset = 140;
-const int contactLastmodOffset = 144;
+const int contactLastModOffset = 144;
const int contactFrameSize = 148;
// Message frame offsets
@@ -245,52 +379,44 @@ 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) {
+
+ final message = BufferReader(frame);
+ try {
+ 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.readCString();
+ if (text.isEmpty) return null;
+
+ return ParsedContactText(senderPrefix: senderPrefix, text: text);
+ } catch (e) {
+ debugPrint('Error parsing contact message text: $e');
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
@@ -313,18 +439,9 @@ int readInt32LE(Uint8List data, int offset) {
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
@@ -384,7 +501,7 @@ Uint8List buildSendTextMsgFrame(
final writer = BufferWriter();
writer.writeByte(cmdSendTxtMsg);
writer.writeByte(txtTypePlain);
- writer.writeByte(attempt.clamp(0, 3));
+ writer.writeByte(attempt.clamp(0, 255));
writer.writeUInt32LE(timestamp);
writer.writeBytes(recipientPubKey.sublist(0, 6));
writer.writeString(text);
@@ -444,6 +561,17 @@ Uint8List buildGetBattAndStorageFrame() {
return Uint8List.fromList([cmdGetBattAndStorage]);
}
+/// Companion radio stats: [56][statsType] where statsType is statsTypeCore/Radio/Packets.
+Uint8List buildGetStatsFrame(int statsType) {
+ return Uint8List.fromList([cmdGetStats, statsType & 0xFF]);
+}
+
+/// Path hash width on air: [61][0][mode], mode 0..2 → (mode+1) bytes per hop hash.
+Uint8List buildSetPathHashModeFrame(int mode) {
+ final m = mode.clamp(0, 2);
+ return Uint8List.fromList([cmdSetPathHashMode, 0, m]);
+}
+
// Build CMD_SET_DEVICE_TIME frame
Uint8List buildSetDeviceTimeFrame(int timestamp) {
final writer = BufferWriter();
@@ -522,18 +650,29 @@ Uint8List buildSetChannelFrame(int channelIndex, String name, Uint8List psk) {
}
// Build CMD_SET_RADIO_PARAMS frame
-// Format: [cmd][freq x4][bw x4][sf][cr]
+// Format: [cmd][freq x4][bw x4][sf][cr] (pre-v9)
+// [cmd][freq x4][bw x4][sf][cr][repeat] (firmware v9+)
// freq: frequency in Hz (300000-2500000)
// bw: bandwidth in Hz (7000-500000)
// sf: spreading factor (5-12)
// cr: coding rate (5-8)
-Uint8List buildSetRadioParamsFrame(int freqHz, int bwHz, int sf, int cr) {
+// clientRepeat: enable off-grid packet repeat (firmware v9+, omit for older)
+Uint8List buildSetRadioParamsFrame(
+ int freqHz,
+ int bwHz,
+ int sf,
+ int cr, {
+ bool? clientRepeat,
+}) {
final writer = BufferWriter();
writer.writeByte(cmdSetRadioParams);
writer.writeUInt32LE(freqHz);
writer.writeUInt32LE(bwHz);
writer.writeByte(sf);
writer.writeByte(cr);
+ if (clientRepeat != null) {
+ writer.writeByte(clientRepeat ? 1 : 0);
+ }
return writer.toBytes();
}
@@ -553,14 +692,17 @@ Uint8List buildResetPathFrame(Uint8List pubKey) {
}
// Build CMD_ADD_UPDATE_CONTACT frame to set custom path
-// Format: [cmd][pub_key x32][type][flags][path_len][path x64][name x32][timestamp x4]
+// Format: [cmd][pub_key x32][type][flags][path_len][path x64][name x32][Lat? x4, Lon? x4][timestamp? x4]
Uint8List buildUpdateContactPathFrame(
Uint8List pubKey,
- Uint8List customPath,
+ Uint8List path,
int pathLen, {
int type = 1, // ADV_TYPE_CHAT
int flags = 0,
String name = '',
+ double? lat,
+ double? lon,
+ DateTime? lastModified,
}) {
final writer = BufferWriter();
writer.writeByte(cmdAddUpdateContact);
@@ -569,17 +711,7 @@ Uint8List buildUpdateContactPathFrame(
writer.writeByte(flags);
writer.writeByte(pathLen);
- // Path data (64 bytes, zero-padded)
- final pathPadded = Uint8List(maxPathSize);
- if (customPath.isNotEmpty && pathLen > 0) {
- final copyLen = customPath.length < maxPathSize
- ? customPath.length
- : maxPathSize;
- for (int i = 0; i < copyLen; i++) {
- pathPadded[i] = customPath[i];
- }
- }
- writer.writeBytes(pathPadded);
+ writer.writeBytesPadded(path, maxPathSize);
// Name (32 bytes, null-padded)
writer.writeCString(name, maxNameSize);
@@ -588,6 +720,27 @@ Uint8List buildUpdateContactPathFrame(
final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;
writer.writeUInt32LE(timestamp);
+ if ((lat == null || lon == null) && lastModified != null) {
+ // If lat/lon not provided, write zeros
+ writer.writeInt32LE(0);
+ writer.writeInt32LE(0);
+ } else {
+ // Latitude and Longitude are expected in degrees, convert to int by multiplying by 1e6
+ // Latitude
+ final latitude = lat ?? 0.0;
+ writer.writeInt32LE((latitude * 1e6).round());
+
+ // Longitude
+ final longitude = lon ?? 0.0;
+ writer.writeInt32LE((longitude * 1e6).round());
+ }
+
+ if (lastModified != null) {
+ // Last modified
+ final lastModifiedTimestamp = lastModified.millisecondsSinceEpoch ~/ 1000;
+ writer.writeUInt32LE(lastModifiedTimestamp);
+ }
+
return writer.toBytes();
}
@@ -600,16 +753,15 @@ Uint8List buildGetContactByKeyFrame(Uint8List pubKey) {
return writer.toBytes();
}
-// Build CMD_GET_RADIO_SETTINGS frame
-Uint8List buildGetRadioSettingsFrame() {
- return Uint8List.fromList([cmdGetRadioSettings]);
-}
-
//Build CMD_GET_CUSTOM_VARS frame
Uint8List buildGetCustomVarsFrame() {
return Uint8List.fromList([cmdGetCustomVar]);
}
+Uint8List buildGetAutoAddFlagsFrame() {
+ return Uint8List.fromList([cmdGetAutoAddConfig]);
+}
+
// Calculate LoRa airtime for a packet
// Based on Semtech SX127x datasheet formula
// Returns airtime in milliseconds
@@ -689,7 +841,7 @@ Uint8List buildSendCliCommandFrame(
final writer = BufferWriter();
writer.writeByte(cmdSendTxtMsg);
writer.writeByte(txtTypeCliData);
- writer.writeByte(attempt.clamp(0, 3));
+ writer.writeByte(attempt.clamp(0, 255));
writer.writeUInt32LE(timestamp);
writer.writeBytes(repeaterPubKey.sublist(0, 6));
writer.writeString(command);
@@ -708,3 +860,98 @@ Uint8List buildSendBinaryReq(Uint8List repeaterPubKey, {Uint8List? payload}) {
}
return writer.toBytes();
}
+
+//Build a trace request frame
+//[cmd][tag x4][auth x4][flag][payload]
+Uint8List buildTraceReq(int tag, int auth, int flag, {Uint8List? payload}) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdSendTracePath);
+ writer.writeUInt32LE(tag);
+ writer.writeUInt32LE(auth);
+ writer.writeByte(flag);
+ if (payload != null && payload.isNotEmpty) {
+ writer.writeBytes(payload);
+ }
+ return writer.toBytes();
+}
+
+// Build a export contact frame
+// [cmd][pub_key x32 / if empty exports your contact info]
+Uint8List buildExportContactFrame(Uint8List pubKey) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdExportContact);
+ writer.writeBytes(pubKey);
+ return writer.toBytes();
+}
+
+// Build a import contact frame
+// [cmd][contact_frame x98+]
+Uint8List buildImportContactFrame(Uint8List contactFrame) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdImportContact);
+ writer.writeBytes(contactFrame);
+ return writer.toBytes();
+}
+
+// Build a export contact frame
+// [cmd][pub_key x32]
+Uint8List buildZeroHopContact(Uint8List pubKey) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdShareContact);
+ writer.writeBytes(pubKey);
+ return writer.toBytes();
+}
+
+// Build CMD_SET_OTHER_PARAMS frame
+// Format: [cmd][allowTelemetryFlags][advertLocationPolicy][multiAcks]
+Uint8List buildSetOtherParamsFrame(
+ int allowTelemetryFlags,
+ int advertLocationPolicy,
+ int multiAcks,
+) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdSetOtherParams);
+ //Going forward the app will just set Auto Add Contacts to disabled, and use the filter flags
+ //Allow Auto Add Contacts use inverted logic (0x01 = disabled, 0x00 = enabled).
+ writer.writeByte(0x01);
+ writer.writeByte(allowTelemetryFlags); // Allow Telemetry Flags
+ writer.writeByte(advertLocationPolicy); // Advertisement Location Policy
+ writer.writeByte(multiAcks); // Multi Acknowledgements
+ return writer.toBytes();
+}
+
+// Build CMD_SET_AUTO_ADD_CONFIG frame
+// Format: [cmd][flags]
+Uint8List buildSetAutoAddConfigFrame({
+ required bool autoAddChat,
+ required bool autoAddRepeater,
+ required bool autoAddRoomServer,
+ required bool autoAddSensor,
+ required bool overwriteOldest,
+}) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdSetAutoAddConfig);
+ int flags = 0;
+ if (autoAddChat) flags |= autoAddChatFlag;
+ if (autoAddRepeater) flags |= autoAddRepeaterFlag;
+ if (autoAddRoomServer) flags |= autoAddRoomServerFlag;
+ if (autoAddSensor) flags |= autoAddSensorFlag;
+ if (overwriteOldest) flags |= autoAddOverwriteOldestFlag;
+ writer.writeByte(flags);
+ return writer.toBytes();
+}
+
+//Build CMD_SEND_TELEMETRY_REQ
+// Format: [cmd][reserved x3][pub_key? x32]
+Uint8List buildSendTelemetryReq(Uint8List? pubKey) {
+ final writer = BufferWriter();
+ writer.writeByte(cmdSendTelemetryReq);
+
+ if (pubKey != null && pubKey.length == pubKeySize) {
+ writer.writeBytes(Uint8List(3)); // reserved bytes
+ writer.writeBytes(pubKey);
+ } else {
+ writer.writeBytes(Uint8List(4)); // reserved bytes
+ }
+ return writer.toBytes();
+}
diff --git a/lib/connector/meshcore_uuids.dart b/lib/connector/meshcore_uuids.dart
new file mode 100644
index 0000000..ae6697b
--- /dev/null
+++ b/lib/connector/meshcore_uuids.dart
@@ -0,0 +1,15 @@
+class MeshCoreUuids {
+ static const String service = "6e400001-b5a3-f393-e0a9-e50e24dcca9e";
+ static const String rxCharacteristic = "6e400002-b5a3-f393-e0a9-e50e24dcca9e";
+ static const String txCharacteristic = "6e400003-b5a3-f393-e0a9-e50e24dcca9e";
+
+ static const List deviceNamePrefixes = [
+ "MeshCore-",
+ "Whisper-",
+ "WisCore-",
+ "Seeed",
+ "Lilygo",
+ "HT-",
+ "LowMesh_MC_",
+ ];
+}
diff --git a/lib/helpers/cayenne_lpp.dart b/lib/helpers/cayenne_lpp.dart
index ad5aa8c..07909e6 100644
--- a/lib/helpers/cayenne_lpp.dart
+++ b/lib/helpers/cayenne_lpp.dart
@@ -1,4 +1,6 @@
import 'dart:typed_data';
+import 'package:meshcore_open/utils/app_logger.dart';
+
import '../connector/meshcore_protocol.dart';
class CayenneLpp {
@@ -26,9 +28,11 @@ class CayenneLpp {
static const int lppUnixTime = 133; // 4 bytes, unsigned
static const int lppGyrometer = 134; // 2 bytes per axis, 0.01 °/s
static const int lppColour = 135; // 1 byte per RGB Color
- static const int lppGps = 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
+ static const int lppGps =
+ 136; // 3 byte lon/lat 0.0001 °, 3 bytes alt 0.01 meter
static const int lppSwitch = 142; // 1 byte, 0/1
- static const int lppPolyline = 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
+ static const int lppPolyline =
+ 240; // 1 byte size, 1 byte delta factor, 3 byte lon/lat 0.0001° * factor, n (size-8) bytes deltas
final BufferWriter _writer = BufferWriter();
@@ -82,180 +86,192 @@ class CayenneLpp {
static List> parse(Uint8List bytes) {
final buffer = BufferReader(bytes);
final telemetry = >[];
+ try {
+ while (buffer.remaining >= 2) {
+ final channel = buffer.readUInt8();
+ final type = buffer.readUInt8();
- while (buffer.remaining >= 2) {
- final channel = buffer.readUInt8();
- final type = buffer.readUInt8();
+ if (channel == 0 && type == 0) {
+ break;
+ }
- if (channel == 0 && type == 0) {
- break;
- }
-
- switch (type) {
- case lppGenericSensor:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt32BE(),
- });
- break;
- case lppLuminosity:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt16BE(),
- });
- break;
- case lppPresence:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt8(),
- });
- break;
- case lppTemperature:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readInt16BE() / 10,
- });
- break;
- case lppRelativeHumidity:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt8() / 2,
- });
- break;
- case lppBarometricPressure:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt16BE() / 10,
- });
- break;
- case lppVoltage:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readInt16BE() / 100,
- });
- break;
- case lppCurrent:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readInt16BE() / 1000,
- });
- break;
- case lppPercentage:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt8(),
- });
- break;
- case lppConcentration:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt16BE(),
- });
- break;
- case lppPower:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': buffer.readUInt16BE(),
- });
- break;
- case lppGps:
- telemetry.add({
- 'channel': channel,
- 'type': type,
- 'value': {
- 'latitude': buffer.readInt24BE() / 10000,
- 'longitude': buffer.readInt24BE() / 10000,
- 'altitude': buffer.readInt24BE() / 100,
- },
- });
- break;
- default:
- return telemetry;
+ switch (type) {
+ case lppGenericSensor:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt32BE(),
+ });
+ break;
+ case lppLuminosity:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt16BE(),
+ });
+ break;
+ case lppPresence:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt8(),
+ });
+ break;
+ case lppTemperature:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readInt16BE() / 10,
+ });
+ break;
+ case lppRelativeHumidity:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt8() / 2,
+ });
+ break;
+ case lppBarometricPressure:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt16BE() / 10,
+ });
+ break;
+ case lppVoltage:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readInt16BE() / 100,
+ });
+ break;
+ case lppCurrent:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readInt16BE() / 1000,
+ });
+ break;
+ case lppPercentage:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt8(),
+ });
+ break;
+ case lppConcentration:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt16BE(),
+ });
+ break;
+ case lppPower:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': buffer.readUInt16BE(),
+ });
+ break;
+ case lppGps:
+ telemetry.add({
+ 'channel': channel,
+ 'type': type,
+ 'value': {
+ 'latitude': buffer.readInt24BE() / 10000,
+ 'longitude': buffer.readInt24BE() / 10000,
+ 'altitude': buffer.readInt24BE() / 100,
+ },
+ });
+ break;
+ default:
+ return telemetry;
+ }
}
+ return telemetry;
+ } catch (e) {
+ // Handle parsing errors, possibly due to malformed data
+ appLogger.error('Error parsing Cayenne LPP data: $e');
+ // Return any telemetry parsed so far to preserve partial data
+ return telemetry;
}
-
- return telemetry;
}
static List> parseByChannel(Uint8List bytes) {
final buffer = BufferReader(bytes);
final Map> channels = {};
+ try {
+ while (buffer.remaining >= 2) {
+ final channel = buffer.readUInt8();
+ final type = buffer.readUInt8();
- while (buffer.remaining >= 2) {
- final channel = buffer.readUInt8();
- final type = buffer.readUInt8();
+ // Optional: stop on padding (00 00)
+ if (channel == 0 && type == 0) {
+ break;
+ }
- // Optional: stop on padding (00 00)
- if (channel == 0 && type == 0) {
- break;
+ final channelData = channels.putIfAbsent(
+ channel,
+ () => {'channel': channel, 'values': {}},
+ );
+
+ switch (type) {
+ case lppGenericSensor:
+ channelData['values']['generic'] = buffer.readUInt32BE();
+ break;
+ case lppLuminosity:
+ channelData['values']['luminosity'] = buffer.readUInt16BE();
+ break;
+ case lppPresence:
+ channelData['values']['presence'] = buffer.readUInt8() != 0;
+ break;
+ case lppTemperature:
+ channelData['values']['temperature'] = buffer.readInt16BE() / 10.0;
+ break;
+ case lppRelativeHumidity:
+ channelData['values']['humidity'] = buffer.readUInt8() / 2.0;
+ break;
+ case lppBarometricPressure:
+ channelData['values']['pressure'] = buffer.readUInt16BE() / 10.0;
+ break;
+ case lppVoltage:
+ channelData['values']['voltage'] = buffer.readInt16BE() / 100.0;
+ break;
+ case lppCurrent:
+ channelData['values']['current'] = buffer.readInt16BE() / 1000.0;
+ break;
+ case lppPercentage:
+ channelData['values']['percentage'] = buffer.readUInt8();
+ break;
+ case lppConcentration:
+ channelData['values']['concentration'] = buffer.readUInt16BE();
+ break;
+ case lppPower:
+ channelData['values']['power'] = buffer.readUInt16BE();
+ break;
+ case lppGps:
+ channelData['values']['gps'] = {
+ 'latitude': buffer.readInt24BE() / 10000.0,
+ 'longitude': buffer.readInt24BE() / 10000.0,
+ 'altitude': buffer.readInt24BE() / 100.0,
+ };
+ break;
+ // Add more types as needed...
+ default:
+ //Stopped parsing to avoid misalignment
+ return channels.values.toList();
+ }
}
- final channelData = channels.putIfAbsent(channel, () => {
- 'channel': channel,
- 'values': {},
- });
-
- switch (type) {
- case lppGenericSensor:
- channelData['values']['generic'] = buffer.readUInt32BE();
- break;
- case lppLuminosity:
- channelData['values']['luminosity'] = buffer.readUInt16BE();
- break;
- case lppPresence:
- channelData['values']['presence'] = buffer.readUInt8() != 0;
- break;
- case lppTemperature:
- channelData['values']['temperature'] = buffer.readInt16BE() / 10.0;
- break;
- case lppRelativeHumidity:
- channelData['values']['humidity'] = buffer.readUInt8() / 2.0;
- break;
- case lppBarometricPressure:
- channelData['values']['pressure'] = buffer.readUInt16BE() / 10.0;
- break;
- case lppVoltage:
- channelData['values']['voltage'] = buffer.readInt16BE() / 100.0;
- break;
- case lppCurrent:
- channelData['values']['current'] = buffer.readInt16BE() / 1000.0;
- break;
- case lppPercentage:
- channelData['values']['percentage'] = buffer.readUInt8();
- break;
- case lppConcentration:
- channelData['values']['concentration'] = buffer.readUInt16BE();
- break;
- case lppPower:
- channelData['values']['power'] = buffer.readUInt16BE();
- break;
- case lppGps:
- channelData['values']['gps'] = {
- 'latitude': buffer.readInt24BE() / 10000.0,
- 'longitude': buffer.readInt24BE() / 10000.0,
- 'altitude': buffer.readInt24BE() / 100.0,
- };
- break;
- // Add more types as needed...
- default:
- // Unknown type: skip or handle error?
- continue;
- }
+ final List> channelsOut = channels.values.toList();
+ channelsOut.sort((a, b) => a['channel'].compareTo(b['channel']));
+ return channelsOut;
+ } catch (e) {
+ // Handle parsing errors, possibly due to malformed data
+ appLogger.error('Error parsing Cayenne LPP data: $e');
+ return <
+ Map
+ >[]; // Return an empty list on error to avoid crashing the app
}
-
- final List> channelsOut = channels.values.toList();
- channelsOut.sort((a, b) => a['channel'].compareTo(b['channel']));
- return channelsOut;
}
}
diff --git a/lib/helpers/chat_scroll_controller.dart b/lib/helpers/chat_scroll_controller.dart
new file mode 100644
index 0000000..d2c73fb
--- /dev/null
+++ b/lib/helpers/chat_scroll_controller.dart
@@ -0,0 +1,68 @@
+import 'package:flutter/material.dart';
+
+class ChatScrollController extends ScrollController {
+ final ValueNotifier showJumpToBottom = ValueNotifier(false);
+ VoidCallback? onScrollNearTop;
+
+ static const _bottomThreshold = 100.0;
+ static const _topThreshold = 50.0;
+
+ ChatScrollController() {
+ addListener(_handleScroll);
+ }
+
+ void _handleScroll() {
+ if (!hasClients) return;
+ final pos = position;
+
+ // With reverse: true, position 0 is bottom, maxScrollExtent is top
+ // Show jump button when scrolled away from bottom (position > threshold)
+ final isAtBottom = pos.pixels <= _bottomThreshold;
+ if (showJumpToBottom.value == isAtBottom) {
+ showJumpToBottom.value = !isAtBottom;
+ }
+
+ // Pagination trigger when scrolled near top (maxScrollExtent)
+ if (pos.pixels >= pos.maxScrollExtent - _topThreshold) {
+ onScrollNearTop?.call();
+ }
+ }
+
+ void jumpToBottom() {
+ if (hasClients && position.maxScrollExtent > 0) {
+ animateTo(
+ 0, // With reverse: true, position 0 is bottom
+ duration: const Duration(milliseconds: 300),
+ curve: Curves.easeOut,
+ );
+ }
+ }
+
+ void handleKeyboardOpen() {
+ // Simple: just scroll to bottom when keyboard opens
+ if (hasClients) {
+ animateTo(
+ 0, // With reverse: true, position 0 is bottom
+ duration: const Duration(milliseconds: 200),
+ curve: Curves.easeOut,
+ );
+ }
+ }
+
+ void scrollToBottomIfAtBottom() {
+ // Only scroll if jump button is NOT showing (i.e., already at bottom)
+ if (!showJumpToBottom.value && hasClients && position.maxScrollExtent > 0) {
+ animateTo(
+ 0, // With reverse: true, position 0 is bottom
+ duration: const Duration(milliseconds: 200),
+ curve: Curves.easeOut,
+ );
+ }
+ }
+
+ @override
+ void dispose() {
+ showJumpToBottom.dispose();
+ super.dispose();
+ }
+}
diff --git a/lib/helpers/gif_helper.dart b/lib/helpers/gif_helper.dart
new file mode 100644
index 0000000..5b68e90
--- /dev/null
+++ b/lib/helpers/gif_helper.dart
@@ -0,0 +1,38 @@
+class GifHelper {
+ /// Parse a known GIF format, which can be any of:
+ /// g:GIFID
+ /// https://media.giphy.com/media/GIFID/giphy.gif
+ /// https://giphy.com/gifs/Optional-title-with-dashes-GIFID
+ ///
+ /// GIFID is a Giphy GIF ID. The https:// is optional (and
+ /// can also be http://). The giphy.com/gifs form can also
+ /// include a trailing slash.
+ ///
+ /// Returns null if text is not a valid GIF format
+ static String? parseGif(String text) {
+ final trimmed = text.trim();
+ final match = RegExp(r'^g:([A-Za-z0-9_-]+)$').firstMatch(trimmed);
+ if (match != null) {
+ return match.group(1);
+ }
+ final directUrlMatch = RegExp(
+ r'^(?:https?:\/\/)?media\.giphy\.com\/media\/([A-Za-z0-9_-]+)\/giphy\.gif$',
+ ).firstMatch(trimmed);
+ if (directUrlMatch != null) {
+ return directUrlMatch.group(1);
+ }
+ // Giphy understands page URLs with just the ID, or any string and a
+ // dash before the ID, and redirects to a page with a dash-separated
+ // title, a dash, and the ID. IDs in this form *probably* can't
+ // contain dashes.
+ final pageMatch = RegExp(
+ r'^(?:https?:\/\/)?giphy\.com\/gifs\/(?:[^/?]*-)?([A-Za-z0-9_]+)\/?$',
+ ).firstMatch(trimmed);
+ return pageMatch?.group(1);
+ }
+
+ /// Encode a GIF in a format that parseGif() can parse.
+ static String encodeGif(String gifId) {
+ return 'g:$gifId';
+ }
+}
diff --git a/lib/helpers/link_handler.dart b/lib/helpers/link_handler.dart
new file mode 100644
index 0000000..c2eae29
--- /dev/null
+++ b/lib/helpers/link_handler.dart
@@ -0,0 +1,114 @@
+import 'package:flutter/material.dart';
+import 'package:flutter_linkify/flutter_linkify.dart';
+import 'package:url_launcher/url_launcher.dart';
+import '../l10n/l10n.dart';
+import '../utils/platform_info.dart';
+import '../helpers/snack_bar_builder.dart';
+
+class LinkHandler {
+ static TextStyle defaultLinkStyle(BuildContext context, TextStyle base) {
+ final brightness = Theme.of(context).brightness;
+ final orange = brightness == Brightness.dark
+ ? const Color(0xFFFFB74D)
+ : const Color(0xFFE65100);
+ return base.copyWith(color: orange, decoration: TextDecoration.underline);
+ }
+
+ /// Returns a [SelectableLinkify] on desktop or a [Linkify] on mobile.
+ static Widget buildLinkifyText({
+ required BuildContext context,
+ required String text,
+ required TextStyle style,
+ TextStyle? linkStyle,
+ }) {
+ final effectiveLinkStyle = linkStyle ?? defaultLinkStyle(context, style);
+ const options = LinkifyOptions(humanize: false, defaultToHttps: false);
+ const linkifiers = [UrlLinkifier(), EmailLinkifier()];
+ void onOpen(LinkableElement link) => handleLinkTap(context, link.url);
+
+ if (PlatformInfo.isDesktop) {
+ return SelectableLinkify(
+ text: text,
+ style: style,
+ linkStyle: effectiveLinkStyle,
+ options: options,
+ linkifiers: linkifiers,
+ onOpen: onOpen,
+ );
+ }
+ return Linkify(
+ text: text,
+ style: style,
+ linkStyle: effectiveLinkStyle,
+ options: options,
+ linkifiers: linkifiers,
+ onOpen: onOpen,
+ );
+ }
+
+ static Future handleLinkTap(BuildContext context, String url) async {
+ // Show confirmation dialog
+ final shouldOpen = await showDialog(
+ context: context,
+ builder: (context) => AlertDialog(
+ title: Text(context.l10n.chat_openLink),
+ content: Column(
+ mainAxisSize: MainAxisSize.min,
+ crossAxisAlignment: CrossAxisAlignment.start,
+ children: [
+ Text(
+ context.l10n.chat_openLinkConfirmation,
+ style: const TextStyle(fontSize: 14),
+ ),
+ const SizedBox(height: 16),
+ Container(
+ padding: const EdgeInsets.all(12),
+ decoration: BoxDecoration(
+ color: Theme.of(context).colorScheme.surfaceContainerHighest,
+ borderRadius: BorderRadius.circular(8),
+ ),
+ child: SelectableText(
+ url,
+ style: const TextStyle(fontSize: 12, fontFamily: 'monospace'),
+ ),
+ ),
+ ],
+ ),
+ actions: [
+ TextButton(
+ onPressed: () => Navigator.pop(context, false),
+ child: Text(context.l10n.common_cancel),
+ ),
+ FilledButton(
+ onPressed: () => Navigator.pop(context, true),
+ child: Text(context.l10n.chat_open),
+ ),
+ ],
+ ),
+ );
+
+ if (shouldOpen != true) return;
+
+ // Launch URL
+ try {
+ final uri = Uri.parse(url);
+ if (!await launchUrl(uri, mode: LaunchMode.externalApplication)) {
+ if (context.mounted) {
+ showDismissibleSnackBar(
+ context,
+ content: Text(context.l10n.chat_couldNotOpenLink(url)),
+ backgroundColor: Colors.red,
+ );
+ }
+ }
+ } catch (e) {
+ if (context.mounted) {
+ showDismissibleSnackBar(
+ context,
+ content: Text(context.l10n.chat_invalidLink),
+ backgroundColor: Colors.red,
+ );
+ }
+ }
+ }
+}
diff --git a/lib/helpers/path_helper.dart b/lib/helpers/path_helper.dart
new file mode 100644
index 0000000..fe51d63
--- /dev/null
+++ b/lib/helpers/path_helper.dart
@@ -0,0 +1,31 @@
+import '../models/contact.dart';
+import '../connector/meshcore_protocol.dart';
+
+class PathHelper {
+ static String formatPathHex(List pathBytes) {
+ return pathBytes
+ .map((b) => b.toRadixString(16).padLeft(2, '0').toUpperCase())
+ .join(',');
+ }
+
+ static String resolvePathNames(
+ List pathBytes,
+ List allContacts,
+ ) {
+ return pathBytes
+ .map((b) {
+ final hex = b.toRadixString(16).padLeft(2, '0').toUpperCase();
+ final matches = allContacts
+ .where(
+ (c) =>
+ c.publicKey.first == b &&
+ (c.type == advTypeRepeater || c.type == advTypeRoom),
+ )
+ .toList();
+ if (matches.isEmpty) return hex;
+ if (matches.length == 1) return matches.first.name;
+ return matches.map((c) => c.name).join(' | ');
+ })
+ .join(' \u2192 ');
+ }
+}
diff --git a/lib/helpers/reaction_helper.dart b/lib/helpers/reaction_helper.dart
index 004904b..36118ca 100644
--- a/lib/helpers/reaction_helper.dart
+++ b/lib/helpers/reaction_helper.dart
@@ -1,53 +1,117 @@
-class ReactionInfo {
- final String targetMessageId;
- final String emoji;
- final String? reactionKey; // Lightweight key for deduplication: timestamp_senderPrefix
+import '../widgets/emoji_picker.dart';
- ReactionInfo({
- required this.targetMessageId,
- required this.emoji,
- this.reactionKey,
- });
+class ReactionInfo {
+ final String targetHash;
+ final String emoji;
+
+ ReactionInfo({required this.targetHash, required this.emoji});
}
class ReactionHelper {
- /// Parse reaction format: r:[messageId]:[emoji]
- /// Supports both old format (full messageId) and new format (timestamp_senderPrefix)
+ /// Apply a reaction to a list of messages by matching the reaction hash.
+ ///
+ /// [messages] - the message list to search
+ /// [reactionInfo] - the parsed reaction
+ /// [getTimestampSecs] - extract timestamp seconds from a message
+ /// [getSenderName] - extract sender name for hash (null for 1:1 implicit)
+ /// [getMessageText] - extract message text
+ /// [getReactions] - extract current reactions map
+ /// [shouldSkip] - filter function to skip messages (e.g., skip outgoing for incoming reactions)
+ /// [updateMessage] - callback to update the message at index with new reactions
+ ///
+ /// Returns whether a match was found.
+ static bool applyReaction({
+ required List messages,
+ required ReactionInfo reactionInfo,
+ required int Function(T) getTimestampSecs,
+ required String? Function(T) getSenderName,
+ required String Function(T) getMessageText,
+ required Map Function(T) getReactions,
+ required bool Function(T) shouldSkip,
+ required void Function(int index, Map newReactions)
+ updateMessage,
+ }) {
+ final targetHash = reactionInfo.targetHash;
+ for (int i = messages.length - 1; i >= 0; i--) {
+ final msg = messages[i];
+ if (shouldSkip(msg)) continue;
+
+ final msgHash = computeReactionHash(
+ getTimestampSecs(msg),
+ getSenderName(msg),
+ getMessageText(msg),
+ );
+ if (msgHash == targetHash) {
+ final currentReactions = Map.from(getReactions(msg));
+ currentReactions[reactionInfo.emoji] =
+ (currentReactions[reactionInfo.emoji] ?? 0) + 1;
+ updateMessage(i, currentReactions);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ static List? _cachedEmojis;
+
+ /// Combined list of all reaction emojis in fixed order.
+ /// Order must stay stable for index compatibility.
+ static List get reactionEmojis {
+ return _cachedEmojis ??= [
+ ...EmojiPicker.quickEmojis,
+ ...EmojiPicker.smileys,
+ ...EmojiPicker.gestures,
+ ...EmojiPicker.hearts,
+ ...EmojiPicker.objects,
+ ];
+ }
+
+ /// Convert emoji to 2-char hex index. Returns null if emoji not in list.
+ static String? emojiToIndex(String emoji) {
+ final idx = reactionEmojis.indexOf(emoji);
+ if (idx < 0) return null;
+ return idx.toRadixString(16).padLeft(2, '0');
+ }
+
+ /// Convert 2-char hex index to emoji. Returns null if invalid index.
+ static String? indexToEmoji(String hexIndex) {
+ final idx = int.tryParse(hexIndex, radix: 16);
+ if (idx == null || idx < 0 || idx >= reactionEmojis.length) return null;
+ return reactionEmojis[idx];
+ }
+
+ /// Compute a 4-char hex hash for a message reaction.
+ /// Hash input: timestampSeconds + [senderName] + first 5 chars of text
+ /// For 1:1 chats, senderName can be null (sender is implicit).
+ static String computeReactionHash(
+ int timestampSeconds,
+ String? senderName,
+ String text,
+ ) {
+ final first5 = text.length >= 5 ? text.substring(0, 5) : text;
+ final input = senderName != null
+ ? '$timestampSeconds$senderName$first5'
+ : '$timestampSeconds$first5';
+ // Use hashCode and take lower 16 bits, format as 4 hex chars
+ final hash = input.hashCode & 0xFFFF;
+ return hash.toRadixString(16).padLeft(4, '0');
+ }
+
+ /// Parse reaction format: r:HASH:INDEX (where INDEX is 2-char hex emoji index)
+ /// Returns null if text is not a valid reaction format
static ReactionInfo? parseReaction(String text) {
- final regex = RegExp(r'^r:([^:]+):(.+)$');
+ final regex = RegExp(r'^r:([0-9a-f]{4}):([0-9a-f]{2})$');
final match = regex.firstMatch(text);
if (match == null) return null;
- final targetId = match.group(1)!;
- final emoji = match.group(2)!;
+ final emoji = indexToEmoji(match.group(2)!);
+ if (emoji == null) return null;
- // Extract reaction key for deduplication
- // If targetId is in new format (timestamp_senderPrefix), use it directly
- // Otherwise, extract timestamp from old format (timestamp_nameHash_textHash)
- String? reactionKey;
- if (targetId.contains('_')) {
- final parts = targetId.split('_');
- if (parts.length >= 2) {
- // New format: timestamp_senderPrefix, or old format with at least timestamp
- reactionKey = '${parts[0]}_${parts[1]}';
- }
- }
-
- return ReactionInfo(
- targetMessageId: targetId,
- emoji: emoji,
- reactionKey: reactionKey,
- );
+ return ReactionInfo(targetHash: match.group(1)!, emoji: emoji);
}
- /// Generate a lightweight reaction key for a message
- /// Format: r:[timestamp]_[senderPrefix]:[emoji]
- static String buildReactionText(String timestamp, String senderPrefix, String emoji) {
- return 'r:${timestamp}_$senderPrefix:$emoji';
- }
-
- /// Extract sender prefix from public key hex (first 8 chars)
- static String getSenderPrefix(String senderKeyHex) {
- return senderKeyHex.substring(0, 8);
+ /// Encode a reaction message that parseReaction() can parse.
+ static String encodeReaction(String hash, String emojiIndex) {
+ return 'r:$hash:$emojiIndex';
}
}
diff --git a/lib/helpers/smaz.dart b/lib/helpers/smaz.dart
index 0e3e6c9..de34dde 100644
--- a/lib/helpers/smaz.dart
+++ b/lib/helpers/smaz.dart
@@ -262,8 +262,9 @@ class Smaz {
".com",
];
- static final List _rcbBytes =
- _rcb.map((s) => Uint8List.fromList(ascii.encode(s))).toList(growable: false);
+ static final List _rcbBytes = _rcb
+ .map((s) => Uint8List.fromList(ascii.encode(s)))
+ .toList(growable: false);
static final int _maxEntryLen = _rcbBytes.fold(0, (maxLen, entry) {
return entry.length > maxLen ? entry.length : maxLen;
});
@@ -358,24 +359,32 @@ class Smaz {
final code = input[index];
if (code == _verbatimSingle) {
if (index + 1 >= input.length) {
- throw const FormatException('Invalid SMAZ stream: truncated verbatim byte.');
+ throw const FormatException(
+ 'Invalid SMAZ stream: truncated verbatim byte.',
+ );
}
out.addByte(input[index + 1]);
index += 2;
} else if (code == _verbatimRun) {
if (index + 1 >= input.length) {
- throw const FormatException('Invalid SMAZ stream: truncated verbatim length.');
+ throw const FormatException(
+ 'Invalid SMAZ stream: truncated verbatim length.',
+ );
}
final len = input[index + 1] + 1;
final end = index + 2 + len;
if (end > input.length) {
- throw const FormatException('Invalid SMAZ stream: truncated verbatim run.');
+ throw const FormatException(
+ 'Invalid SMAZ stream: truncated verbatim run.',
+ );
}
out.add(input.sublist(index + 2, end));
index = end;
} else {
if (code >= _rcbBytes.length) {
- throw const FormatException('Invalid SMAZ stream: code out of range.');
+ throw const FormatException(
+ 'Invalid SMAZ stream: code out of range.',
+ );
}
out.add(_rcbBytes[code]);
index += 1;
diff --git a/lib/helpers/snack_bar_builder.dart b/lib/helpers/snack_bar_builder.dart
new file mode 100644
index 0000000..d7409b6
--- /dev/null
+++ b/lib/helpers/snack_bar_builder.dart
@@ -0,0 +1,56 @@
+import 'package:flutter/material.dart';
+
+// showDismissibleSnackBar shows a [SnackBar] with tap to dismiss
+// all other properties are default and optional
+void showDismissibleSnackBar(
+ BuildContext context, {
+ Key? key,
+ required Widget content,
+ Color? backgroundColor,
+ double? elevation,
+ EdgeInsetsGeometry? margin,
+ EdgeInsetsGeometry? padding,
+ double? width,
+ ShapeBorder? shape,
+ HitTestBehavior? hitTestBehavior,
+ SnackBarBehavior? behavior,
+ SnackBarAction? action,
+ double? actionOverflowThreshold,
+ bool? showCloseIcon,
+ Color? closeIconColor,
+ Duration? duration,
+ bool? persist,
+ Animation? animation,
+ void Function()? onVisible,
+ DismissDirection? dismissDirection,
+ Clip? clipBehavior,
+}) {
+ final messenger = ScaffoldMessenger.of(context);
+ messenger.showSnackBar(
+ SnackBar(
+ key: key,
+ content: GestureDetector(
+ onTap: () => messenger.hideCurrentSnackBar(),
+ child: content,
+ ),
+ backgroundColor: backgroundColor,
+ elevation: elevation,
+ margin: margin,
+ padding: padding,
+ width: width,
+ shape: shape,
+ hitTestBehavior: hitTestBehavior,
+ behavior: behavior,
+ action: action,
+ actionOverflowThreshold: actionOverflowThreshold,
+ showCloseIcon: showCloseIcon,
+ closeIconColor: closeIconColor,
+ duration: duration ?? const Duration(seconds: 4),
+ persist: persist,
+ animation: animation,
+ onVisible: onVisible,
+ dismissDirection: dismissDirection ?? DismissDirection.down,
+ clipBehavior: clipBehavior ?? Clip.hardEdge,
+ ),
+ );
+}
diff --git a/lib/helpers/utf8_length_limiter.dart b/lib/helpers/utf8_length_limiter.dart
index 843389e..c6acdd2 100644
--- a/lib/helpers/utf8_length_limiter.dart
+++ b/lib/helpers/utf8_length_limiter.dart
@@ -8,7 +8,10 @@ class Utf8LengthLimitingTextInputFormatter extends TextInputFormatter {
const Utf8LengthLimitingTextInputFormatter(this.maxBytes);
@override
- TextEditingValue formatEditUpdate(TextEditingValue oldValue, TextEditingValue newValue) {
+ TextEditingValue formatEditUpdate(
+ TextEditingValue oldValue,
+ TextEditingValue newValue,
+ ) {
if (maxBytes <= 0) return oldValue;
final bytes = utf8.encode(newValue.text);
if (bytes.length <= maxBytes) return newValue;
diff --git a/lib/icons/los_icon.dart b/lib/icons/los_icon.dart
new file mode 100644
index 0000000..58d75b0
--- /dev/null
+++ b/lib/icons/los_icon.dart
@@ -0,0 +1,22 @@
+import 'package:flutter/material.dart';
+import 'package:material_symbols_icons/material_symbols_icons.dart';
+
+class LosIcon extends StatelessWidget {
+ final double size;
+ final Color? color;
+
+ const LosIcon({super.key, this.size = 24, this.color});
+
+ @override
+ Widget build(BuildContext context) {
+ final theme = Theme.of(context);
+ final iconTheme = IconTheme.of(context);
+ final iconColor =
+ color ??
+ iconTheme.color ??
+ theme.iconTheme.color ??
+ theme.colorScheme.onSurface;
+
+ return Icon(Symbols.elevation, size: size, color: iconColor);
+ }
+}
diff --git a/lib/l10n/app_bg.arb b/lib/l10n/app_bg.arb
index 1b5e5de..7ac5417 100644
--- a/lib/l10n/app_bg.arb
+++ b/lib/l10n/app_bg.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Неуспешно изтриване на канала \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "bg",
"appTitle": "MeshCore Open",
"nav_contacts": "Контакти",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Брой контакти",
"settings_infoChannelCount": "Брой канали",
"settings_presets": "Предварителни настройки",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Честота (MHz)",
"settings_frequencyHelper": "300.0 - 2500.0",
"settings_frequencyInvalid": "Невалидна честота (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX Мощност (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Невалидна мощност на TX (0-22 dBm)",
- "settings_longRange": "Дълъг обхват",
- "settings_fastSpeed": "Бърза скорост",
"settings_error": "Грешка: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Нова група",
"contacts_groupName": "Група",
"contacts_groupNameRequired": "Името на групата е задължително.",
+ "contacts_groupNameReserved": "Това име на група е запазено",
"contacts_groupAlreadyExists": "Групата \"{name}\" вече съществува.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Публичен канал",
"channels_privateChannel": "Частен канал",
"channels_editChannel": "Редактирай канал",
+ "channels_muteChannel": "Заглуши канала",
+ "channels_unmuteChannel": "Включи известията на канала",
"channels_deleteChannel": "Изтрий канала",
"channels_deleteChannelConfirm": "Изтрий \"{name}\"? Това не може да бъде отменено.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Отваряне на връзката?",
+ "chat_openLinkConfirmation": "Искате ли да отворите тази връзка в браузъра си?",
+ "chat_open": "Отвори",
+ "chat_couldNotOpenLink": "Не можа да се отвори връзката: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Невалиден формат на връзката",
"map_title": "Карта на възлите",
"map_noNodesWithLocation": "Няма възли с данни за местоположение.",
"map_nodesNeedGps": "Възлагат се възлозите да споделят техните GPS координати,\nза да се появят на картата.",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighboursSubtitle": "Преглед на съседни възли с нулев скок.",
- "repeater_neighbours": "Съседи",
+ "repeater_neighborsSubtitle": "Преглед на съседни възли с нулев скок.",
+ "repeater_neighbors": "Съседи",
"neighbors_receivedData": "Получени данни за съседи",
"neighbors_requestTimedOut": "Съседите поискат изтичане на време.",
"neighbors_errorLoading": "Грешка при зареждане на съседи: {error}",
- "neighbors_repeatersNeighbours": "Повторители Съседи",
+ "neighbors_repeatersNeighbors": "Повторители Съседи",
"neighbors_noData": "Няма налични данни за съседи.",
"channels_createPrivateChannel": "Създай Частен Канал",
"channels_joinPrivateChannel": "Присъедини се към Частен Канал",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Това ще изтрие също {count} канал(а) и техните съобщения.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Остави общността \"{name}\"",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Общ хаштаг (всеки може да се присъедини)",
"community_communityHashtag": "Общностен хаштаг",
"community_communityHashtagDesc": "Само за членове на общността",
- "community_forCommunity": "За {name}"
+ "community_forCommunity": "За {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecretConfirm": "Регенерация на секретния ключ за \"{name}\"? Всички членове ще трябва да сканират новия QR код, за да продължат комуникацията.",
+ "community_secretRegenerated": "Секретно презареждане за \"{name}\"",
+ "community_regenerateSecret": "Регенерейрай секрет",
+ "community_regenerate": "Регенерация",
+ "community_updateSecret": "Актуализирай тайна",
+ "community_scanToUpdateSecret": "Сканьорвайте новия QR код, за да актуализирате секрета за \"{name}\"",
+ "community_secretUpdated": "Секретно обновено за \"{name}\"",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Вие",
+ "pathTrace_notAvailable": "Пътека за проследяване не е достъпна.",
+ "contacts_pathTrace": "Пътен проследяване",
+ "pathTrace_refreshTooltip": "Обнови Path Trace.",
+ "pathTrace_failed": "Пътят за проследяване не успя.",
+ "contacts_repeaterPing": "Пингване на повторителя",
+ "contacts_repeaterPathTrace": "Трасировка до повторител",
+ "contacts_ping": "Пинг",
+ "contacts_chatTraceRoute": "Трасиране на път",
+ "contacts_roomPathTrace": "Трасиране на път до съ",
+ "contacts_roomPing": "Ping на сървъра на стаята",
+ "contacts_pathTraceTo": "Проследи маршрут към {name}",
+ "appSettings_languageUk": "Украински",
+ "contacts_clipboardEmpty": "Клипборда е празна.",
+ "contacts_invalidAdvertFormat": "Невалидни данни за контакт",
+ "appSettings_languageRu": "Руски",
+ "appSettings_enableMessageTracing": "Разрешаване на проследяване на съобщения",
+ "appSettings_enableMessageTracingSubtitle": "Показване на подробни метаданни за маршрутизация и синхронизация за съобщения",
+ "contacts_contactImported": "Контактът е импортиран.",
+ "contacts_zeroHopAdvert": "Реклама без скок",
+ "contacts_contactImportFailed": "Контактът не е успешно импортиран.",
+ "contacts_floodAdvert": "Потопна реклама",
+ "contacts_addContactFromClipboard": "Добави контакт от клипборда",
+ "contacts_copyAdvertToClipboard": "Копирай обявата в клипборда",
+ "contacts_ShareContact": "Копирай контакт в клипборда",
+ "contacts_ShareContactZeroHop": "Сподели контакт чрез обява",
+ "contacts_contactAdvertCopied": "Рекламата е копирана в клипборда.",
+ "contacts_zeroHopContactAdvertFailed": "Неуспешно изпращане на контакт.",
+ "contacts_zeroHopContactAdvertSent": "Изпратен контакт по обява.",
+ "contacts_contactAdvertCopyFailed": "Копирането на обявата в клипборда не успя.",
+ "notification_activityTitle": "Активност на MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{съобщение} other{съобщения}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{съобщение в канал} other{съобщения в канали}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{нов възел} other{нови възли}}",
+ "notification_newTypeDiscovered": "Открит нов {contactType}",
+ "notification_receivedNewMessage": "Получено ново съобщение",
+ "settings_gpxExportContactsSubtitle": "Експортира спътници с местоположение в GPX файл.",
+ "settings_gpxExportRepeatersSubtitle": "Изпраща повторители / roomserver с местоположение в GPX файл.",
+ "settings_gpxExportAll": "Експортирай всички контакти в GPX",
+ "settings_gpxExportAllSubtitle": "Експортира всички контакти с местоположение в файл GPX.",
+ "settings_gpxExportRepeaters": "Експортиране на повтарящи се устройства / сървър на стаята до GPX",
+ "settings_gpxExportContacts": "Експортирай спътници към GPX",
+ "settings_gpxExportSuccess": "Успешно изlexport на файл GPX.",
+ "settings_gpxExportNoContacts": "Няма контакти за изlexport.",
+ "settings_gpxExportChat": "Местоположения на спътници",
+ "settings_gpxExportError": "Възникна грешка при изнасяне.",
+ "settings_gpxExportRepeatersRoom": "Местоположения на повторител и сървър на стаята",
+ "settings_gpxExportNotAvailable": "Не е поддържан на вашето устройство/ОС",
+ "settings_gpxExportAllContacts": "Местоположения на всички контакти",
+ "settings_gpxExportShareText": "Картинни данни изнесени от meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open износ на данни за карта в формат GPX",
+ "pathTrace_someHopsNoLocation": "Един или повече от хмелите липсва местоположение!",
+ "map_pathTraceCancelled": "Отменен е следването на пътя.",
+ "pathTrace_clearTooltip": "Изчисти пътя",
+ "map_removeLast": "Премахни Последно",
+ "map_runTrace": "Изпълни Път на Следване",
+ "map_tapToAdd": "Натиснете върху възлите, за да ги добавите към пътя.",
+ "scanner_bluetoothOff": "Bluetooth е изключен.",
+ "scanner_enableBluetooth": "Активирайте Bluetooth",
+ "scanner_bluetoothOffMessage": "Моля, активирайте Bluetooth, за да сканирате за устройства.",
+ "scanner_chromeRequired": "Изисква се браузър Chrome",
+ "scanner_chromeRequiredMessage": "Това уеб приложение изисква Google Chrome или браузър, базиран на Chromium, за поддръжка на Bluetooth.",
+ "snrIndicator_lastSeen": "Последно видян",
+ "snrIndicator_nearByRepeaters": "Близки повтарящи се устройства",
+ "chat_ShowAllPaths": "Покажи всички пътища",
+ "settings_clientRepeatSubtitle": "Позволете на това устройство да предава пакети към мрежата за други устройства.",
+ "settings_clientRepeatFreqWarning": "За повторение извън мрежата са необходими честоти от 433, 869 или 918 MHz.",
+ "settings_clientRepeat": "Без електричество – повторение",
+ "settings_aboutOpenMeteoAttribution": "Данни за надморска височина на LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "единици",
+ "appSettings_unitsMetric": "Метрика (m / km)",
+ "appSettings_unitsImperial": "Имперска (ft / mi)",
+ "map_lineOfSight": "Линия на видимост",
+ "map_losScreenTitle": "Линия на видимост",
+ "losSelectStartEnd": "Изберете начални и крайни възли за LOS.",
+ "losRunFailed": "Проверката на пряката видимост е неуспешна: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Изчистете всички точки",
+ "losRunToViewElevationProfile": "Стартирайте LOS, за да видите профила на надморската височина",
+ "losMenuTitle": "LOS меню",
+ "losMenuSubtitle": "Докоснете възли или натиснете продължително карта за персонализирани точки",
+ "losShowDisplayNodes": "Показване на възли на дисплея",
+ "losCustomPoints": "Персонализирани точки",
+ "losCustomPointLabel": "Персонализирано {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Точка А",
+ "losPointB": "Точка Б",
+ "losAntennaA": "Антена A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Антена B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Стартирайте LOS",
+ "losNoElevationData": "Няма данни за надморска височина",
+ "losProfileClear": "{distance} {distanceUnit}, чист LOS, минимално разстояние {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, блокиран от {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: проверка...",
+ "losStatusNoData": "LOS: няма данни",
+ "losStatusSummary": "LOS: {clear}/{total} ясно, {blocked} блокирано, {unknown} неизвестно",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Няма налични данни за надморска височина за една или повече проби.",
+ "losErrorInvalidInput": "Невалидни данни за точки/надморска височина за изчисляване на LOS.",
+ "losRenameCustomPoint": "Преименувайте персонализирана точка",
+ "losPointName": "Име на точката",
+ "losShowPanelTooltip": "Показване на LOS панел",
+ "losHidePanelTooltip": "Скриване на LOS панела",
+ "losElevationAttribution": "Данни за надморска височина: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Радиохоризонт",
+ "losLegendLosBeam": "Линия на видимост",
+ "losLegendTerrain": "Терен",
+ "losFrequencyLabel": "Честота",
+ "losFrequencyInfoTooltip": "Преглед на детайли за изчислението",
+ "losFrequencyDialogTitle": "Изчисляване на радиохоризонта",
+ "losFrequencyDialogDescription": "Започвайки от k={baselineK} при {baselineFreq} MHz, изчислението коригира k-фактора за текущата {frequencyMHz} MHz лента, която определя границата на извития радиохоризонт.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Премахване от списъка с любими",
+ "listFilter_addToFavorites": "Добави към любими",
+ "listFilter_favorites": "Любими",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchFavorites": "Търсене на {number}{str} любими...",
+ "contacts_searchRoomServers": "Търсене на {number}{str} сървъри в стаята...",
+ "contacts_unread": "Непрочетено",
+ "contacts_searchRepeaters": "Търсене на {number}{str} повтарящи се...",
+ "contacts_searchContactsNoNumber": "Търси контакти...",
+ "contacts_searchUsers": "Търсене на {number}{str} потребители...",
+ "contactsSettings_title": "Настройки на контактите",
+ "contactsSettings_autoAddTitle": "Автоматично откриване",
+ "contactsSettings_autoAddUsersTitle": "Автоматично добавяне на потребители",
+ "contactsSettings_otherTitle": "Други настройки свързани с контакти",
+ "settings_contactSettingsSubtitle": "Настройки за добавяне на контакти.",
+ "settings_contactSettings": "Настройки за контакти",
+ "contactsSettings_autoAddSensorsTitle": "Автоматично добавяне на датчици",
+ "contactsSettings_autoAddRoomServersTitle": "Автоматично добавяне на сървъри на стаите",
+ "contactsSettings_autoAddRoomServersSubtitle": "Позволи на спътника да добавя автоматично откритите сървъри на стаите.",
+ "contactsSettings_autoAddRepeatersTitle": "Автоматично добавяне на повтарящи се елементи",
+ "contactsSettings_autoAddUsersSubtitle": "Позволи на спътника да добавя автоматично откритите потребители.",
+ "contactsSettings_autoAddRepeatersSubtitle": "Позволи на спътника да добавя автоматично откритите повтарящи се устройства.",
+ "contactsSettings_autoAddSensorsSubtitle": "Позволи на спътника да добавя автоматично откритите датчици.",
+ "contactsSettings_overwriteOldestTitle": "Премахни най-старото",
+ "discoveredContacts_Title": "Открити контакти",
+ "discoveredContacts_searchHint": "Търсене на открити контакти",
+ "discoveredContacts_noMatching": "Няма съвпадащи контакти",
+ "discoveredContacts_contactAdded": "Контакт добавен",
+ "discoveredContacts_copyContact": "Копирай контакт в клипборда",
+ "discoveredContacts_deleteContact": "Изтрий контакт",
+ "discoveredContacts_addContact": "Добави контакт",
+ "contactsSettings_overwriteOldestSubtitle": "Когато списъкът с контакти е пълен, най-старият неключов контакт ще бъде заменен.",
+ "discoveredContacts_deleteContactAll": "Изтриване на Всички Открити Контакти",
+ "discoveredContacts_deleteContactAllContent": "Сигурни ли сте, че искате да изтриете всички открити контакти?",
+ "common_deleteAll": "Изтрий всичко",
+ "map_guessedLocation": "Предполагано местоположение",
+ "map_showGuessedLocations": "Покажете местоположенията на предположените възли.",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenTitle": "Свържете се чрез USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenSubtitle": "Изберете открития сериен уред и свържете директно към вашия MeshCore възел.",
+ "usbScreenStatus": "Изберете USB устройство",
+ "usbScreenNote": "USB серийната връзка е активна на поддържаните Android устройства и настолни платформи.",
+ "usbScreenEmptyState": "Няма открити USB устройства. Включете едно и опитайте отново.",
+ "usbErrorPermissionDenied": "Не беше разрешено достъпът през USB.",
+ "usbErrorDeviceMissing": "Избраното USB устройство вече не е налично.",
+ "usbErrorInvalidPort": "Изберете валитно USB устройство.",
+ "usbErrorBusy": "Друг мол за свързване през USB вече е в процес на изпълнение.",
+ "usbErrorNotConnected": "Няма свързано USB устройство.",
+ "usbErrorOpenFailed": "Не успях да отворя избраното USB устройство.",
+ "usbErrorConnectFailed": "Не успях да се свържа с избраното USB устройство.",
+ "usbErrorUnsupported": "USB серийната комуникация не се поддържа на тази платформа.",
+ "usbErrorAlreadyActive": "USB връзката вече е активирана.",
+ "usbErrorNoDeviceSelected": "Няма избран USB устройство.",
+ "usbErrorPortClosed": "USB връзката не е активна.",
+ "usbFallbackDeviceName": "Устройство за четене на уеб серийни данни",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_connecting": "Свързване към USB устройство...",
+ "usbConnectionFailed": "Неуспешно свързване през USB: {error}",
+ "usbStatus_notConnected": "Изберете USB устройство",
+ "usbStatus_searching": "Търсене на USB устройства...",
+ "usbErrorConnectTimedOut": "Връзката прекъсна. Уверете се, че устройството има софтуер за USB връзка.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostHint": "192.168.40.10",
+ "tcpScreenTitle": "Свържете се чрез TCP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostLabel": "IP адрес",
+ "tcpPortLabel": "Пристанище",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Въведете крайната точка и свържете се.",
+ "tcpStatus_connectingTo": "Свързване към {endpoint}...",
+ "tcpErrorHostRequired": "Необходим е IP адрес.",
+ "tcpErrorPortInvalid": "Портът трябва да бъде между 1 и 65535.",
+ "tcpErrorUnsupported": "Транспортът чрез TCP не се поддържа на тази платформа.",
+ "tcpErrorTimedOut": "Връзката TCP изтекла.",
+ "tcpConnectionFailed": "Неуспешно е установено TCP връзката: {error}",
+ "map_showDiscoveryContacts": "Покажи контакти за откриване",
+ "map_setAsMyLocation": "Задайте като моя местоположение",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_denyAll": "Откажи всичко",
+ "settings_allowAll": "Позволи всичко",
+ "settings_allowByContact": "Позволи по флагове за контакт",
+ "settings_privacy": "Настройки на поверителността",
+ "settings_privacySettingsDescription": "Изберете каква информация устройството ви споделя с другите.",
+ "settings_privacySubtitle": "Контролирайте каква информация се споделя.",
+ "settings_telemetryBaseMode": "Базов режим на телеметрия",
+ "settings_telemetryLocationMode": "Режим на местоположение на телеметрията",
+ "settings_advertLocation": "Място на обявата",
+ "settings_advertLocationSubtitle": "Включи местоположение в обявата",
+ "contact_info": "Контактна информация",
+ "settings_telemetryEnvironmentMode": "Режим на средата на телеметрията",
+ "contact_telemetry": "Телеметрия",
+ "contact_lastSeen": "Последно видян",
+ "contact_clearChat": "Изчисти чата",
+ "contact_teleBase": "Базата данни за телеметрия",
+ "contact_settings": "Настройки за контакти",
+ "contact_teleBaseSubtitle": "Позволи споделяне на ниво на батерията и основна телеметрия",
+ "contact_teleEnv": "Среда на телеметрия",
+ "contact_teleLocSubtitle": "Позволи споделяне на данни за местоположение",
+ "contact_teleLoc": "Местоположение на телеметрията",
+ "contact_teleEnvSubtitle": "Позволи споделяне на данни от средносферните датчици",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeight": "Първоначална тежест на маршрута",
+ "appSettings_maxRouteWeight": "Максимално допустимо тегло на маршрута",
+ "appSettings_initialRouteWeightSubtitle": "Начално тегло за новооткрити маршрути",
+ "appSettings_maxRouteWeightSubtitle": "Максималното тегло, което един маршрут може да събере от успешни доставки.",
+ "appSettings_routeWeightSuccessIncrement": "Увеличение на теглото за успех",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Тегло, добавено към път след успешно доставяне.",
+ "appSettings_routeWeightFailureDecrement": "Намаляване на теглото, свързано с неуспех",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Тегло, което е било премахнато от пътя след неуспешен опит за доставка.",
+ "appSettings_maxMessageRetries": "Максимален брой опити за изпращане на съобщение",
+ "appSettings_maxMessageRetriesSubtitle": "Брой опити за повторно изпращане, преди съобщението да бъде маркирано като неуспешно.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_multiAck": "Мулти-потвърди: {value}",
+ "settings_telemetryModeUpdated": "Режим на телеметрията е обновен",
+ "map_showOverlaps": "Покриване на ключа на повтаряча",
+ "map_runTraceWithReturnPath": "Върни се по същия път.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Моля, изчакайте малко, преди да изпратите отново.",
+ "appSettings_languageHu": "Унгарски",
+ "appSettings_jumpToOldestUnread": "Преминете към най-старата непочетена статия",
+ "appSettings_jumpToOldestUnreadSubtitle": "Когато отворите чат с непрочетени съобщения, плъзнете надолу, за да видите първото непрочетено съобщение, вместо най-новото.",
+ "appSettings_languageJa": "Японски",
+ "appSettings_languageKo": "Корейски",
+ "radioStats_tooltip": "Статистика за радио и мрежа",
+ "radioStats_screenTitle": "Статистически данни за радиопредаванията",
+ "radioStats_notConnected": "Свържете се с устройство, за да видите статистически данни за радиопредаване.",
+ "radioStats_firmwareTooOld": "Статистиката на радиостанцията изисква съвместимо софтуерно решение версия 8 или по-нова.",
+ "radioStats_waiting": "Изчакване на данни…",
+ "radioStats_noiseFloor": "Ниво на шума: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Последен RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Последна стойност на SNR: {snr} dB",
+ "radioStats_txAir": "Време на въздух (общо): {seconds} секунди",
+ "radioStats_rxAir": "Общо време на използване на RX (в секунди): {seconds} с",
+ "radioStats_chartCaption": "Ниво на шума (dBm) за последните измервания.",
+ "radioStats_stripNoise": "Ниво на шума: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Извличане на данни за радиото…",
+ "radioStats_settingsTile": "Статистически данни за радиостанции",
+ "radioStats_settingsSubtitle": "Ниво на шума, RSSI, SNR и време на пренос",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableTitle": "Активирайте превода",
+ "translation_title": "Превод",
+ "translation_composerTitle": "Преведете преди да изпратите",
+ "translation_enableSubtitle": "Превеждайте входящите съобщения и позволявайте предварително превеждане преди изпращане.",
+ "translation_composerSubtitle": "Контролира началния статус на иконата за превод, създадена от композитора.",
+ "translation_targetLanguage": "Целеви език",
+ "translation_useAppLanguage": "Използвайте езика на приложението",
+ "translation_downloadedModelLabel": "Изтегнат модел",
+ "translation_presetModelLabel": "Предварително конфигуриран модел от Hugging Face",
+ "translation_manualUrlLabel": "URL на ръководството",
+ "translation_downloadModel": "Изтеглете модела",
+ "translation_downloading": "Изтегляне...",
+ "translation_working": "Работа...",
+ "translation_stop": "Спрете",
+ "translation_mergingChunks": "Съединяване на изтеглените части в един файл...",
+ "translation_downloadedModels": "Изтеглени модели",
+ "translation_deleteModel": "Изтриване на модела",
+ "translation_modelDownloaded": "Моделът за превод е изтеглен.",
+ "translation_downloadStopped": "Изтеглянето беше прекъснато.",
+ "translation_downloadFailed": "Не успях да изтегля: {error}",
+ "translation_enterUrlFirst": "Въведете първо URL адрес на модела.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "Изпращайте съобщения на оригиналния въведен език.",
+ "translation_translateBeforeSending": "Преведете преди да изпратите",
+ "translation_messageTranslation": "Превод на съобщението",
+ "translation_composerEnabledHint": "Съобщенията ще бъдат преведени, преди да бъдат изпратени.",
+ "translation_translateTo": "Превеждане на {language}",
+ "translation_translationOptions": "Опции за превод",
+ "translation_systemLanguage": "Език на системата",
+ "scanner_linuxPairingPinTitle": "PIN за съвпадение чрез Bluetooth",
+ "scanner_linuxPairingPinPrompt": "Въведете PIN кода за {deviceName} (оставете празно, ако няма такъв).",
+ "scanner_linuxPairingHidePin": "Скриване на PIN кода",
+ "scanner_linuxPairingShowPin": "Покажи PIN",
+ "repeater_cliQuickClockSync": "Синхронизация на часовника",
+ "repeater_cliQuickDiscovery": "Открий Съседи",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Автоматично изпращайте съобщение \"синхронизиране на часовника\" след успешно влизане.",
+ "repeater_clockSyncAfterLogin": "Синхронизиране на часовника след влизане",
+ "chat_sendMessage": "Изпратете съобщение",
+ "room_guest": "Информация за сървъра на стаята",
+ "repeater_guest": "Информация за ретранслаторите",
+ "repeater_guestTools": "Инструменти за гости"
}
diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb
index 07f395a..4695505 100644
--- a/lib/l10n/app_de.arb
+++ b/lib/l10n/app_de.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Kanal {name} konnte nicht gelöscht werden",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "de",
"appTitle": "MeshCore Open",
"nav_contacts": "Kontakte",
@@ -74,7 +82,7 @@
"settings_title": "Einstellungen",
"settings_deviceInfo": "Geräteinformationen",
"settings_appSettings": "App-Einstellungen",
- "settings_appSettingsSubtitle": "Benachrichtigungen, Messaging und Kartenwahrnehmungen",
+ "settings_appSettingsSubtitle": "Benachrichtigungen, Messaging und Kartenwahrnehmung",
"settings_nodeSettings": "Knoten-Einstellungen",
"settings_nodeName": "Knotenname",
"settings_nodeNameNotSet": "Nicht festgelegt",
@@ -96,14 +104,14 @@
"settings_privacyModeEnabled": "Datenschutzmodus aktiviert",
"settings_privacyModeDisabled": "Datenschutzmodus deaktiviert",
"settings_actions": "Aktionen",
- "settings_sendAdvertisement": "Sende eine Ankündigung",
- "settings_sendAdvertisementSubtitle": "Sende Ankündigung",
+ "settings_sendAdvertisement": "Sende Ankündigung",
+ "settings_sendAdvertisementSubtitle": "Sende eine Ankündigung",
"settings_advertisementSent": "Ankündigung gesendet",
"settings_syncTime": "Zeitsynchronisierung",
"settings_syncTimeSubtitle": "Stelle die Gerätezeit auf die Uhrzeit des Telefons ein",
"settings_timeSynchronized": "Zeit synchronisiert",
"settings_refreshContacts": "Kontakte aktualisieren",
- "settings_refreshContactsSubtitle": "Kontakte-Liste vom Gerät neu laden",
+ "settings_refreshContactsSubtitle": "Kontakt-Liste vom Gerät neu laden",
"settings_rebootDevice": "Gerät neu starten",
"settings_rebootDeviceSubtitle": "MeshCore-Gerät neu starten",
"settings_rebootDeviceConfirm": "Sind Sie sicher, dass Sie das Gerät neu starten möchten? Sie werden getrennt.",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Anzahl Kontakte",
"settings_infoChannelCount": "Anzahl Kanäle",
"settings_presets": "Voreinstellungen",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frequenz (MHz)",
"settings_frequencyHelper": "300,00 - 2.500,00",
"settings_frequencyInvalid": "Ungültige Frequenz (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX-Leistung (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Ungültige TX-Leistung (0-22 dBm)",
- "settings_longRange": "Grosse Reichweite",
- "settings_fastSpeed": "Schnelle Geschwindigkeit",
"settings_error": "Fehler: {message}",
"@settings_error": {
"placeholders": {
@@ -266,7 +269,7 @@
}
}
},
- "contacts_manageRepeater": "Wiederholungen verwalten",
+ "contacts_manageRepeater": "Repeater verwalten",
"contacts_roomLogin": "Raum-Login",
"contacts_openChat": "Öffne Chat",
"contacts_editGroup": "Gruppe bearbeiten",
@@ -282,6 +285,7 @@
"contacts_newGroup": "Neue Gruppe",
"contacts_groupName": "Gruppenname",
"contacts_groupNameRequired": "Der Gruppennamen ist erforderlich.",
+ "contacts_groupNameReserved": "Dieser Gruppenname ist reserviert",
"contacts_groupAlreadyExists": "Die Gruppe \"{name}\" existiert bereits.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -293,8 +297,8 @@
"contacts_filterContacts": "Filtert Kontakte...",
"contacts_noContactsMatchFilter": "Keine Kontakte passen zu Ihrem Filter",
"contacts_noMembers": "Keine Mitglieder",
- "contacts_lastSeenNow": "gerade gesehen",
- "contacts_lastSeenMinsAgo": "Letzte Sichtung vor {minutes} Minuten.",
+ "contacts_lastSeenNow": "kürzlich",
+ "contacts_lastSeenMinsAgo": "~ {minutes} Min.",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +306,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "Letzte Sichtung vor 1 Stunde.",
- "contacts_lastSeenHoursAgo": "Letzte Sichtung vor {hours} Stunden.",
+ "contacts_lastSeenHourAgo": "~ 1 Std.",
+ "contacts_lastSeenHoursAgo": "~ {hours} Std.",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +315,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "Letzte Sichtung vor 1 Tag",
- "contacts_lastSeenDaysAgo": "Letzte Sichtung {days} Tage zuvor",
+ "contacts_lastSeenDayAgo": "~ 1 Tag",
+ "contacts_lastSeenDaysAgo": "~ {days} Tage",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Öffentlicher Kanal",
"channels_privateChannel": "Privater Kanal",
"channels_editChannel": "Kanal bearbeiten",
+ "channels_muteChannel": "Kanal stummschalten",
+ "channels_unmuteChannel": "Kanal Stummschaltung aufheben",
"channels_deleteChannel": "Lösche den Kanal",
"channels_deleteChannelConfirm": "Löschen von \"{name}\"? Dies kann nicht rückgängig gemacht werden.",
"@channels_deleteChannelConfirm": {
@@ -360,7 +366,7 @@
"channels_channelIndexLabel": "Kanalindex",
"channels_channelName": "Kanalname",
"channels_usePublicChannel": "Verwende öffentlichen Kanal",
- "channels_standardPublicPsk": "Standard-Öffentliche PSK",
+ "channels_standardPublicPsk": "Öffentliche Standard PSK",
"channels_pskHex": "PSK (Hex)",
"channels_generateRandomPsk": "Zufällige PSK generieren",
"channels_enterChannelName": "Bitte geben Sie einen Kanalnamen ein.",
@@ -489,8 +495,8 @@
}
}
},
- "debugFrame_textMessageHeader": "Textnachricht-Frame:",
- "debugFrame_destinationPubKey": "- Ziel-Pub-Schlüssel: {pubKey}",
+ "debugFrame_textMessageHeader": "Textnachrichten Frame:",
+ "debugFrame_destinationPubKey": "- Ziel-Public-Schlüssel: {pubKey}",
"@debugFrame_destinationPubKey": {
"placeholders": {
"pubKey": {
@@ -540,7 +546,7 @@
"chat_routingMode": "Routenmodus",
"chat_autoUseSavedPath": "Automatisch (gespeicherten Pfad verwenden)",
"chat_forceFloodMode": "Flut-Modus erzwingen",
- "chat_recentAckPaths": "Aktuelle ACK-Pfade (tasten, um zu verwenden):",
+ "chat_recentAckPaths": "Aktuelle ACK-Pfade (antippen, um zu verwenden):",
"chat_pathHistoryFull": "Die Pfadhistorie ist voll. Entferne Einträge, um neue hinzuzufügen.",
"chat_hopSingular": "Sprung",
"chat_hopPlural": "Sprünge",
@@ -554,7 +560,7 @@
},
"chat_successes": "Erfolgreich",
"chat_removePath": "Pfad entfernen",
- "chat_noPathHistoryYet": "Keine eine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.",
+ "chat_noPathHistoryYet": "Keine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.",
"chat_pathActions": "Pfadaktionen:",
"chat_setCustomPath": "Lege benutzerdefinierten Pfad fest",
"chat_setCustomPathSubtitle": "Manuellen Routenpfad festlegen",
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Link öffnen?",
+ "chat_openLinkConfirmation": "Möchten Sie diesen Link in Ihrem Browser öffnen?",
+ "chat_open": "Öffnen",
+ "chat_couldNotOpenLink": "Link konnte nicht geöffnet werden: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Ungültiges Link-Format",
"map_title": "Karte",
"map_noNodesWithLocation": "Keine Knoten mit Standortdaten",
"map_nodesNeedGps": "Knoten müssen ihre GPS-Koordinaten teilen,\num auf der Karte zu erscheinen.",
@@ -705,7 +723,7 @@
"mapCache_cacheArea": "Zwischenspeicherbereich",
"mapCache_useCurrentView": "Aktuelle Ansicht verwenden",
"mapCache_zoomRange": "Zoom Bereich",
- "mapCache_estimatedTiles": "Geschätzte Fliesen: {count}",
+ "mapCache_estimatedTiles": "Geschätzte Kacheln: {count}",
"@mapCache_estimatedTiles": {
"placeholders": {
"count": {
@@ -842,7 +860,7 @@
},
"path_enterCustomPath": "Gebe Pfad ein",
"path_currentPathLabel": "Aktueller Pfad",
- "path_hexPrefixInstructions": "Gebe für jeden Hopfen 2-stellige Hex-Präfixe ein, getrennt durch Kommas.",
+ "path_hexPrefixInstructions": "Gebe für jeden Zwischen-Hop das 2-stellige Hex-Präfix ein, getrennt durch Kommas.",
"path_hexPrefixExample": "Beispiel: A1,F2,3C (jeder Knoten verwendet den ersten Byte seines öffentlichen Schlüssels)",
"path_labelHexPrefixes": "Pfad (Hex-Präfixe)",
"path_helperMaxHops": "Max 64 Sprünge. Jede Präfixe ist 2 Hexadezimalzeichen (1 Byte)",
@@ -875,7 +893,7 @@
"repeater_forceFloodMode": "Flut-Modus erzwingen",
"repeater_pathManagement": "Pfadverwaltung",
"repeater_refresh": "Aktualisieren",
- "repeater_statusRequestTimeout": "Statusanfrage zeitweise fehlgeschlagen.",
+ "repeater_statusRequestTimeout": "Statusanfrage durch Timeout fehlgeschlagen.",
"repeater_errorLoadingStatus": "Fehler beim Laden des Status: {error}",
"@repeater_errorLoadingStatus": {
"placeholders": {
@@ -945,7 +963,7 @@
}
}
},
- "repeater_duplicatesFloodDirect": "Überflut: {flood}, Direkt: {direct}",
+ "repeater_duplicatesFloodDirect": "Flut: {flood}, Direkt: {direct}",
"@repeater_duplicatesFloodDirect": {
"placeholders": {
"flood": {
@@ -971,7 +989,7 @@
"repeater_adminPassword": "Admin-Passwort",
"repeater_adminPasswordHelper": "Vollzugriffspasswort",
"repeater_guestPassword": "Gast-Passwort",
- "repeater_guestPasswordHelper": "Schreibgeschützter Zugriffspasswort",
+ "repeater_guestPasswordHelper": "Schreibgeschütztes Zugriffspasswort",
"repeater_radioSettings": "Funk Einstellungen",
"repeater_frequencyMhz": "Frequenz (MHz)",
"repeater_frequencyHelper": "300-2500 MHz",
@@ -1014,7 +1032,7 @@
"repeater_encryptedAdvertInterval": "Intervall der verschlüsselten Ankündigung",
"repeater_dangerZone": "Gefahrenzone",
"repeater_rebootRepeater": "Neustart Repeater",
- "repeater_rebootRepeaterSubtitle": "Wiederholen Sie das Repeater-Gerät.",
+ "repeater_rebootRepeaterSubtitle": "Repeater-Gerät neu starten.",
"repeater_rebootRepeaterConfirm": "Sind Sie sicher, dass Sie diesen Repeater neu starten möchten?",
"repeater_regenerateIdentityKey": "Schlüssel für die Identitätswiederherstellung",
"repeater_regenerateIdentityKeySubtitle": "Neuen öffentlichen/privaten Schlüsselpaar generieren",
@@ -1074,11 +1092,11 @@
}
},
"repeater_cliTitle": "Repeater CLI",
- "repeater_debugNextCommand": "Fehlersuche Nächster Befehl",
+ "repeater_debugNextCommand": "Fehlersuche des nächsten Befehls",
"repeater_commandHelp": "Hilfe",
"repeater_clearHistory": "Löschen der Historie",
"repeater_noCommandsSent": "Noch keine Befehle gesendet.",
- "repeater_typeCommandOrUseQuick": "Geben Sie einen Befehl unten ein oder verwenden Sie Schnellbefehle",
+ "repeater_typeCommandOrUseQuick": "Geben Sie unten einen Befehl ein oder verwenden Sie die Schnellbefehle",
"repeater_enterCommandHint": "Geben Sie den Befehl ein...",
"repeater_previousCommand": "Vorhergehende Aktion",
"repeater_nextCommand": "Nächste Aktion",
@@ -1120,7 +1138,7 @@
"repeater_cliHelpSetLat": "Legt die Breitengrad der Ankündigung fest. (dezimale Grad)",
"repeater_cliHelpSetLon": "Legt die Längengrade der Ankündigung fest. (dezimale Grad)",
"repeater_cliHelpSetRadio": "Legt komplett neue Radio-Parameter fest und speichert diese als Präferenzen. Benötigt einen \"Reboot\"-Befehl, um sie anzuwenden.",
- "repeater_cliHelpSetRxDelay": "Sets (experimentell) als Basis (muss > 1 sein für den Effekt) zur Anwendung einer leichten Verzögerung bei empfangenen Paketen, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.",
+ "repeater_cliHelpSetRxDelay": "Fügt eine leichte Verzögerung bei empfangenen Paketen hinzu, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.",
"repeater_cliHelpSetTxDelay": "Legt einen Faktor fest, der mit der Zeit bei voller Zuluft für ein Flood-Mode-Paket und mit einem zufälligen Slot-System multipliziert wird, um dessen Weiterleitung zu verzögern (um Kollisionen zu vermeiden).",
"repeater_cliHelpSetDirectTxDelay": "Ähnlich wie txdelay, aber zum Anwenden einer zufälligen Verzögerung bei der Weiterleitung von Direktmodus-Paketen.",
"repeater_cliHelpSetBridgeEnabled": "Brücke aktivieren/deaktivieren.",
@@ -1131,14 +1149,14 @@
"repeater_cliHelpSetAdcMultiplier": "Legt einen benutzerdefinierten Faktor zur Anpassung der gemeldeten Batteriewirkspannung fest (nur auf ausgewählten Boards unterstützt).",
"repeater_cliHelpTempRadio": "Legt vorübergehende Funkparameter für die angegebene Anzahl von Minuten fest und kehrt anschließend zu den ursprünglichen Funkparametern zurück (wird nicht in den Einstellungen gespeichert).",
"repeater_cliHelpSetPerm": "Ändert die ACL. Entfernt das passende Eintragen (durch Pubkey-Präfix), wenn \"permissions\" auf 0 steht. Fügt ein neues Eintragen hinzu, wenn die Pubkey-Hex-Länge vollständig ist und nicht bereits in der ACL vorhanden ist. Aktualisiert das Eintragen anhand des übereinstimmenden Pubkey-Präfix. Berechtigungsbits variieren je nach Firmware-Rolle, aber die unteren 2 Bits sind: 0 (Gast), 1 (Nur Lesen), 2 (Lesen/Schreiben), 3 (Admin)",
- "repeater_cliHelpGetBridgeType": "Ruft Brückentyp none, rs232, espnow ab.",
+ "repeater_cliHelpGetBridgeType": "Ruft Brückentyp: none, rs232, espnow ab.",
"repeater_cliHelpLogStart": "Beginnt die Paketprotokollierung in das Dateisystem.",
"repeater_cliHelpLogStop": "Stoppt das Paketprotokollieren in das Dateisystem.",
"repeater_cliHelpLogErase": "Löscht die Paketprotokolle aus dem Dateisystem.",
"repeater_cliHelpNeighbors": "Zeigt eine Liste anderer Repeater-Knoten an, die über Zero-Hop-Ankündigung gehört wurden. Jede Zeile ist id-prefix-hex:timestamp:snr-times-4",
"repeater_cliHelpNeighborRemove": "Entfernt das erste übereinstimmende Element (über Pubkey-Präfix (hex)) aus der Liste der Nachbarn.",
"repeater_cliHelpRegion": "Listet alle definierten Regionen auf.",
- "repeater_cliHelpRegionLoad": "Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingedruckt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile/des Befehls.",
+ "repeater_cliHelpRegionLoad": "Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingerückt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile.",
"repeater_cliHelpRegionGet": "Sucht die Region mit dem gegebenen Namenspräfix (oder \"\\\" für den globalen Scope) und antwortet mit \"-> region-name (parent-name) 'F'\".",
"repeater_cliHelpRegionPut": "Fügt eine Region-Definition mit dem angegebenen Namen hinzu oder aktualisiert diese.",
"repeater_cliHelpRegionRemove": "Löscht eine Regiondefinition mit dem angegebenen Namen. (muss genau übereinstimmen und keine Kindregionen haben)",
@@ -1231,7 +1249,7 @@
"channelPath_otherObservedPaths": "Sonstige beobachtete Pfade",
"channelPath_repeaterHops": "Repeater-Sprünge",
"channelPath_noHopDetails": "Die Detailangaben für dieses Paket sind nicht verfügbar.",
- "channelPath_messageDetails": "Nachrichtsdetails",
+ "channelPath_messageDetails": "Nachrichtendetails",
"channelPath_senderLabel": "Sender",
"channelPath_timeLabel": "Zeit",
"channelPath_repeatsLabel": "Wiederholungen",
@@ -1332,10 +1350,13 @@
"listFilter_az": "A-Z",
"listFilter_filters": "Filtere",
"listFilter_all": "Alle",
+ "listFilter_favorites": "Favoriten",
+ "listFilter_addToFavorites": "Zu Favoriten hinzufügen",
+ "listFilter_removeFromFavorites": "Aus Favoriten entfernen",
"listFilter_users": "Benutzer",
"listFilter_repeaters": "Repeater",
"listFilter_roomServers": "Raumserver",
- "listFilter_unreadOnly": "Nur nicht gelesen",
+ "listFilter_unreadOnly": "Nicht gelesen",
"listFilter_newGroup": "Neue Gruppe",
"@neighbors_errorLoading": {
"placeholders": {
@@ -1344,13 +1365,13 @@
}
}
},
- "repeater_neighbours": "Nachbarn",
- "repeater_neighboursSubtitle": "Anzahl der Hop-Nachbarn anzeigen.",
- "neighbors_receivedData": "Empfangene Nachbarendaten",
- "neighbors_requestTimedOut": "Nachbarn melden zeitweise Ausfall.",
+ "repeater_neighbors": "Nachbarn",
+ "repeater_neighborsSubtitle": "Anzahl der Hop-Nachbarn anzeigen.",
+ "neighbors_receivedData": "Empfangene Nachbarsdaten",
+ "neighbors_requestTimedOut": "Anfrage durch Timeout fehlgeschlagen.",
"neighbors_errorLoading": "Fehler beim Laden der Nachbarn: {error}",
- "neighbors_repeatersNeighbours": "Wiederholer Nachbarn",
- "neighbors_noData": "Keine Nachbardaten verfügbar.",
+ "neighbors_repeatersNeighbors": "Nachbarn",
+ "neighbors_noData": "Keine Nachbarsdaten verfügbar.",
"channels_joinPrivateChannel": "Treten Sie einem privaten Kanal bei",
"channels_joinPrivateChannelDesc": "Manuelle Eingabe eines geheimen Schlüssels.",
"channels_createPrivateChannel": "Erstelle einen privaten Kanal",
@@ -1377,8 +1398,8 @@
}
}
},
- "neighbors_heardAgo": "Hörte: {time} vor her.",
- "neighbors_unknownContact": "Unbekannte {pubkey}",
+ "neighbors_heardAgo": "Gehört vor: {time}",
+ "neighbors_unknownContact": "Unbekannt {pubkey}",
"settings_locationGPSEnable": "GPS aktivieren",
"settings_locationGPSEnableSubtitle": "Aktiviert GPS zur automatischen Aktualisierung des Standorts.",
"settings_locationIntervalSec": "Intervall für GPS (Sekunden)",
@@ -1473,16 +1494,612 @@
"community_deleteChannelsWarning": "Dies löscht auch {count} Kanal/Kanäle und deren Nachrichten.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Community \"{name}\" verlassen",
"community_addHashtagChannel": "Füge einen Community-Hashtag hinzu",
"community_addHashtagChannelDesc": "Füge einen Hashtag-Kanal für diese Community hinzu",
- "community_selectCommunity": "Wählen Sie Community",
+ "community_selectCommunity": "Wählen Sie eine Community",
"community_regularHashtag": "Regulärer Hashtag",
- "community_regularHashtagDesc": "Öffentliches Hashtag (jeder kann teilnehmen)",
+ "community_regularHashtagDesc": "Öffentlicher Hashtag (jeder kann teilnehmen)",
"community_communityHashtagDesc": "Nur für Mitglieder der Community",
"community_forCommunity": "Für {name}",
- "community_communityHashtag": "Community Hashtag"
+ "community_communityHashtag": "Community Hashtag",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "Neu generieren",
+ "community_secretRegenerated": "Wiederherstellung des Schlüssels für \"{name}\" erfolgreich",
+ "community_regenerateSecretConfirm": "Nehmen Sie den geheimen Schlüssel für \"{name}\" neu auf? Alle Mitglieder müssen den neuen QR-Code scannen, um die Kommunikation fortzusetzen.",
+ "community_regenerateSecret": "Neugenerierung des Schlüssels",
+ "community_secretUpdated": "Schlüssel für \"{name}\" aktualisiert",
+ "community_scanToUpdateSecret": "Scannen Sie den neuen QR-Code, um das Geheimnis für \"{name}\" zu aktualisieren.",
+ "community_updateSecret": "Aktualisieren Sie den Schlüssel",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_refreshTooltip": "Path Trace aktualisieren.",
+ "pathTrace_you": "Du",
+ "pathTrace_failed": "Pfadverfolgung fehlgeschlagen.",
+ "pathTrace_notAvailable": "Pfadverfolgung nicht verfügbar.",
+ "contacts_pathTrace": "Pfadverfolgung",
+ "contacts_ping": "Pingen",
+ "contacts_repeaterPathTrace": "Pfadverfolgung zum Repeater",
+ "contacts_repeaterPing": "Repeater pingen",
+ "contacts_roomPathTrace": "Pfadverfolgung zum Raumserver",
+ "contacts_roomPing": "Raumserver anpingen",
+ "contacts_pathTraceTo": "Route nach {name} verfolgen",
+ "contacts_chatTraceRoute": "Pfadverfolgungsroute",
+ "appSettings_languageRu": "Russisch",
+ "contacts_invalidAdvertFormat": "Ungültige Kontaktdaten",
+ "contacts_clipboardEmpty": "Die Zwischenablage ist leer.",
+ "appSettings_languageUk": "Ukrainisch",
+ "appSettings_enableMessageTracing": "Nachrichtenverfolgung aktivieren",
+ "appSettings_enableMessageTracingSubtitle": "Detaillierte Routing- und Timing-Metadaten für Nachrichten anzeigen",
+ "contacts_contactImported": "Kontakt wurde importiert.",
+ "contacts_contactImportFailed": "Kontakt konnte nicht importiert werden",
+ "contacts_zeroHopAdvert": "Zero-Hop-Ankündigung",
+ "contacts_floodAdvert": "Flut-Ankündigung",
+ "contacts_addContactFromClipboard": "Kontakt aus Zwischenablage hinzufügen",
+ "contacts_ShareContactZeroHop": "Kontakt über Anzeige teilen",
+ "contacts_copyAdvertToClipboard": "Ankündigung in die Zwischenablage kopieren",
+ "contacts_ShareContact": "Kontakt in die Zwischenablage kopieren",
+ "contacts_zeroHopContactAdvertFailed": "Kontakt konnte nicht gesendet werden.",
+ "contacts_zeroHopContactAdvertSent": "Kontakt über Anzeige gesendet",
+ "contacts_contactAdvertCopied": "Anzeige in die Zwischenablage kopiert.",
+ "contacts_contactAdvertCopyFailed": "Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.",
+ "notification_activityTitle": "MeshCore Aktivität",
+ "notification_messagesCount": "{count} {count, plural, =1{Nachricht} other{Nachrichten}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{Kanalnachricht} other{Kanalnachrichten}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{neuer Knoten} other{neue Knoten}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "Neuer {contactType} entdeckt",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "Neue Nachricht empfangen",
+ "settings_gpxExportAll": "Alle Knoten als GPX exportieren",
+ "settings_gpxExportAllSubtitle": "Exportiert alle Knoten mit einem Standort in eine GPX-Datei.",
+ "settings_gpxExportRepeaters": "Repeater und Raumserver als GPX exportieren",
+ "settings_gpxExportContacts": "Kontakte als GPX exportieren",
+ "settings_gpxExportRepeatersSubtitle": "Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.",
+ "settings_gpxExportContactsSubtitle": "Exportiert Kontakte mit einem Ort in eine GPX-Datei.",
+ "settings_gpxExportRepeatersRoom": "Repeater- und Raumserver-Standorte",
+ "settings_gpxExportChat": "Kontaktstandorte",
+ "settings_gpxExportNoContacts": "Keine Kontakte zum Exportieren.",
+ "settings_gpxExportError": "Beim Export ist ein Fehler aufgetreten.",
+ "settings_gpxExportNotAvailable": "Nicht auf Ihrem Gerät/Betriebssystem unterstützt",
+ "settings_gpxExportSuccess": "GPX-Datei erfolgreich exportiert.",
+ "settings_gpxExportAllContacts": "Alle Kontaktstandorte",
+ "settings_gpxExportShareSubject": "GPX-Kartendaten aus meshcore-open exportieren",
+ "settings_gpxExportShareText": "GPX-Kartendaten aus meshcore-open exportiert",
+ "pathTrace_someHopsNoLocation": "Bei einer oder mehreren Knoten fehlt der Standort!",
+ "map_removeLast": "Letztes Entfernen",
+ "map_tapToAdd": "Tippen Sie auf Knoten, um sie zum Pfad hinzuzufügen.",
+ "map_runTrace": "Pfadverlauf ausführen",
+ "pathTrace_clearTooltip": "Pfad löschen",
+ "map_pathTraceCancelled": "Pfadverfolgung abgebrochen.",
+ "scanner_bluetoothOffMessage": "Bitte aktivieren Sie Bluetooth, um nach Geräten zu suchen.",
+ "scanner_chromeRequired": "Chrome Browser erforderlich",
+ "scanner_chromeRequiredMessage": "Diese Webanwendung erfordert Google Chrome oder einen Chromium-basierten Browser für die Bluetooth-Unterstützung.",
+ "scanner_bluetoothOff": "Bluetooth ist deaktiviert.",
+ "scanner_enableBluetooth": "Bluetooth aktivieren",
+ "snrIndicator_lastSeen": "Zuletzt gesehen",
+ "snrIndicator_nearByRepeaters": "In der Nähe befindliche Repeater",
+ "chat_ShowAllPaths": "Alle Pfade anzeigen",
+ "settings_clientRepeat": "Wiederholung, ohne Stromanschluss",
+ "settings_clientRepeatFreqWarning": "Die Kommunikation ohne Stromversorgung erfordert Frequenzen von 433, 869 oder 918 MHz.",
+ "settings_clientRepeatSubtitle": "Ermöglichen Sie diesem Gerät, Mesh-Pakete für andere zu wiederholen.",
+ "settings_aboutOpenMeteoAttribution": "LOS-Höhendaten: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Einheiten",
+ "appSettings_unitsMetric": "Metrisch (m/km)",
+ "appSettings_unitsImperial": "Imperial (ft/mi)",
+ "map_lineOfSight": "Sichtlinie",
+ "map_losScreenTitle": "Sichtlinie",
+ "losSelectStartEnd": "Wählen Sie Start- und Endknoten für LOS aus.",
+ "losRunFailed": "Sichtlinienprüfung fehlgeschlagen: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Löschen Sie alle Punkte",
+ "losRunToViewElevationProfile": "Führen Sie LOS aus, um das Höhenprofil anzuzeigen",
+ "losMenuTitle": "LOS-Menü",
+ "losMenuSubtitle": "Tippen Sie auf Knoten oder drücken Sie lange auf die Karte, um benutzerdefinierte Punkte anzuzeigen",
+ "losShowDisplayNodes": "Anzeigeknoten anzeigen",
+ "losCustomPoints": "Benutzerdefinierte Punkte",
+ "losCustomPointLabel": "Benutzerdefiniert {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punkt A",
+ "losPointB": "Punkt B",
+ "losAntennaA": "Antenne A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenne B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Führen Sie LOS aus",
+ "losNoElevationData": "Keine Höhendaten",
+ "losProfileClear": "{distance} {distanceUnit}, freie Sichtlinie, Mindestabstand {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blockiert durch {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: Überprüfen...",
+ "losStatusNoData": "LOS: keine Daten",
+ "losStatusSummary": "Sichtlinie: {clear}/{total} frei, {blocked} blockiert, {unknown} unbekannt",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Für eine oder mehrere Proben sind keine Höhendaten verfügbar.",
+ "losErrorInvalidInput": "Ungültige Punkte/Höhendaten für die LOS-Berechnung.",
+ "losRenameCustomPoint": "Benennen Sie den benutzerdefinierten Punkt um",
+ "losPointName": "Punktname",
+ "losShowPanelTooltip": "LOS-Panel anzeigen",
+ "losHidePanelTooltip": "LOS-Panel ausblenden",
+ "losElevationAttribution": "Höhendaten: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Funkhorizont",
+ "losLegendLosBeam": "Sichtlinie",
+ "losLegendTerrain": "Gelände",
+ "losFrequencyLabel": "Frequenz",
+ "losFrequencyInfoTooltip": "Details zur Berechnung anzeigen",
+ "losFrequencyDialogTitle": "Berechnung des Funkhorizonts",
+ "losFrequencyDialogDescription": "Ausgehend von k={baselineK} bei {baselineFreq} MHz passt die Berechnung den k-Faktor für das aktuelle {frequencyMHz} MHz-Band an, das die gekrümmte Funkhorizontobergrenze definiert.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Ungelesen",
+ "contacts_searchContactsNoNumber": "Kontakte suchen...",
+ "contacts_searchRepeaters": "Suche {number}{str} Repeater...",
+ "contacts_searchFavorites": "Suche {number}{str} Favoriten...",
+ "contacts_searchUsers": "Suche {number}{str} Benutzer...",
+ "contacts_searchRoomServers": "Suche {number}{str} Raumserver...",
+ "settings_contactSettings": "Kontakteinstellungen",
+ "contactsSettings_otherTitle": "Weitere Einstellungen zu Kontakten",
+ "contactsSettings_title": "Kontakteinstellungen",
+ "contactsSettings_autoAddTitle": "Automatische Erkennung",
+ "contactsSettings_autoAddUsersTitle": "Automatische Hinzufügung von Benutzern",
+ "settings_contactSettingsSubtitle": "Einstellungen für das Hinzufügen von Kontakten",
+ "contactsSettings_autoAddSensorsTitle": "Automatisch Sensoren hinzufügen",
+ "contactsSettings_autoAddUsersSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Benutzer hinzuzufügen",
+ "contactsSettings_autoAddRoomServersTitle": "Automatisch Raumservers hinzufügen",
+ "contactsSettings_autoAddRoomServersSubtitle": "Ermöglichen Sie dem Begleiter, entdeckte Raumserver automatisch hinzuzufügen",
+ "contactsSettings_autoAddRepeatersTitle": "Automatisch Repeater hinzufügen",
+ "contactsSettings_autoAddRepeatersSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Repeater hinzuzufügen.",
+ "discoveredContacts_noMatching": "Keine passenden Kontakte",
+ "discoveredContacts_searchHint": "Entdeckte Kontakte suchen",
+ "discoveredContacts_addContact": "Kontakt hinzufügen",
+ "discoveredContacts_contactAdded": "Kontakt hinzugefügt",
+ "discoveredContacts_deleteContact": "Kontakt löschen",
+ "discoveredContacts_Title": "Entdeckte Kontakte",
+ "discoveredContacts_copyContact": "Kontakt in die Zwischenablage kopieren",
+ "contactsSettings_overwriteOldestTitle": "Überschreiben des Ältesten",
+ "contactsSettings_autoAddSensorsSubtitle": "Ermöglichen Sie dem Begleiter, automatisch entdeckte Sensoren hinzuzufügen",
+ "contactsSettings_overwriteOldestSubtitle": "Wenn die Kontaktliste voll ist, wird der älteste nicht favorisierte Kontakt ersetzt.",
+ "common_deleteAll": "Alles löschen",
+ "discoveredContacts_deleteContactAllContent": "Sind Sie sicher, dass Sie alle gefundenen Kontakte löschen möchten?",
+ "discoveredContacts_deleteContactAll": "Alle entdeckten Kontakte löschen",
+ "map_showGuessedLocations": "Zeige die vermuteten Knotenpositionen",
+ "map_guessedLocation": "Geschätzter Ort",
+ "usbScreenSubtitle": "Wählen Sie ein erkannten serielles Gerät aus und verbinden Sie es direkt mit Ihrem MeshCore-Knoten.",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenTitle": "Verbinden über USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenStatus": "Wählen Sie ein USB-Gerät aus",
+ "usbScreenNote": "Die USB-Serielle Schnittstelle ist auf unterstützten Android-Geräten und Desktop-Plattformen aktiv.",
+ "usbScreenEmptyState": "Keine USB-Geräte gefunden. Schließen Sie eines an und aktualisieren Sie.",
+ "usbErrorPermissionDenied": "Die USB-Berechtigung wurde abgelehnt.",
+ "usbErrorDeviceMissing": "Das ausgewählte USB-Gerät ist nicht mehr verfügbar.",
+ "usbErrorInvalidPort": "Wählen Sie ein gültiges USB-Gerät aus.",
+ "usbErrorBusy": "Eine weitere Anfrage für eine USB-Verbindung ist bereits in Bearbeitung.",
+ "usbErrorNotConnected": "Es ist kein USB-Gerät angeschlossen.",
+ "usbErrorOpenFailed": "Fehlgeschlagen beim Öffnen des ausgewählten USB-Geräts.",
+ "usbErrorConnectFailed": "Keine Verbindung zum ausgewählten USB-Gerät hergestellt.",
+ "usbErrorUnsupported": "Die USB-Serielle Schnittstelle wird auf dieser Plattform nicht unterstützt.",
+ "usbErrorAlreadyActive": "Eine USB-Verbindung ist bereits hergestellt.",
+ "usbErrorNoDeviceSelected": "Kein USB-Gerät wurde ausgewählt.",
+ "usbErrorPortClosed": "Die USB-Verbindung ist nicht aktiv.",
+ "usbFallbackDeviceName": "Web-Serielle Geräte",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Suche nach USB-Geräten...",
+ "usbStatus_notConnected": "Wählen Sie ein USB-Gerät aus",
+ "usbStatus_connecting": "Verbindung zum USB-Gerät...",
+ "usbConnectionFailed": "Fehler beim USB-Verbindungsaufbau: {error}",
+ "usbErrorConnectTimedOut": "Verbindung konnte nicht hergestellt werden. Stellen Sie sicher, dass das Gerät die entsprechende USB-Firmware enthält.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostLabel": "IP-Adresse",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpScreenTitle": "Verbinden über TCP",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Geben Sie den Endpunkt ein und verbinden Sie sich.",
+ "tcpStatus_connectingTo": "Verbindung zu {endpoint}...",
+ "tcpErrorHostRequired": "Eine IP-Adresse ist erforderlich.",
+ "tcpErrorPortInvalid": "Die Portnummer muss zwischen 1 und 65535 liegen.",
+ "tcpErrorUnsupported": "Die TCP-Übertragung wird auf dieser Plattform nicht unterstützt.",
+ "tcpErrorTimedOut": "Die TCP-Verbindung ist abgelaufen.",
+ "tcpConnectionFailed": "Fehler beim TCP-Verbindungsaufbau: {error}",
+ "map_showDiscoveryContacts": "Entdeckungs-Kontakte anzeigen",
+ "map_setAsMyLocation": "Als meine aktuelle Position festlegen",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_allowByContact": "Zulassen durch Kontaktflaggen",
+ "settings_privacy": "Datenschutzeinstellungen",
+ "settings_allowAll": "Alles zulassen",
+ "settings_privacySettingsDescription": "Wählen Sie die Informationen, die Ihr Gerät mit anderen teilt.",
+ "settings_denyAll": "Alle ablehnen",
+ "settings_privacySubtitle": "Steuern Sie die Informationen, die freigegeben werden.",
+ "settings_telemetryLocationMode": "Telemetrie-Ortsmodus",
+ "settings_telemetryEnvironmentMode": "Telemetrie-Umgebungsmodus",
+ "settings_advertLocation": "Anzeigenort",
+ "settings_advertLocationSubtitle": "Ort in der Anzeige einbeziehen",
+ "settings_telemetryBaseMode": "Telemetrie-Basismodus",
+ "contact_teleBase": "Telemetriebasis",
+ "contact_teleBaseSubtitle": "Erlauben des Freigebens des Batteriestands und der grundlegenden Telemetrie",
+ "contact_teleLoc": "Telemetrieort",
+ "contact_teleLocSubtitle": "Teilen von Standortdaten zulassen",
+ "contact_info": "Kontaktinformationen",
+ "contact_settings": "Kontakteinstellungen",
+ "contact_telemetry": "Telemetrie",
+ "contact_teleEnv": "Telemetrieumgebung",
+ "contact_lastSeen": "Zuletzt gesehen",
+ "contact_clearChat": "Chat löschen",
+ "contact_teleEnvSubtitle": "Teilen von Umgebungsensordaten zulassen",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeightSubtitle": "Ausgangsgewicht für neu entdeckte Pfade",
+ "appSettings_maxRouteWeightSubtitle": "Maximales Gewicht, das ein Weg durch erfolgreiche Lieferungen erreichen kann.",
+ "appSettings_maxRouteWeight": "Maximale Gesamtstreckenlänge",
+ "appSettings_initialRouteWeight": "Anfangs-Streckengewicht",
+ "appSettings_routeWeightSuccessIncrement": "Erhöhung des Erfolgsgewichts",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Gewicht, das einem Pfad nach erfolgreicher Lieferung hinzugefügt wird.",
+ "appSettings_routeWeightFailureDecrement": "Reduzierung des Gewichts bei Fehlern",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Gewicht, das nach einem fehlgeschlagenen Versand von einem Weg entfernt wurde",
+ "appSettings_maxMessageRetries": "Maximale Anzahl an Wiederholungsversuchen",
+ "appSettings_maxMessageRetriesSubtitle": "Anzahl der Versuche, eine Nachricht erneut zu senden, bevor sie als fehlgeschlagen markiert wird.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Telemetriemodus aktualisiert",
+ "settings_multiAck": "Mehrfach-Bestätigungen: {value}",
+ "map_showOverlaps": "Überlappungen der Repeater-Taste",
+ "map_runTraceWithReturnPath": "Auf dem gleichen Pfad zurückkehren.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Bitte warten Sie einen Moment, bevor Sie erneut senden.",
+ "appSettings_jumpToOldestUnread": "Zum ältesten, nicht gelesenen Eintrag springen",
+ "appSettings_languageHu": "Ungarisch",
+ "appSettings_jumpToOldestUnreadSubtitle": "Wenn Sie ein Chatfenster öffnen, in dem Nachrichten vorhanden sind, die noch nicht gelesen wurden, scrollen Sie zu der ersten unlesenen Nachricht, anstatt zur neuesten.",
+ "appSettings_languageJa": "Japanisch",
+ "appSettings_languageKo": "Koreanisch",
+ "radioStats_tooltip": "Daten zu Radio- und Mesh-Netzwerken",
+ "radioStats_screenTitle": "Senderinformationen",
+ "radioStats_notConnected": "Verbinden Sie ein Gerät, um Radiostatisiken anzuzeigen.",
+ "radioStats_firmwareTooOld": "Für die Verwendung der Funkstatistiken ist die Firmware-Version 8 oder höher erforderlich.",
+ "radioStats_waiting": "Warte auf Daten…",
+ "radioStats_noiseFloor": "Rauschpegel: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Letzter RSSI-Wert: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Letzter SNR: {snr} dB",
+ "radioStats_txAir": "Gesamt-TX-Zeit: {seconds} s",
+ "radioStats_rxAir": "Gesamt-RX-Zeit: {seconds} s",
+ "radioStats_chartCaption": "Rauschpegel (dBm) basierend auf den letzten Messwerten.",
+ "radioStats_stripNoise": "Rauschpegel: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Abrufen von Radiostatus…",
+ "radioStats_settingsTile": "Senderinformationen",
+ "radioStats_settingsSubtitle": "Rauschpegel, RSSI, Signal-Rausch-Verhältnis (SNR) und Nutzzeit",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_title": "Übersetzung",
+ "translation_composerTitle": "Übersetzen Sie vor dem Versenden",
+ "translation_enableSubtitle": "Nachrichten empfangen und übersetzen sowie die Möglichkeit bieten, Nachrichten vor dem Versenden zu übersetzen.",
+ "translation_enableTitle": "Aktivieren Sie die Übersetzung",
+ "translation_composerSubtitle": "Steuert den Standardzustand des Icons für die Übersetzung des Komponisten.",
+ "translation_targetLanguage": "Zielsprache",
+ "translation_useAppLanguage": "Verwenden Sie die App-Sprache",
+ "translation_downloadedModelLabel": "Heruntergeladenes Modell",
+ "translation_presetModelLabel": "Vordefinierter Hugging Face-Modell",
+ "translation_manualUrlLabel": "URL für das manuelle Modell",
+ "translation_downloadModel": "Modell herunterladen",
+ "translation_downloading": "Herunterladen...",
+ "translation_working": "Arbeiten...",
+ "translation_stop": "Stopp",
+ "translation_mergingChunks": "Zusammenführen der heruntergeladenen Teile in die finale Datei...",
+ "translation_downloadedModels": "Heruntergeladene Modelle",
+ "translation_deleteModel": "Modell löschen",
+ "translation_modelDownloaded": "Übersetzungsmotor heruntergeladen.",
+ "translation_downloadStopped": "Herunterladen abgebrochen.",
+ "translation_downloadFailed": "Download fehlgeschlagen: {error}",
+ "translation_enterUrlFirst": "Geben Sie zunächst die URL eines Modells ein.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_messageTranslation": "Nachricht übersetzen",
+ "translation_composerEnabledHint": "Die Nachrichten werden vor dem Versenden übersetzt.",
+ "translation_translateBeforeSending": "Übersetzen Sie vor dem Versenden",
+ "translation_composerDisabledHint": "Nachrichten in der ursprünglichen, getippten Sprache senden.",
+ "translation_translateTo": "Übersetzen Sie auf {language}",
+ "translation_translationOptions": "Übersetzungsmöglichkeiten",
+ "translation_systemLanguage": "Sprache des Systems",
+ "scanner_linuxPairingShowPin": "PIN anzeigen",
+ "scanner_linuxPairingHidePin": "PIN ausblenden",
+ "scanner_linuxPairingPinTitle": "Bluetooth-Paarungs-PIN",
+ "scanner_linuxPairingPinPrompt": "Geben Sie die PIN für {deviceName} ein (leer lassen, falls keine).",
+ "repeater_cliQuickClockSync": "Uhr Synchronisieren",
+ "repeater_cliQuickDiscovery": "Entdecke Nachbarn",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "Uhrzeit-Synchronisation nach dem Anmelden",
+ "repeater_clockSyncAfterLoginSubtitle": "Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden.",
+ "repeater_guest": "Informationen zu Repeatern",
+ "repeater_guestTools": "Gastwerkzeuge",
+ "chat_sendMessage": "Nachricht senden",
+ "room_guest": "Informationen zum Room Server"
}
diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb
index 1c1ee51..8ad6bf3 100644
--- a/lib/l10n/app_en.arb
+++ b/lib/l10n/app_en.arb
@@ -1,18 +1,16 @@
{
"@@locale": "en",
-
"appTitle": "MeshCore Open",
-
"nav_contacts": "Contacts",
"nav_channels": "Channels",
"nav_map": "Map",
-
"common_cancel": "Cancel",
"common_ok": "OK",
"common_connect": "Connect",
"common_unknownDevice": "Unknown Device",
"common_save": "Save",
"common_delete": "Delete",
+ "common_deleteAll": "Delete All",
"common_close": "Close",
"common_edit": "Edit",
"common_add": "Add",
@@ -35,17 +33,78 @@
"common_voltageValue": "{volts} V",
"@common_voltageValue": {
"placeholders": {
- "volts": {"type": "String"}
+ "volts": {
+ "type": "String"
+ }
}
},
"common_percentValue": "{percent}%",
"@common_percentValue": {
"placeholders": {
- "percent": {"type": "int"}
+ "percent": {
+ "type": "int"
+ }
}
},
-
"scanner_title": "MeshCore Open",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "Connect over TCP",
+ "tcpHostLabel": "IP Address",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Enter endpoint and connect",
+ "tcpStatus_connectingTo": "Connecting to {endpoint}...",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpErrorHostRequired": "IP address is required.",
+ "tcpErrorPortInvalid": "Port must be between 1 and 65535.",
+ "tcpErrorUnsupported": "TCP transport is not supported on this platform.",
+ "tcpErrorTimedOut": "TCP connection timed out.",
+ "tcpConnectionFailed": "TCP connection failed: {error}",
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbScreenTitle": "Connect over USB",
+ "usbScreenSubtitle": "Choose a detected serial device and connect directly to your MeshCore node.",
+ "usbScreenStatus": "Select a USB device",
+ "usbScreenNote": "USB serial is active on supported Android devices and desktop platforms.",
+ "usbScreenEmptyState": "No USB devices found. Plug one in and refresh.",
+ "usbErrorPermissionDenied": "USB permission was denied.",
+ "usbErrorDeviceMissing": "The selected USB device is no longer available.",
+ "usbErrorInvalidPort": "Select a valid USB device.",
+ "usbErrorBusy": "Another USB connection request is already in progress.",
+ "usbErrorNotConnected": "No USB device is connected.",
+ "usbErrorOpenFailed": "Failed to open the selected USB device.",
+ "usbErrorConnectFailed": "Failed to connect to the selected USB device.",
+ "usbErrorUnsupported": "USB serial is not supported on this platform.",
+ "usbErrorAlreadyActive": "A USB connection is already active.",
+ "usbErrorNoDeviceSelected": "No USB device was selected.",
+ "usbErrorPortClosed": "The USB connection is not open.",
+ "usbErrorConnectTimedOut": "Connection timed out. Make sure the device has USB Companion firmware.",
+ "usbFallbackDeviceName": "Web Serial Device",
+ "usbStatus_notConnected": "Select a USB device",
+ "usbStatus_connecting": "Connecting to USB device...",
+ "usbStatus_searching": "Searching for USB devices...",
+ "usbConnectionFailed": "USB connection failed: {error}",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
"scanner_scanning": "Scanning for devices...",
"scanner_connecting": "Connecting...",
"scanner_disconnecting": "Disconnecting...",
@@ -53,7 +112,9 @@
"scanner_connectedTo": "Connected to {deviceName}",
"@scanner_connectedTo": {
"placeholders": {
- "deviceName": {"type": "String"}
+ "deviceName": {
+ "type": "String"
+ }
}
},
"scanner_searchingDevices": "Searching for MeshCore devices...",
@@ -61,15 +122,21 @@
"scanner_connectionFailed": "Connection failed: {error}",
"@scanner_connectionFailed": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
+
"scanner_stop": "Stop",
"scanner_scan": "Scan",
-
+ "scanner_bluetoothOff": "Bluetooth is off",
+ "scanner_bluetoothOffMessage": "Please turn on Bluetooth to scan for devices",
+ "scanner_chromeRequired": "Chrome Browser Required",
+ "scanner_chromeRequiredMessage": "This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.",
+ "scanner_enableBluetooth": "Enable Bluetooth",
"device_quickSwitch": "Quick switch",
"device_meshcore": "MeshCore",
-
"settings_title": "Settings",
"settings_deviceInfo": "Device Info",
"settings_appSettings": "App Settings",
@@ -93,11 +160,33 @@
"settings_locationIntervalInvalid": "Interval must be at least 60 seconds, and less than 86400 seconds.",
"settings_latitude": "Latitude",
"settings_longitude": "Longitude",
+ "settings_contactSettings": "Contact Settings",
+ "settings_contactSettingsSubtitle": "Settings for how contacts are added.",
"settings_privacyMode": "Privacy Mode",
"settings_privacyModeSubtitle": "Hide name/location in advertisements",
"settings_privacyModeToggle": "Toggle privacy mode to hide your name and location in advertisements.",
"settings_privacyModeEnabled": "Privacy mode enabled",
"settings_privacyModeDisabled": "Privacy mode disabled",
+ "settings_privacy": "Privacy Settings",
+ "settings_privacySubtitle": "Control what information is shared.",
+ "settings_privacySettingsDescription": "Choose what information your device shares with others.",
+ "settings_denyAll": "Deny all",
+ "settings_allowByContact": "Allow by contact flags",
+ "settings_allowAll": "Allow all",
+ "settings_telemetryBaseMode": "Telemetry Base Mode",
+ "settings_telemetryLocationMode": "Telemetry Location Mode",
+ "settings_telemetryEnvironmentMode": "Telemetry Environment Mode",
+ "settings_advertLocation": "Advert Location",
+ "settings_advertLocationSubtitle": "Include location in advert.",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_telemetryModeUpdated": "Telemetry mode updated",
"settings_actions": "Actions",
"settings_sendAdvertisement": "Send Advertisement",
"settings_sendAdvertisementSubtitle": "Broadcast presence now",
@@ -119,11 +208,14 @@
"settings_aboutVersion": "MeshCore Open v{version}",
"@settings_aboutVersion": {
"placeholders": {
- "version": {"type": "String"}
+ "version": {
+ "type": "String"
+ }
}
},
"settings_aboutLegalese": "2026 MeshCore Open Source Project",
"settings_aboutDescription": "An open-source Flutter client for MeshCore LoRa mesh networking devices.",
+ "settings_aboutOpenMeteoAttribution": "LOS elevation data: Open-Meteo (CC BY 4.0)",
"settings_infoName": "Name",
"settings_infoId": "ID",
"settings_infoStatus": "Status",
@@ -132,9 +224,6 @@
"settings_infoContactsCount": "Contacts Count",
"settings_infoChannelCount": "Channel Count",
"settings_presets": "Presets",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frequency (MHz)",
"settings_frequencyHelper": "300.0 - 2500.0",
"settings_frequencyInvalid": "Invalid frequency (300-2500 MHz)",
@@ -144,15 +233,17 @@
"settings_txPower": "TX Power (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Invalid TX power (0-22 dBm)",
- "settings_longRange": "Long Range",
- "settings_fastSpeed": "Fast Speed",
+ "settings_clientRepeat": "Off-Grid Repeat",
+ "settings_clientRepeatSubtitle": "Allow this device to repeat mesh packets for others",
+ "settings_clientRepeatFreqWarning": "Off-grid repeat requires 433, 869, or 918 MHz frequency",
"settings_error": "Error: {message}",
"@settings_error": {
"placeholders": {
- "message": {"type": "String"}
+ "message": {
+ "type": "String"
+ }
}
},
-
"appSettings_title": "App Settings",
"appSettings_appearance": "Appearance",
"appSettings_theme": "Theme",
@@ -174,6 +265,10 @@
"appSettings_languageNl": "Nederlands",
"appSettings_languageSk": "Slovenčina",
"appSettings_languageBg": "Български",
+ "appSettings_languageRu": "Русский",
+ "appSettings_languageUk": "Українська",
+ "appSettings_enableMessageTracing": "Enable Message Tracing",
+ "appSettings_enableMessageTracingSubtitle": "Show detailed routing and timing metadata for messages",
"appSettings_notifications": "Notifications",
"appSettings_enableNotifications": "Enable Notifications",
"appSettings_enableNotificationsSubtitle": "Receive notifications for messages and adverts",
@@ -195,12 +290,35 @@
"appSettings_autoRouteRotationSubtitle": "Cycle between best paths and flood mode",
"appSettings_autoRouteRotationEnabled": "Auto route rotation enabled",
"appSettings_autoRouteRotationDisabled": "Auto route rotation disabled",
+ "appSettings_maxRouteWeight": "Max Route Weight",
+ "appSettings_maxRouteWeightSubtitle": "Maximum weight a path can accumulate from successful deliveries",
+ "appSettings_initialRouteWeight": "Initial Route Weight",
+ "appSettings_initialRouteWeightSubtitle": "Starting weight for newly discovered paths",
+ "appSettings_routeWeightSuccessIncrement": "Success Weight Increment",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Weight added to a path after successful delivery",
+ "appSettings_routeWeightFailureDecrement": "Failure Weight Decrement",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Weight removed from a path after failed delivery",
+ "appSettings_maxMessageRetries": "Max Message Retries",
+ "appSettings_maxMessageRetriesSubtitle": "Number of retry attempts before marking a message as failed",
+ "path_routeWeight": "{weight}/{max}",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
"appSettings_battery": "Battery",
"appSettings_batteryChemistry": "Battery Chemistry",
"appSettings_batteryChemistryPerDevice": "Set per device ({deviceName})",
"@appSettings_batteryChemistryPerDevice": {
"placeholders": {
- "deviceName": {"type": "String"}
+ "deviceName": {
+ "type": "String"
+ }
}
},
"appSettings_batteryChemistryConnectFirst": "Connect to a device to choose",
@@ -219,7 +337,9 @@
"appSettings_timeFilterShowLast": "Show nodes from last {hours} hours",
"@appSettings_timeFilterShowLast": {
"placeholders": {
- "hours": {"type": "int"}
+ "hours": {
+ "type": "int"
+ }
}
},
"appSettings_mapTimeFilter": "Map Time Filter",
@@ -230,12 +350,19 @@
"appSettings_last24Hours": "Last 24 hours",
"appSettings_lastWeek": "Last week",
"appSettings_offlineMapCache": "Offline Map Cache",
+ "appSettings_unitsTitle": "Units",
+ "appSettings_unitsMetric": "Metric (m / km)",
+ "appSettings_unitsImperial": "Imperial (ft / mi)",
"appSettings_noAreaSelected": "No area selected",
"appSettings_areaSelectedZoom": "Area selected (zoom {minZoom}-{maxZoom})",
"@appSettings_areaSelectedZoom": {
"placeholders": {
- "minZoom": {"type": "int"},
- "maxZoom": {"type": "int"}
+ "minZoom": {
+ "type": "int"
+ },
+ "maxZoom": {
+ "type": "int"
+ }
}
},
"appSettings_debugCard": "Debug",
@@ -243,18 +370,75 @@
"appSettings_appDebugLoggingSubtitle": "Log app debug messages for troubleshooting",
"appSettings_appDebugLoggingEnabled": "App debug logging enabled",
"appSettings_appDebugLoggingDisabled": "App debug logging disabled",
-
"contacts_title": "Contacts",
"contacts_noContacts": "No contacts yet",
"contacts_contactsWillAppear": "Contacts will appear when devices advertise",
- "contacts_searchContacts": "Search contacts...",
+ "contacts_unread": "Unread",
+ "contacts_searchContactsNoNumber": "Search Contacts...",
+ "contacts_searchContacts": "Search {number}{str} Contacts...",
+ "@contacts_searchContacts": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchFavorites": "Search {number}{str} Favorites...",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "Search {number}{str} Users...",
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "Search {number}{str} Repeaters...",
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "Search {number}{str} Room servers...",
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
"contacts_noUnreadContacts": "No unread contacts",
"contacts_noContactsFound": "No contacts or groups found",
"contacts_deleteContact": "Delete Contact",
"contacts_removeConfirm": "Remove {contactName} from contacts?",
"@contacts_removeConfirm": {
"placeholders": {
- "contactName": {"type": "String"}
+ "contactName": {
+ "type": "String"
+ }
}
},
"contacts_manageRepeater": "Manage Repeater",
@@ -266,43 +450,64 @@
"contacts_deleteGroupConfirm": "Remove \"{groupName}\"?",
"@contacts_deleteGroupConfirm": {
"placeholders": {
- "groupName": {"type": "String"}
+ "groupName": {
+ "type": "String"
+ }
}
},
"contacts_newGroup": "New Group",
"contacts_groupName": "Group name",
"contacts_groupNameRequired": "Group name is required",
+ "contacts_groupNameReserved": "This group name is reserved",
"contacts_groupAlreadyExists": "Group \"{name}\" already exists",
"@contacts_groupAlreadyExists": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"contacts_filterContacts": "Filter contacts...",
"contacts_noContactsMatchFilter": "No contacts match your filter",
"contacts_noMembers": "No members",
- "contacts_lastSeenNow": "Last seen now",
- "contacts_lastSeenMinsAgo": "Last seen {minutes} mins ago",
+ "contacts_lastSeenNow": "recently",
+ "contacts_lastSeenMinsAgo": "~ {minutes} min.",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
- "minutes": {"type": "int"}
+ "minutes": {
+ "type": "int"
+ }
}
},
- "contacts_lastSeenHourAgo": "Last seen 1 hour ago",
- "contacts_lastSeenHoursAgo": "Last seen {hours} hours ago",
+ "contacts_lastSeenHourAgo": "~ 1 hour",
+ "contacts_lastSeenHoursAgo": "~ {hours} hours",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
- "hours": {"type": "int"}
+ "hours": {
+ "type": "int"
+ }
}
},
- "contacts_lastSeenDayAgo": "Last seen 1 day ago",
- "contacts_lastSeenDaysAgo": "Last seen {days} days ago",
+ "contacts_lastSeenDayAgo": "~ 1 day",
+ "contacts_lastSeenDaysAgo": "~ {days} days",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
- "days": {"type": "int"}
+ "days": {
+ "type": "int"
+ }
}
},
-
+ "contact_info": "Contact Info",
+ "contact_settings": "Contact Settings",
+ "contact_telemetry": "Telemetry",
+ "contact_lastSeen": "Last seen",
+ "contact_clearChat": "Clear Chat",
+ "contact_teleBase": "Telemetry Base",
+ "contact_teleBaseSubtitle": "Allow sharing battery level and basic telemetry",
+ "contact_teleLoc": "Telemetry Location",
+ "contact_teleLocSubtitle": "Allow sharing location data",
+ "contact_teleEnv": "Telemetry Environment",
+ "contact_teleEnvSubtitle": "Allow sharing environment sensor data",
"channels_title": "Channels",
"channels_noChannelsConfigured": "No channels configured",
"channels_addPublicChannel": "Add Public Channel",
@@ -311,7 +516,9 @@
"channels_channelIndex": "Channel {index}",
"@channels_channelIndex": {
"placeholders": {
- "index": {"type": "int"}
+ "index": {
+ "type": "int"
+ }
}
},
"channels_hashtagChannel": "Hashtag channel",
@@ -320,17 +527,31 @@
"channels_publicChannel": "Public channel",
"channels_privateChannel": "Private channel",
"channels_editChannel": "Edit channel",
+ "channels_muteChannel": "Mute channel",
+ "channels_unmuteChannel": "Unmute channel",
"channels_deleteChannel": "Delete channel",
"channels_deleteChannelConfirm": "Delete \"{name}\"? This cannot be undone.",
"@channels_deleteChannelConfirm": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleteFailed": "Failed to delete channel \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
}
},
"channels_channelDeleted": "Channel \"{name}\" deleted",
"@channels_channelDeleted": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"channels_addChannel": "Add Channel",
@@ -345,20 +566,26 @@
"channels_channelAdded": "Channel \"{name}\" added",
"@channels_channelAdded": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"channels_editChannelTitle": "Edit Channel {index}",
"@channels_editChannelTitle": {
"placeholders": {
- "index": {"type": "int"}
+ "index": {
+ "type": "int"
+ }
}
},
"channels_smazCompression": "SMAZ compression",
"channels_channelUpdated": "Channel \"{name}\" updated",
"@channels_channelUpdated": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"channels_publicChannelAdded": "Public channel added",
@@ -379,34 +606,50 @@
"channels_scanQrCodeComingSoon": "Coming soon",
"channels_enterHashtag": "Enter hashtag",
"channels_hashtagHint": "e.g. #team",
-
"chat_noMessages": "No messages yet",
+ "chat_sendMessage": "Send message",
+ "chat_sendMessageTo": "Send message to {name}",
+ "@chat_sendMessageTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"chat_sendMessageToStart": "Send a message to get started",
"chat_originalMessageNotFound": "Original message not found",
"chat_replyingTo": "Replying to {name}",
"@chat_replyingTo": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"chat_replyTo": "Reply to {name}",
"@chat_replyTo": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"chat_location": "Location",
"chat_sendMessageTo": "Send a message to {contactName}",
"@chat_sendMessageTo": {
"placeholders": {
- "contactName": {"type": "String"}
+ "contactName": {
+ "type": "String"
+ }
}
},
"chat_typeMessage": "Type a message...",
"chat_messageTooLong": "Message too long (max {maxBytes} bytes).",
"@chat_messageTooLong": {
"placeholders": {
- "maxBytes": {"type": "int"}
+ "maxBytes": {
+ "type": "int"
+ }
}
},
"chat_messageCopied": "Message copied",
@@ -415,8 +658,12 @@
"chat_retryCount": "Retry {current}/{max}",
"@chat_retryCount": {
"placeholders": {
- "current": {"type": "int"},
- "max": {"type": "int"}
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
}
},
"chat_sendGif": "Send GIF",
@@ -448,39 +695,53 @@
"debugFrame_length": "Frame Length: {count} bytes",
"@debugFrame_length": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"debugFrame_command": "Command: 0x{value}",
"@debugFrame_command": {
"placeholders": {
- "value": {"type": "String"}
+ "value": {
+ "type": "String"
+ }
}
},
"debugFrame_textMessageHeader": "Text Message Frame:",
"debugFrame_destinationPubKey": "- Destination PubKey: {pubKey}",
"@debugFrame_destinationPubKey": {
"placeholders": {
- "pubKey": {"type": "String"}
+ "pubKey": {
+ "type": "String"
+ }
}
},
"debugFrame_timestamp": "- Timestamp: {timestamp}",
"@debugFrame_timestamp": {
"placeholders": {
- "timestamp": {"type": "int"}
+ "timestamp": {
+ "type": "int"
+ }
}
},
"debugFrame_flags": "- Flags: 0x{value}",
"@debugFrame_flags": {
"placeholders": {
- "value": {"type": "String"}
+ "value": {
+ "type": "String"
+ }
}
},
"debugFrame_textType": "- Text Type: {type} ({label})",
"@debugFrame_textType": {
"placeholders": {
- "type": {"type": "int"},
- "label": {"type": "String"}
+ "type": {
+ "type": "int"
+ },
+ "label": {
+ "type": "String"
+ }
}
},
"debugFrame_textTypeCli": "CLI",
@@ -488,11 +749,14 @@
"debugFrame_text": "- Text: \"{text}\"",
"@debugFrame_text": {
"placeholders": {
- "text": {"type": "String"}
+ "text": {
+ "type": "String"
+ }
}
},
"debugFrame_hexDump": "Hex Dump:",
"chat_pathManagement": "Path Management",
+ "chat_ShowAllPaths": "Show all paths",
"chat_routingMode": "Routing mode",
"chat_autoUseSavedPath": "Auto (use saved path)",
"chat_forceFloodMode": "Force Flood Mode",
@@ -503,7 +767,9 @@
"chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}",
"@chat_hopsCount": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"chat_successes": "successes",
@@ -522,8 +788,12 @@
"chat_pathSetHops": "Path set: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
"@chat_pathSetHops": {
"placeholders": {
- "hopCount": {"type": "int"},
- "status": {"type": "String"}
+ "hopCount": {
+ "type": "int"
+ },
+ "status": {
+ "type": "String"
+ }
}
},
"chat_pathSavedLocally": "Saved locally. Connect to sync.",
@@ -538,7 +808,9 @@
"chat_hopsForced": "{count} hops (forced)",
"@chat_hopsForced": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"chat_floodAuto": "Flood (auto)",
@@ -547,23 +819,42 @@
"chat_unread": "Unread: {count}",
"@chat_unread": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
-
+ "chat_openLink": "Open Link?",
+ "chat_openLinkConfirmation": "Do you want to open this link in your browser?",
+ "chat_open": "Open",
+ "chat_couldNotOpenLink": "Could not open link: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Invalid link format",
"map_title": "Node Map",
+ "map_lineOfSight": "Line of Sight",
+ "map_losScreenTitle": "Line of Sight",
"map_noNodesWithLocation": "No nodes with location data",
"map_nodesNeedGps": "Nodes need to share their GPS coordinates\nto appear on the map",
"map_nodesCount": "Nodes: {count}",
"@map_nodesCount": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"map_pinsCount": "Pins: {count}",
"@map_pinsCount": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"map_chat": "Chat",
@@ -579,6 +870,7 @@
"map_source": "Source",
"map_flags": "Flags",
"map_shareMarkerHere": "Share marker here",
+ "map_setAsMyLocation": "Set as my location",
"map_pinLabel": "Pin label",
"map_label": "Label",
"map_pointOfInterest": "Point of interest",
@@ -589,7 +881,9 @@
"map_publicLocationShareConfirm": "You are about to share a location in {channelLabel}. This channel is public and anyone with the PSK can see it.",
"@map_publicLocationShareConfirm": {
"placeholders": {
- "channelLabel": {"type": "String"}
+ "channelLabel": {
+ "type": "String"
+ }
}
},
"map_connectToShareMarkers": "Connect to a device to share markers",
@@ -598,15 +892,24 @@
"map_chatNodes": "Chat Nodes",
"map_repeaters": "Repeaters",
"map_otherNodes": "Other Nodes",
+ "map_showOverlaps": "Repeater Key Overlaps",
"map_keyPrefix": "Key Prefix",
"map_filterByKeyPrefix": "Filter by key prefix",
"map_publicKeyPrefix": "Public key prefix",
"map_markers": "Markers",
"map_showSharedMarkers": "Show shared markers",
+ "map_showGuessedLocations": "Show guessed node locations",
+ "map_showDiscoveryContacts": "Show Discovery Contacts",
+ "map_guessedLocation": "Guessed location",
"map_lastSeenTime": "Last Seen Time",
"map_sharedPin": "Shared pin",
"map_joinRoom": "Join Room",
"map_manageRepeater": "Manage Repeater",
+ "map_tapToAdd": "Tap on nodes to add them to the path.",
+ "map_runTrace": "Run path trace",
+ "map_runTraceWithReturnPath": "Return back on the same path.",
+ "map_removeLast": "Remove Last",
+ "map_pathTraceCancelled": "Path trace cancelled.",
"mapCache_title": "Offline Map Cache",
"mapCache_selectAreaFirst": "Select an area to cache first",
"mapCache_noTilesToDownload": "No tiles to download for this area",
@@ -614,21 +917,29 @@
"mapCache_downloadTilesPrompt": "Download {count} tiles for offline use?",
"@mapCache_downloadTilesPrompt": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"mapCache_downloadAction": "Download",
"mapCache_cachedTiles": "Cached {count} tiles",
"@mapCache_cachedTiles": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"mapCache_cachedTilesWithFailed": "Cached {downloaded} tiles ({failed} failed)",
"@mapCache_cachedTilesWithFailed": {
"placeholders": {
- "downloaded": {"type": "int"},
- "failed": {"type": "int"}
+ "downloaded": {
+ "type": "int"
+ },
+ "failed": {
+ "type": "int"
+ }
}
},
"mapCache_clearOfflineCacheTitle": "Clear offline cache",
@@ -641,14 +952,20 @@
"mapCache_estimatedTiles": "Estimated tiles: {count}",
"@mapCache_estimatedTiles": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"mapCache_downloadedTiles": "Downloaded {completed} / {total}",
"@mapCache_downloadedTiles": {
"placeholders": {
- "completed": {"type": "int"},
- "total": {"type": "int"}
+ "completed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
}
},
"mapCache_downloadTilesButton": "Download Tiles",
@@ -656,36 +973,51 @@
"mapCache_failedDownloads": "Failed downloads: {count}",
"@mapCache_failedDownloads": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
"@mapCache_boundsLabel": {
"placeholders": {
- "north": {"type": "String"},
- "south": {"type": "String"},
- "east": {"type": "String"},
- "west": {"type": "String"}
+ "north": {
+ "type": "String"
+ },
+ "south": {
+ "type": "String"
+ },
+ "east": {
+ "type": "String"
+ },
+ "west": {
+ "type": "String"
+ }
}
},
-
"time_justNow": "Just now",
"time_minutesAgo": "{minutes}m ago",
"@time_minutesAgo": {
"placeholders": {
- "minutes": {"type": "int"}
+ "minutes": {
+ "type": "int"
+ }
}
},
"time_hoursAgo": "{hours}h ago",
"@time_hoursAgo": {
"placeholders": {
- "hours": {"type": "int"}
+ "hours": {
+ "type": "int"
+ }
}
},
"time_daysAgo": "{days}d ago",
"@time_daysAgo": {
"placeholders": {
- "days": {"type": "int"}
+ "days": {
+ "type": "int"
+ }
}
},
"time_hour": "hour",
@@ -698,18 +1030,16 @@
"time_months": "months",
"time_minutes": "minutes",
"time_allTime": "All Time",
-
"dialog_disconnect": "Disconnect",
"dialog_disconnectConfirm": "Are you sure you want to disconnect from this device?",
-
"login_repeaterLogin": "Repeater Login",
"login_roomLogin": "Room Server Login",
"login_password": "Password",
"login_enterPassword": "Enter password",
"login_savePassword": "Save password",
"login_savePasswordSubtitle": "Password will be stored securely on this device",
- "login_repeaterDescription": "Enter the repeater password to access settings and status.",
- "login_roomDescription": "Enter the room password to access settings and status.",
+ "login_repeaterDescription": "Enter the repeater password for guest or admin access.",
+ "login_roomDescription": "Enter the room password for guest or admin access.",
"login_routing": "Routing",
"login_routingMode": "Routing mode",
"login_autoUseSavedPath": "Auto (use saved path)",
@@ -719,32 +1049,39 @@
"login_attempt": "Attempt {current}/{max}",
"@login_attempt": {
"placeholders": {
- "current": {"type": "int"},
- "max": {"type": "int"}
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
}
},
"login_failed": "Login failed: {error}",
"@login_failed": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"login_failedMessage": "Login failed. Either the password is incorrect or the repeater is unreachable.",
-
-
"common_reload": "Reload",
"common_clear": "Clear",
-
"path_currentPath": "Current path: {path}",
"@path_currentPath": {
"placeholders": {
- "path": {"type": "String"}
+ "path": {
+ "type": "String"
+ }
}
},
"path_usingHopsPath": "Using {count} {count, plural, =1{hop} other{hops}} path",
"@path_usingHopsPath": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"path_enterCustomPath": "Enter Custom Path",
@@ -759,26 +1096,37 @@
"path_invalidHexPrefixes": "Invalid hex prefixes: {prefixes}",
"@path_invalidHexPrefixes": {
"placeholders": {
- "prefixes": {"type": "String"}
+ "prefixes": {
+ "type": "String"
+ }
}
},
"path_tooLong": "Path too long. Maximum 64 hops allowed.",
"path_setPath": "Set Path",
-
"repeater_management": "Repeater Management",
"room_management": "Room Server Management",
+ "repeater_guest": "Repeater Information",
+ "room_guest": "Room Server Information",
"repeater_managementTools": "Management Tools",
+ "repeater_guestTools": "Guest Tools",
"repeater_status": "Status",
"repeater_statusSubtitle": "View repeater status, stats, and neighbors",
"repeater_telemetry": "Telemetry",
"repeater_telemetrySubtitle": "View telemetry of sensors and system stats",
"repeater_cli": "CLI",
"repeater_cliSubtitle": "Send commands to the repeater",
- "repeater_neighbours": "Neighbors",
- "repeater_neighboursSubtitle": "View zero hop neighbors.",
+ "repeater_neighbors": "Neighbors",
+ "repeater_neighborsSubtitle": "View zero hop neighbors.",
"repeater_settings": "Settings",
"repeater_settingsSubtitle": "Configure repeater parameters",
-
+ "repeater_clockSyncAfterLogin": "Clock sync after login",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Automatically send \"clock sync\" after a successful login",
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
"repeater_statusTitle": "Repeater Status",
"repeater_routingMode": "Routing mode",
"repeater_autoUseSavedPath": "Auto (use saved path)",
@@ -789,7 +1137,9 @@
"repeater_errorLoadingStatus": "Error loading status: {error}",
"@repeater_errorLoadingStatus": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"repeater_systemInformation": "System Information",
@@ -811,42 +1161,67 @@
"repeater_daysHoursMinsSecs": "{days} days {hours}h {minutes}m {seconds}s",
"@repeater_daysHoursMinsSecs": {
"placeholders": {
- "days": {"type": "int"},
- "hours": {"type": "int"},
- "minutes": {"type": "int"},
- "seconds": {"type": "int"}
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
}
},
"repeater_packetTxTotal": "Total: {total}, Flood: {flood}, Direct: {direct}",
"@repeater_packetTxTotal": {
"placeholders": {
- "total": {"type": "int"},
- "flood": {"type": "String"},
- "direct": {"type": "String"}
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
}
},
"repeater_packetRxTotal": "Total: {total}, Flood: {flood}, Direct: {direct}",
"@repeater_packetRxTotal": {
"placeholders": {
- "total": {"type": "int"},
- "flood": {"type": "String"},
- "direct": {"type": "String"}
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
}
},
"repeater_duplicatesFloodDirect": "Flood: {flood}, Direct: {direct}",
"@repeater_duplicatesFloodDirect": {
"placeholders": {
- "flood": {"type": "String"},
- "direct": {"type": "String"}
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
}
},
"repeater_duplicatesTotal": "Total: {total}",
"@repeater_duplicatesTotal": {
"placeholders": {
- "total": {"type": "int"}
+ "total": {
+ "type": "int"
+ }
}
},
-
"repeater_settingsTitle": "Repeater Settings",
"repeater_basicSettings": "Basic Settings",
"repeater_repeaterName": "Repeater Name",
@@ -880,14 +1255,18 @@
"repeater_localAdvertIntervalMinutes": "{minutes} minutes",
"@repeater_localAdvertIntervalMinutes": {
"placeholders": {
- "minutes": {"type": "int"}
+ "minutes": {
+ "type": "int"
+ }
}
},
"repeater_floodAdvertInterval": "Flood Advertisement Interval",
"repeater_floodAdvertIntervalHours": "{hours} hours",
"@repeater_floodAdvertIntervalHours": {
"placeholders": {
- "hours": {"type": "int"}
+ "hours": {
+ "type": "int"
+ }
}
},
"repeater_encryptedAdvertInterval": "Encrypted Advertisement Interval",
@@ -905,13 +1284,17 @@
"repeater_commandSent": "Command sent: {command}",
"@repeater_commandSent": {
"placeholders": {
- "command": {"type": "String"}
+ "command": {
+ "type": "String"
+ }
}
},
"repeater_errorSendingCommand": "Error sending command: {error}",
"@repeater_errorSendingCommand": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"repeater_confirm": "Confirm",
@@ -919,7 +1302,9 @@
"repeater_errorSavingSettings": "Error saving settings: {error}",
"@repeater_errorSavingSettings": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"repeater_refreshBasicSettings": "Refresh Basic Settings",
@@ -933,16 +1318,19 @@
"repeater_refreshed": "{label} refreshed",
"@repeater_refreshed": {
"placeholders": {
- "label": {"type": "String"}
+ "label": {
+ "type": "String"
+ }
}
},
"repeater_errorRefreshing": "Error refreshing {label}",
"@repeater_errorRefreshing": {
"placeholders": {
- "label": {"type": "String"}
+ "label": {
+ "type": "String"
+ }
}
},
-
"repeater_cliTitle": "Repeater CLI",
"repeater_debugNextCommand": "Debug Next Command",
"repeater_commandHelp": "Command Help",
@@ -957,7 +1345,9 @@
"repeater_cliCommandError": "Error: {error}",
"@repeater_cliCommandError": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"repeater_cliQuickGetName": "Get Name",
@@ -967,6 +1357,8 @@
"repeater_cliQuickVersion": "Version",
"repeater_cliQuickAdvertise": "Advertise",
"repeater_cliQuickClock": "Clock",
+ "repeater_cliQuickClockSync": "Clock Sync",
+ "repeater_cliQuickDiscovery": "Discover Neighbors",
"repeater_cliHelpAdvert": "Sends an advertisement packet",
"repeater_cliHelpReboot": "Reboots the device. (note, you'll prob get 'Timeout' which is normal)",
"repeater_cliHelpClock": "Displays current time per device's clock.",
@@ -1037,14 +1429,18 @@
"telemetry_errorLoading": "Error loading telemetry: {error}",
"@telemetry_errorLoading": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
"telemetry_noData": "No telemetry data available.",
"telemetry_channelTitle": "Channel {channel}",
"@telemetry_channelTitle": {
"placeholders": {
- "channel": {"type": "int"}
+ "channel": {
+ "type": "int"
+ }
}
},
"telemetry_batteryLabel": "Battery",
@@ -1055,50 +1451,67 @@
"telemetry_batteryValue": "{percent}% / {volts}V",
"@telemetry_batteryValue": {
"placeholders": {
- "percent": {"type": "int"},
- "volts": {"type": "String"}
+ "percent": {
+ "type": "int"
+ },
+ "volts": {
+ "type": "String"
+ }
}
},
"telemetry_voltageValue": "{volts}V",
"@telemetry_voltageValue": {
"placeholders": {
- "volts": {"type": "String"}
+ "volts": {
+ "type": "String"
+ }
}
},
"telemetry_currentValue": "{amps}A",
"@telemetry_currentValue": {
"placeholders": {
- "amps": {"type": "String"}
+ "amps": {
+ "type": "String"
+ }
}
},
"telemetry_temperatureValue": "{celsius}°C / {fahrenheit}°F",
"@telemetry_temperatureValue": {
"placeholders": {
- "celsius": {"type": "String"},
- "fahrenheit": {"type": "String"}
+ "celsius": {
+ "type": "String"
+ },
+ "fahrenheit": {
+ "type": "String"
+ }
}
},
-
- "neighbors_receivedData": "Received Neighbours Data",
- "neighbors_requestTimedOut": "Neighbours request timed out.",
+ "neighbors_receivedData": "Received Neighbors Data",
+ "neighbors_requestTimedOut": "Neighbors request timed out.",
"neighbors_errorLoading": "Error loading neighbors: {error}",
"@neighbors_errorLoading": {
"placeholders": {
- "error": {"type": "String"}
+ "error": {
+ "type": "String"
+ }
}
},
- "neighbors_repeatersNeighbours": "Repeaters Neighbours",
- "neighbors_noData": "No neighbours data available.",
+ "neighbors_repeatersNeighbors": "Repeaters Neighbors",
+ "neighbors_noData": "No neighbors data available.",
"neighbors_unknownContact": "Unknown {pubkey}",
"@neighbors_unknownContact": {
"placeholders": {
- "pubkey": {"type": "String"}
+ "pubkey": {
+ "type": "String"
+ }
}
},
"neighbors_heardAgo": "Heard: {time} ago",
"@neighbors_heardAgo": {
"placeholders": {
- "time": {"type": "String"}
+ "time": {
+ "type": "String"
+ }
}
},
"channelPath_title": "Packet Path",
@@ -1110,28 +1523,40 @@
"channelPath_senderLabel": "Sender",
"channelPath_timeLabel": "Time",
"channelPath_repeatsLabel": "Repeats",
- "channelPath_pathLabel": "Path",
+ "channelPath_pathLabel": "Path {index}",
"channelPath_observedLabel": "Observed",
"channelPath_observedPathTitle": "Observed path {index} • {hops}",
"@channelPath_observedPathTitle": {
"placeholders": {
- "index": {"type": "int"},
- "hops": {"type": "String"}
+ "index": {
+ "type": "int"
+ },
+ "hops": {
+ "type": "String"
+ }
}
},
"channelPath_noLocationData": "No location data",
"channelPath_timeWithDate": "{day}/{month} {time}",
"@channelPath_timeWithDate": {
"placeholders": {
- "day": {"type": "int"},
- "month": {"type": "int"},
- "time": {"type": "String"}
+ "day": {
+ "type": "int"
+ },
+ "month": {
+ "type": "int"
+ },
+ "time": {
+ "type": "String"
+ }
}
},
"channelPath_timeOnly": "{time}",
"@channelPath_timeOnly": {
"placeholders": {
- "time": {"type": "String"}
+ "time": {
+ "type": "String"
+ }
}
},
"channelPath_unknownPath": "Unknown",
@@ -1140,14 +1565,20 @@
"channelPath_observedZeroOf": "0 of {total} hops",
"@channelPath_observedZeroOf": {
"placeholders": {
- "total": {"type": "int"}
+ "total": {
+ "type": "int"
+ }
}
},
"channelPath_observedSomeOf": "{observed} of {total} hops",
"@channelPath_observedSomeOf": {
"placeholders": {
- "observed": {"type": "int"},
- "total": {"type": "int"}
+ "observed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
}
},
"channelPath_mapTitle": "Path Map",
@@ -1155,13 +1586,16 @@
"channelPath_primaryPath": "Path {index} (Primary)",
"@channelPath_primaryPath": {
"placeholders": {
- "index": {"type": "int"}
+ "index": {
+ "type": "int"
+ }
}
},
- "channelPath_pathLabel": "Path {index}",
"@channelPath_pathLabel": {
"placeholders": {
- "index": {"type": "int"}
+ "index": {
+ "type": "int"
+ }
}
},
"channelPath_pathLabelTitle": "Path",
@@ -1169,13 +1603,16 @@
"channelPath_selectedPathLabel": "{label} • {prefixes}",
"@channelPath_selectedPathLabel": {
"placeholders": {
- "label": {"type": "String"},
- "prefixes": {"type": "String"}
+ "label": {
+ "type": "String"
+ },
+ "prefixes": {
+ "type": "String"
+ }
}
},
"channelPath_noHopDetailsAvailable": "No hop details available for this packet.",
"channelPath_unknownRepeater": "Unknown Repeater",
-
"community_title": "Community",
"community_create": "Create Community",
"community_createDesc": "Create a new community and share via QR code.",
@@ -1184,7 +1621,9 @@
"community_joinConfirmation": "Do you want to join the community \"{name}\"?",
"@community_joinConfirmation": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_scanQr": "Scan Community QR",
@@ -1197,20 +1636,26 @@
"community_created": "Community \"{name}\" created",
"@community_created": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_joined": "Joined community \"{name}\"",
"@community_joined": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_qrTitle": "Share Community",
"community_qrInstructions": "Scan this QR code to join \"{name}\"",
"@community_qrInstructions": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_hashtagPrivacyHint": "Community hashtag channels are only joinable by members of the community",
@@ -1219,7 +1664,9 @@
"community_alreadyMemberMessage": "You are already a member of \"{name}\".",
"@community_alreadyMemberMessage": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_addPublicChannel": "Add Community Public Channel",
@@ -1231,46 +1678,60 @@
"community_deleteConfirm": "Leave \"{name}\"?",
"@community_deleteConfirm": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_deleteChannelsWarning": "This will also delete {count} channel(s) and their messages.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Left community \"{name}\"",
"@community_deleted": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_regenerateSecret": "Regenerate Secret",
"community_regenerateSecretConfirm": "Regenerate the secret key for \"{name}\"? All members will need to scan the new QR code to continue communicating.",
"@community_regenerateSecretConfirm": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_regenerate": "Regenerate",
"community_secretRegenerated": "Secret regenerated for \"{name}\"",
"@community_secretRegenerated": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_updateSecret": "Update Secret",
"community_secretUpdated": "Secret updated for \"{name}\"",
"@community_secretUpdated": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_scanToUpdateSecret": "Scan the new QR code to update the secret for \"{name}\"",
"@community_scanToUpdateSecret": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
"community_addHashtagChannel": "Add Community Hashtag",
@@ -1283,10 +1744,11 @@
"community_forCommunity": "For {name}",
"@community_forCommunity": {
"placeholders": {
- "name": {"type": "String"}
+ "name": {
+ "type": "String"
+ }
}
},
-
"listFilter_tooltip": "Filter and sort",
"listFilter_sortBy": "Sort by",
"listFilter_latestMessages": "Latest messages",
@@ -1294,9 +1756,370 @@
"listFilter_az": "A-Z",
"listFilter_filters": "Filters",
"listFilter_all": "All",
+ "listFilter_favorites": "Favorites",
+ "listFilter_addToFavorites": "Add to favorites",
+ "listFilter_removeFromFavorites": "Remove from favorites",
"listFilter_users": "Users",
"listFilter_repeaters": "Repeaters",
"listFilter_roomServers": "Room servers",
"listFilter_unreadOnly": "Unread only",
- "listFilter_newGroup": "New group"
+ "listFilter_newGroup": "New group",
+ "pathTrace_you": "You",
+ "pathTrace_failed": "Path trace failed.",
+ "pathTrace_notAvailable": "Path trace not available.",
+ "pathTrace_refreshTooltip": "Refresh Path Trace.",
+ "pathTrace_someHopsNoLocation": "One or more of the hops is missing a location!",
+ "pathTrace_clearTooltip": "Clear path.",
+ "losSelectStartEnd": "Select start and end nodes for LOS.",
+ "losRunFailed": "Line-of-sight check failed: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Clear all points",
+ "losRunToViewElevationProfile": "Run LOS to view elevation profile",
+ "losMenuTitle": "LOS Menu",
+ "losMenuSubtitle": "Tap nodes or long-press map for custom points",
+ "losShowDisplayNodes": "Show display nodes",
+ "losCustomPoints": "Custom points",
+ "losCustomPointLabel": "Custom {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Point A",
+ "losPointB": "Point B",
+ "losAntennaA": "Antenna A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenna B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Run LOS",
+ "losNoElevationData": "No elevation data",
+ "losProfileClear": "{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: checking...",
+ "losStatusNoData": "LOS: no data",
+ "losStatusSummary": "LOS: {clear}/{total} clear, {blocked} blocked, {unknown} unknown",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Elevation data unavailable for one or more samples.",
+ "losErrorInvalidInput": "Invalid points/elevation data for LOS calculation.",
+ "losRenameCustomPoint": "Rename custom point",
+ "losPointName": "Point name",
+ "losShowPanelTooltip": "Show LOS panel",
+ "losHidePanelTooltip": "Hide LOS panel",
+ "losElevationAttribution": "Elevation data: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Radio horizon",
+ "losLegendLosBeam": "LOS beam",
+ "losLegendTerrain": "Terrain",
+ "losFrequencyLabel": "Frequency",
+ "losFrequencyInfoTooltip": "View calculation details",
+ "losFrequencyDialogTitle": "Radio horizon calculation",
+ "losFrequencyDialogDescription": "Starting from k={baselineK} at {baselineFreq} MHz, the calculation adjusts the k-factor for the current {frequencyMHz} MHz band, which defines the curved radio horizon cap.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "contacts_pathTrace": "Path Trace",
+ "contacts_ping": "Ping",
+ "contacts_repeaterPathTrace": "Path trace to repeater",
+ "contacts_repeaterPing": "Ping repeater",
+ "contacts_roomPathTrace": "Path trace to room server",
+ "contacts_roomPing": "Ping room server",
+ "contacts_chatTraceRoute": "Path trace route",
+ "contacts_pathTraceTo": "Trace route to {name}",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_clipboardEmpty": "Clipboard is empty.",
+ "contacts_invalidAdvertFormat": "Invalid contact data",
+ "contacts_contactImported": "Contact has been imported.",
+ "contacts_contactImportFailed": "Failed to import contact.",
+ "contacts_zeroHopAdvert": "Zero Hop Advert",
+ "contacts_floodAdvert": "Flood Advert",
+ "contacts_copyAdvertToClipboard": "Copy Advert to Clipboard",
+ "contacts_addContactFromClipboard": "Add Contact from Clipboard",
+ "contacts_ShareContact": "Copy contact to Clipboard",
+ "contacts_ShareContactZeroHop": "Share contact by advert",
+ "contacts_zeroHopContactAdvertSent": "Sent contact by advert.",
+ "contacts_zeroHopContactAdvertFailed": "Failed to send contact.",
+ "contacts_contactAdvertCopied": "Advert copied to Clipboard.",
+ "contacts_contactAdvertCopyFailed": "Copying advert to Clipboard failed.",
+ "notification_activityTitle": "MeshCore Activity",
+ "notification_messagesCount": "{count} {count, plural, =1{message} other{messages}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{channel message} other{channel messages}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{new node} other{new nodes}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "New {contactType} discovered",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "Received new message",
+ "settings_gpxExportRepeaters": "Export repeaters / room server to GPX",
+ "settings_gpxExportRepeatersSubtitle": "Exports repeaters / roomserver with a location to GPX file.",
+ "settings_gpxExportContacts": "Export companions to GPX",
+ "settings_gpxExportContactsSubtitle": "Exports companions with a location to GPX file.",
+ "settings_gpxExportAll": "Export all contacts to GPX",
+ "settings_gpxExportAllSubtitle": "Exports all contacts with a location to GPX file.",
+ "settings_gpxExportSuccess": "Successfully exported GPX file.",
+ "settings_gpxExportNoContacts": "No contacts to export.",
+ "settings_gpxExportNotAvailable": "Not supported on your device/OS",
+ "settings_gpxExportError": "There was an error when exporting.",
+ "settings_gpxExportRepeatersRoom": "Repeater & room server locations",
+ "settings_gpxExportChat": "Companion locations",
+ "settings_gpxExportAllContacts": "All contacts locations",
+ "settings_gpxExportShareText": "Map data exported from meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open GPX map data export",
+ "snrIndicator_nearByRepeaters": "Nearby Repeaters",
+ "snrIndicator_lastSeen": "Last seen",
+ "contactsSettings_title": "Contacts settings",
+ "contactsSettings_autoAddTitle": "Automatic Discovery",
+ "contactsSettings_otherTitle": "Other contact related settings",
+ "contactsSettings_autoAddUsersTitle": "Auto-add users",
+ "contactsSettings_autoAddUsersSubtitle": "Allow the companion to automatically add discovered users.",
+ "contactsSettings_autoAddRepeatersTitle": "Auto-add repeaters",
+ "contactsSettings_autoAddRepeatersSubtitle": "Allow the companion to automatically add discovered repeaters.",
+ "contactsSettings_autoAddRoomServersTitle": "Auto-add room servers",
+ "contactsSettings_autoAddRoomServersSubtitle": "Allow the companion to automatically add discovered room servers.",
+ "contactsSettings_autoAddSensorsTitle": "Auto-add sensors",
+ "contactsSettings_autoAddSensorsSubtitle": "Allow the companion to automatically add discovered sensors.",
+ "contactsSettings_overwriteOldestTitle": "Overwrite Oldest",
+ "contactsSettings_overwriteOldestSubtitle": "When the contact list is full, the oldest non-favorited contact will be replaced.",
+ "discoveredContacts_Title": "Discovered Contacts",
+ "discoveredContacts_noMatching": "No matching contacts",
+ "discoveredContacts_searchHint": "Search discovered contacts",
+ "discoveredContacts_contactAdded": "Contact added",
+ "discoveredContacts_addContact": "Add Contact",
+ "discoveredContacts_copyContact": "Copy Contact to clipboard",
+ "discoveredContacts_deleteContact": "Delete Discovered Contact",
+ "discoveredContacts_deleteContactAll": "Delete All Discovered Contacts",
+ "discoveredContacts_deleteContactAllContent": "Are you sure you want to delete all discovered contacts?",
+ "chat_sendCooldown": "Please wait a moment before sending again.",
+ "appSettings_jumpToOldestUnread": "Jump to oldest unread",
+ "appSettings_jumpToOldestUnreadSubtitle": "When opening a chat with unread messages, scroll to the first unread instead of the latest.",
+ "appSettings_languageHu": "Hungarian",
+ "appSettings_languageJa": "Japanese",
+ "appSettings_languageKo": "Korean",
+ "radioStats_tooltip": "Radio & mesh stats",
+ "radioStats_screenTitle": "Radio stats",
+ "radioStats_notConnected": "Connect to a device to view radio statistics.",
+ "radioStats_firmwareTooOld": "Radio statistics require companion firmware v8 or newer.",
+ "radioStats_waiting": "Waiting for data…",
+ "radioStats_noiseFloor": "Noise floor: {noiseDbm} dBm",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastRssi": "Last RSSI: {rssiDbm} dBm",
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastSnr": "Last SNR: {snr} dB",
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "radioStats_txAir": "TX airtime (total): {seconds} s",
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_rxAir": "RX airtime (total): {seconds} s",
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_chartCaption": "Noise floor (dBm) over recent samples.",
+ "radioStats_stripNoise": "Noise floor: {noiseDbm} dBm",
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_stripWaiting": "Fetching radio stats…",
+ "radioStats_settingsTile": "Radio stats",
+ "radioStats_settingsSubtitle": "Noise floor, RSSI, SNR, and airtime",
+
+ "translation_title": "Translation",
+ "translation_enableTitle": "Enable translation",
+ "translation_enableSubtitle": "Translate incoming messages and allow pre-send translation.",
+ "translation_composerTitle": "Translate before sending",
+ "translation_composerSubtitle": "Controls the default state of the composer translation icon.",
+ "translation_targetLanguage": "Target language",
+ "translation_useAppLanguage": "Use app language",
+ "translation_downloadedModelLabel": "Downloaded model",
+ "translation_presetModelLabel": "Preset Hugging Face model",
+ "translation_manualUrlLabel": "Manual model URL",
+ "translation_downloadModel": "Download model",
+ "translation_downloading": "Downloading...",
+ "translation_working": "Working...",
+ "translation_stop": "Stop",
+ "translation_mergingChunks": "Merging downloaded chunks into final file...",
+ "translation_downloadedModels": "Downloaded models",
+ "translation_deleteModel": "Delete model",
+ "translation_modelDownloaded": "Translation model downloaded.",
+ "translation_downloadStopped": "Download stopped.",
+ "translation_downloadFailed": "Download failed: {error}",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enterUrlFirst": "Enter a model URL first.",
+ "scanner_linuxPairingShowPin": "Show PIN",
+ "scanner_linuxPairingHidePin": "Hide PIN",
+ "scanner_linuxPairingPinTitle": "Bluetooth Pairing PIN",
+ "scanner_linuxPairingPinPrompt": "Enter PIN for {deviceName} (leave blank if none).",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_messageTranslation": "Message translation",
+ "translation_translateBeforeSending": "Translate before sending",
+ "translation_composerEnabledHint": "Messages will be translated before send.",
+ "translation_composerDisabledHint": "Send messages in the original typed language.",
+ "translation_translateTo": "Translate to {language}",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_translationOptions": "Translation options",
+ "translation_systemLanguage": "System language"
}
diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb
index b406e94..ac9527b 100644
--- a/lib/l10n/app_es.arb
+++ b/lib/l10n/app_es.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "No se pudo eliminar el canal \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "es",
"appTitle": "MeshCore Open",
"nav_contacts": "Contactos",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Número de contactos",
"settings_infoChannelCount": "Número de canales",
"settings_presets": "Preajustes",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frecuencia (MHz)",
"settings_frequencyHelper": "300,0 - 2500,0",
"settings_frequencyInvalid": "Frecuencia inválida (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX Potencia (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Potencia de TX inválida (0-22 dBm)",
- "settings_longRange": "Largo Alcance",
- "settings_fastSpeed": "Velocidad Rápida",
"settings_error": "Error: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Nuevo Grupo",
"contacts_groupName": "Nombre del grupo",
"contacts_groupNameRequired": "El nombre del grupo es obligatorio",
+ "contacts_groupNameReserved": "Este nombre de grupo está reservado",
"contacts_groupAlreadyExists": "El grupo \"{name}\" ya existe",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -294,7 +298,7 @@
"contacts_noContactsMatchFilter": "No hay contactos que coincidan con tu filtro",
"contacts_noMembers": "No miembros",
"contacts_lastSeenNow": "Última vez que se vio ahora",
- "contacts_lastSeenMinsAgo": "Última vez visto hace {minutes} minutos.",
+ "contacts_lastSeenMinsAgo": "~ {minutes} min.",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +306,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "Última vez que se vio hace 1 hora",
- "contacts_lastSeenHoursAgo": "Última vez visto hace {hours} horas.",
+ "contacts_lastSeenHourAgo": "~ 1 hora",
+ "contacts_lastSeenHoursAgo": "~ {hours} horas",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +315,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "Última vez que se vio hace 1 día",
- "contacts_lastSeenDaysAgo": "Última vez visto hace {days} días.",
+ "contacts_lastSeenDayAgo": "~ 1 día",
+ "contacts_lastSeenDaysAgo": "~ {days} días",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Canal público",
"channels_privateChannel": "Canal privado",
"channels_editChannel": "Editar canal",
+ "channels_muteChannel": "Silenciar canal",
+ "channels_unmuteChannel": "Activar canal",
"channels_deleteChannel": "Eliminar canal",
"channels_deleteChannelConfirm": "Eliminar \"{name}\"? Esto no se puede deshacer.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "¿Abrir enlace?",
+ "chat_openLinkConfirmation": "¿Quiere abrir este enlace en su navegador?",
+ "chat_open": "Abrir",
+ "chat_couldNotOpenLink": "No se pudo abrir el enlace: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Formato de enlace no válido",
"map_title": "Mapa de Nodos",
"map_noNodesWithLocation": "No hay nodos con datos de ubicación",
"map_nodesNeedGps": "Los nodos necesitan compartir sus coordenadas GPS\npara aparecer en el mapa",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighbours": "Vecinos",
- "repeater_neighboursSubtitle": "Ver vecinos de salto cero.",
+ "repeater_neighbors": "Vecinos",
+ "repeater_neighborsSubtitle": "Ver vecinos de salto cero.",
"neighbors_receivedData": "Recibidas Datos de Vecinos",
"neighbors_requestTimedOut": "Los vecinos solicitan que se desconecte.",
"neighbors_errorLoading": "Error al cargar vecinos: {error}",
- "neighbors_repeatersNeighbours": "Repetidores Vecinos",
+ "neighbors_repeatersNeighbors": "Repetidores Vecinos",
"neighbors_noData": "No hay datos de vecinos disponibles.",
"channels_joinPrivateChannel": "Únete a un Canal Privado",
"channels_createPrivateChannel": "Crear un Canal Privado",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Esto también eliminará {count} canal(es) y sus mensajes.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Has salido de la comunidad \"{name}\"",
@@ -1484,5 +1504,602 @@
"community_regularHashtagDesc": "Hashtag público (cualquiera puede unirse)",
"community_communityHashtag": "Hashtag de la Comunidad",
"community_communityHashtagDesc": "Exclusivo para miembros de la comunidad",
- "community_forCommunity": "Para {name}"
+ "community_forCommunity": "Para {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "Regenerar Contraseña Secreta",
+ "community_regenerateSecretConfirm": "Regenerar la clave secreta para \"{name}\"? Todos los miembros deberán escanear el nuevo código QR para seguir comunicándose.",
+ "community_secretRegenerated": "Código secreto regenerado para \"{name}\"",
+ "community_regenerate": "Regenerar",
+ "community_secretUpdated": "Confidencialidad actualizada para \"{name}\"",
+ "community_scanToUpdateSecret": "Escanear el nuevo código QR para actualizar el secreto de \"{name}\"",
+ "community_updateSecret": "Actualizar Contraseña",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Tú",
+ "pathTrace_failed": "El trazado de ruta falló.",
+ "pathTrace_refreshTooltip": "Actualizar Path Trace",
+ "contacts_pathTrace": "Rastreo de caminos",
+ "contacts_repeaterPathTrace": "Rastrear ruta al repetidor",
+ "contacts_repeaterPing": "Pingar repetidor",
+ "contacts_ping": "Ping",
+ "pathTrace_notAvailable": "El trazado de ruta no está disponible.",
+ "contacts_roomPing": "Pingar servidor de sala",
+ "contacts_roomPathTrace": "Rastreo de ruta al servidor de la habitación",
+ "contacts_pathTraceTo": "Rastrear ruta a {name}",
+ "contacts_chatTraceRoute": "Ruta de trazado",
+ "appSettings_languageUk": "Ucraniano",
+ "contacts_clipboardEmpty": "El portapapeles está vacío.",
+ "appSettings_languageRu": "Ruso",
+ "appSettings_enableMessageTracing": "Habilitar seguimiento de mensajes",
+ "appSettings_enableMessageTracingSubtitle": "Mostrar metadatos detallados de enrutamiento y tiempo para los mensajes",
+ "contacts_invalidAdvertFormat": "Datos de contacto no válidos",
+ "contacts_floodAdvert": "Anuncio de inundación",
+ "contacts_contactImported": "El contacto ha sido importado.",
+ "contacts_contactImportFailed": "Contacto no se importó correctamente.",
+ "contacts_zeroHopAdvert": "Anuncio de Zero Hop",
+ "contacts_ShareContactZeroHop": "Compartir contacto por anuncio",
+ "contacts_ShareContact": "Copiar contacto al Portapapeles",
+ "contacts_copyAdvertToClipboard": "Copiar anuncio al portapapeles",
+ "contacts_addContactFromClipboard": "Agregar contacto desde el portapapeles",
+ "contacts_zeroHopContactAdvertFailed": "No se pudo enviar el contacto.",
+ "contacts_zeroHopContactAdvertSent": "Envió contacto por anuncio.",
+ "contacts_contactAdvertCopied": "Anuncio copiado al Portapapeles.",
+ "contacts_contactAdvertCopyFailed": "Copiar anuncio al Portapapeles ha fallado.",
+ "notification_activityTitle": "Actividad de MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{mensaje} other{mensajes}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{mensaje de canal} other{mensajes de canal}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{nuevo nodo} other{nuevos nodos}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "Nuevo {contactType} descubierto",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "Nuevo mensaje recibido",
+ "settings_gpxExportContactsSubtitle": "Exporta compañeros con una ubicación a archivo GPX.",
+ "settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala a GPX",
+ "settings_gpxExportSuccess": "Archivo GPX exportado con éxito.",
+ "settings_gpxExportNoContacts": "No hay contactos para exportar.",
+ "settings_gpxExportNotAvailable": "No compatible con tu dispositivo/SO",
+ "settings_gpxExportError": "Hubo un error al exportar.",
+ "settings_gpxExportRepeatersSubtitle": "Exporta repetidores o roomserver con una ubicación a un archivo GPX.",
+ "settings_gpxExportAllSubtitle": "Exporta todos los contactos con una ubicación a un archivo GPX.",
+ "settings_gpxExportAll": "Exportar todos los contactos a GPX",
+ "settings_gpxExportContacts": "Exportar compañeros a GPX",
+ "settings_gpxExportChat": "Ubicaciones de compañero",
+ "settings_gpxExportRepeatersRoom": "Ubicaciones del servidor de repetidor y sala",
+ "settings_gpxExportAllContacts": "Todas las ubicaciones de contactos",
+ "settings_gpxExportShareText": "Datos del mapa exportados desde meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open exportación de datos de mapa GPX",
+ "pathTrace_someHopsNoLocation": "Uno o más de los lúpulos carecen de una ubicación",
+ "pathTrace_clearTooltip": "Borrar ruta",
+ "map_runTrace": "Ejecutar Rastreo de Ruta",
+ "map_tapToAdd": "Pulse en los nodos para agregarlos al camino.",
+ "map_removeLast": "Eliminar último",
+ "map_pathTraceCancelled": "Rastreo de ruta cancelado.",
+ "scanner_bluetoothOffMessage": "Por favor, active el Bluetooth para escanear dispositivos.",
+ "scanner_chromeRequired": "Navegador Chrome requerido",
+ "scanner_chromeRequiredMessage": "Esta aplicación web requiere Google Chrome o un navegador basado en Chromium para el soporte de Bluetooth.",
+ "scanner_bluetoothOff": "Bluetooth está desactivado.",
+ "scanner_enableBluetooth": "Habilitar Bluetooth",
+ "snrIndicator_nearByRepeaters": "Repetidores cercanos",
+ "snrIndicator_lastSeen": "Visto por última vez",
+ "chat_ShowAllPaths": "Mostrar todos los caminos",
+ "settings_clientRepeatFreqWarning": "Para la comunicación fuera de la red, se requiere una frecuencia de 433, 869 o 918 MHz.",
+ "settings_clientRepeat": "Repetir sin conexión",
+ "settings_clientRepeatSubtitle": "Permita que este dispositivo repita los paquetes de red para otros usuarios.",
+ "settings_aboutOpenMeteoAttribution": "Datos de elevación LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Unidades",
+ "appSettings_unitsMetric": "Métrico (m/km)",
+ "appSettings_unitsImperial": "Imperial (pies/millas)",
+ "map_lineOfSight": "Línea de visión",
+ "map_losScreenTitle": "Línea de visión",
+ "losSelectStartEnd": "Seleccione los nodos de inicio y fin para LOS.",
+ "losRunFailed": "Error en la comprobación de la línea de visión: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Borrar todos los puntos",
+ "losRunToViewElevationProfile": "Ejecute LOS para ver el perfil de elevación",
+ "losMenuTitle": "Menú LOS",
+ "losMenuSubtitle": "Toque nodos o mantenga presionado el mapa para puntos personalizados",
+ "losShowDisplayNodes": "Mostrar nodos de visualización",
+ "losCustomPoints": "Puntos personalizados",
+ "losCustomPointLabel": "Personalizado {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punto A",
+ "losPointB": "Punto B",
+ "losAntennaA": "Antena A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antena B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Ejecutar LOS",
+ "losNoElevationData": "Sin datos de elevación",
+ "losProfileClear": "{distance} {distanceUnit}, despejar LOS, autorización mínima {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, bloqueado por {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: comprobando...",
+ "losStatusNoData": "LOS: sin datos",
+ "losStatusSummary": "LOS: {clear}/{total} claro, {blocked} bloqueado, {unknown} desconocido",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Datos de elevación no disponibles para una o más muestras.",
+ "losErrorInvalidInput": "Datos de puntos/elevación no válidos para el cálculo de LOS.",
+ "losRenameCustomPoint": "Cambiar el nombre del punto personalizado",
+ "losPointName": "Nombre del punto",
+ "losShowPanelTooltip": "Mostrar panel LOS",
+ "losHidePanelTooltip": "Ocultar panel LOS",
+ "losElevationAttribution": "Datos de elevación: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Horizonte radioeléctrico",
+ "losLegendLosBeam": "Línea de visión",
+ "losLegendTerrain": "Terreno",
+ "losFrequencyLabel": "Frecuencia",
+ "losFrequencyInfoTooltip": "Ver detalles del cálculo",
+ "losFrequencyDialogTitle": "Cálculo del horizonte radioeléctrico",
+ "losFrequencyDialogDescription": "A partir de k={baselineK} en {baselineFreq} MHz, el cálculo ajusta el factor k para la banda actual de {frequencyMHz} MHz, que define el límite curvo del horizonte de radio.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_favorites": "Favoritos",
+ "listFilter_removeFromFavorites": "Eliminar de las favoritas",
+ "listFilter_addToFavorites": "Añadir a favoritos",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchContactsNoNumber": "Buscar contactos...",
+ "contacts_unread": "No leído",
+ "contacts_searchFavorites": "Buscar {number}{str} Favoritos...",
+ "contacts_searchUsers": "Buscar {number}{str} Usuarios...",
+ "contacts_searchRepeaters": "Buscar {number}{str} Repetidores...",
+ "contacts_searchRoomServers": "Buscar {number}{str} servidores de sala...",
+ "contactsSettings_autoAddTitle": "Detección automática",
+ "settings_contactSettings": "Configuración de contacto",
+ "contactsSettings_autoAddUsersTitle": "Agregar usuarios automáticamente",
+ "contactsSettings_otherTitle": "Otras configuraciones relacionadas con el contacto",
+ "contactsSettings_autoAddUsersSubtitle": "Permitir que el compañero agregue automáticamente a los usuarios descubiertos.",
+ "contactsSettings_autoAddRepeatersSubtitle": "Permitir que el compañero agregue automáticamente los repetidores descubiertos.",
+ "contactsSettings_autoAddRoomServersSubtitle": "Permitir que el compañero agregue automáticamente los servidores de salas descubiertos.",
+ "contactsSettings_autoAddSensorsTitle": "Agregar sensores automáticamente",
+ "contactsSettings_title": "Configuración de contactos",
+ "settings_contactSettingsSubtitle": "Configuración de cómo se agregan los contactos.",
+ "contactsSettings_autoAddSensorsSubtitle": "Permitir que el compañero agregue automáticamente los sensores descubiertos.",
+ "contactsSettings_autoAddRepeatersTitle": "Agregar repetidores automáticamente",
+ "contactsSettings_overwriteOldestTitle": "Sobreescribir el más antiguo",
+ "contactsSettings_autoAddRoomServersTitle": "Agregar automáticamente servidores de sala",
+ "discoveredContacts_noMatching": "No se encontraron contactos coincidentes",
+ "discoveredContacts_contactAdded": "Contacto agregado",
+ "discoveredContacts_copyContact": "Copiar contacto al portapapeles",
+ "discoveredContacts_deleteContact": "Eliminar contacto",
+ "discoveredContacts_Title": "Contactos descubiertos",
+ "discoveredContacts_searchHint": "Buscar contactos descubiertos",
+ "discoveredContacts_addContact": "Agregar contacto",
+ "contactsSettings_overwriteOldestSubtitle": "Cuando la lista de contactos esté llena, se reemplazará el contacto no favorito más antiguo.",
+ "common_deleteAll": "Eliminar todo",
+ "discoveredContacts_deleteContactAll": "Eliminar Todos los Contactos Descubiertos",
+ "discoveredContacts_deleteContactAllContent": "¿Está seguro de que desea eliminar todos los contactos descubiertos!",
+ "map_guessedLocation": "Ubicación estimada",
+ "map_showGuessedLocations": "Mostrar las ubicaciones estimadas de los nodos.",
+ "usbScreenTitle": "Conecte mediante USB",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenSubtitle": "Seleccione el dispositivo de serie detectado y conéctelo directamente a su nodo MeshCore.",
+ "usbScreenStatus": "Seleccione un dispositivo USB",
+ "usbScreenNote": "La comunicación serial a través de USB está activa en dispositivos Android compatibles y en plataformas de escritorio.",
+ "usbScreenEmptyState": "No se encontraron dispositivos USB. Conecte uno y vuelva a intentar.",
+ "usbErrorPermissionDenied": "Se denegó el permiso de acceso a través de USB.",
+ "usbErrorDeviceMissing": "El dispositivo USB seleccionado ya no está disponible.",
+ "usbErrorInvalidPort": "Seleccione un dispositivo USB válido.",
+ "usbErrorBusy": "Ya se ha iniciado una solicitud de conexión USB adicional.",
+ "usbErrorNotConnected": "No hay ningún dispositivo USB conectado.",
+ "usbErrorOpenFailed": "No se pudo abrir el dispositivo USB seleccionado.",
+ "usbErrorConnectFailed": "No se pudo conectar con el dispositivo USB seleccionado.",
+ "usbErrorUnsupported": "La comunicación serial a través de USB no está soportada en esta plataforma.",
+ "usbErrorAlreadyActive": "La conexión USB ya está activa.",
+ "usbErrorNoDeviceSelected": "No se ha seleccionado ningún dispositivo USB.",
+ "usbErrorPortClosed": "La conexión USB no está activa.",
+ "usbFallbackDeviceName": "Dispositivo de serie web",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_connecting": "Conectándose al dispositivo USB...",
+ "usbStatus_searching": "Buscando dispositivos USB...",
+ "usbStatus_notConnected": "Seleccione un dispositivo USB",
+ "usbConnectionFailed": "Error al conectar mediante USB: {error}",
+ "usbErrorConnectTimedOut": "La conexión ha caducado. Asegúrese de que el dispositivo tenga el firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpScreenTitle": "Establecer conexión a través de TCP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpHostLabel": "Dirección IP",
+ "tcpPortLabel": "Puerto",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Ingrese la dirección final y conecte.",
+ "tcpStatus_connectingTo": "Conectándose a {endpoint}...",
+ "tcpErrorHostRequired": "Se requiere la dirección IP.",
+ "tcpErrorPortInvalid": "El puerto debe estar entre 1 y 65535.",
+ "tcpErrorUnsupported": "El protocolo de transporte TCP no está soportado en esta plataforma.",
+ "tcpErrorTimedOut": "La conexión TCP ha caducado.",
+ "tcpConnectionFailed": "Error en la conexión TCP: {error}",
+ "map_showDiscoveryContacts": "Mostrar Contactos de Descubrimiento",
+ "map_setAsMyLocation": "Establecer mi ubicación",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacySubtitle": "Controlar qué información se comparte.",
+ "settings_allowByContact": "Permitir por banderas de contacto",
+ "settings_denyAll": "Denegar todo",
+ "settings_telemetryBaseMode": "Modo base de telemetría",
+ "settings_telemetryEnvironmentMode": "Modo de entorno de telemetría",
+ "settings_advertLocationSubtitle": "Incluir ubicación en anuncio",
+ "contact_info": "Información de contacto",
+ "settings_privacySettingsDescription": "Elige qué información comparte tu dispositivo con otros.",
+ "settings_allowAll": "Permitir todo",
+ "settings_privacy": "Configuración de privacidad",
+ "contact_settings": "Configuración de contacto",
+ "settings_telemetryLocationMode": "Modo de ubicación de telemetría",
+ "contact_teleBase": "Base de Telemetría",
+ "contact_teleLoc": "Ubicación de telemetría",
+ "settings_advertLocation": "Ubicación de anuncio",
+ "contact_teleLocSubtitle": "Permitir el intercambio de datos de ubicación",
+ "contact_clearChat": "Borrar chat",
+ "contact_telemetry": "Telemetría",
+ "contact_lastSeen": "Visto por última vez",
+ "contact_teleBaseSubtitle": "Permitir el intercambio de nivel de batería y telemetría básica",
+ "contact_teleEnv": "Entorno de Telemetría",
+ "contact_teleEnvSubtitle": "Permitir el intercambio de datos de sensores de entorno",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeight": "Peso inicial de la ruta",
+ "appSettings_maxRouteWeight": "Peso máximo permitido para la ruta",
+ "appSettings_initialRouteWeightSubtitle": "Peso inicial para rutas recién descubiertas",
+ "appSettings_maxRouteWeightSubtitle": "Peso máximo que una ruta puede acumular gracias a entregas exitosas.",
+ "appSettings_routeWeightSuccessIncrement": "Incremento de peso para el éxito",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Peso añadido a una ruta después de una entrega exitosa.",
+ "appSettings_routeWeightFailureDecrement": "Reducción del peso asociado al fallo",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Peso retirado de un camino después de un intento de entrega fallido.",
+ "appSettings_maxMessageRetries": "Número máximo de reintentos de envío de mensajes",
+ "appSettings_maxMessageRetriesSubtitle": "Número de intentos de reintento antes de marcar un mensaje como fallido.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Modo de telemetría actualizado",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "map_showOverlaps": "Superposiciones de tecla repetidora",
+ "map_runTraceWithReturnPath": "Volver atrás por el mismo camino.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_jumpToOldestUnread": "Salta a los mensajes más antiguos sin leer",
+ "chat_sendCooldown": "Por favor, espere un momento antes de reenviar.",
+ "appSettings_languageHu": "Húngaro",
+ "appSettings_jumpToOldestUnreadSubtitle": "Cuando abras una conversación con mensajes sin leer, desplázate hacia el primer mensaje sin leer en lugar del más reciente.",
+ "appSettings_languageJa": "Japonés",
+ "appSettings_languageKo": "Coreano",
+ "radioStats_tooltip": "Estadísticas de radio y malla",
+ "radioStats_screenTitle": "Estadísticas de radio",
+ "radioStats_notConnected": "Conéctese a un dispositivo para visualizar estadísticas de radio.",
+ "radioStats_firmwareTooOld": "Las estadísticas de radio requieren un firmware compatible v8 o posterior.",
+ "radioStats_waiting": "Esperando datos…",
+ "radioStats_noiseFloor": "Nivel de ruido: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Último RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Último SNR: {snr} dB",
+ "radioStats_txAir": "Tiempo de emisión en Texas (total): {seconds} s",
+ "radioStats_rxAir": "Tiempo de transmisión de RX (total): {seconds} s",
+ "radioStats_chartCaption": "Nivel de ruido (dBm) en muestras recientes.",
+ "radioStats_stripNoise": "Nivel de ruido: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Obteniendo estadísticas de la radio…",
+ "radioStats_settingsTile": "Estadísticas de radio",
+ "radioStats_settingsSubtitle": "Nivel de ruido, RSSI, SNR y tiempo de transmisión",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_title": "Traducción",
+ "translation_enableSubtitle": "Traducir los mensajes entrantes y permitir la traducción previa al envío.",
+ "translation_enableTitle": "Habilitar la traducción",
+ "translation_composerTitle": "Traducir antes de enviar",
+ "translation_composerSubtitle": "Controla el estado predeterminado del icono de traducción del compositor.",
+ "translation_targetLanguage": "Idioma de destino",
+ "translation_useAppLanguage": "Utilizar el idioma de la aplicación",
+ "translation_downloadedModelLabel": "Modelo descargado",
+ "translation_presetModelLabel": "Modelo predefinido de Hugging Face",
+ "translation_manualUrlLabel": "URL del modelo manual",
+ "translation_downloadModel": "Descargar el modelo",
+ "translation_downloading": "Descargando...",
+ "translation_working": "Trabajando...",
+ "translation_stop": "¡Detente!",
+ "translation_mergingChunks": "Combinando los fragmentos descargados en el archivo final...",
+ "translation_downloadedModels": "Modelos descargados",
+ "translation_deleteModel": "Eliminar modelo",
+ "translation_modelDownloaded": "Modelo de traducción descargado.",
+ "translation_downloadStopped": "La descarga se ha detenido.",
+ "translation_downloadFailed": "No se pudo descargar: {error}",
+ "translation_enterUrlFirst": "Primero, introduzca la URL del modelo.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_linuxPairingPinPrompt": "Introduzca el código PIN para {deviceName} (deje en blanco si no hay ninguno).",
+ "scanner_linuxPairingShowPin": "Mostrar código PIN",
+ "scanner_linuxPairingPinTitle": "PIN para emparejar dispositivos Bluetooth",
+ "scanner_linuxPairingHidePin": "Ocultar PIN",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "Envía mensajes utilizando el lenguaje escrito original.",
+ "translation_composerEnabledHint": "Los mensajes serán traducidos antes de ser enviados.",
+ "translation_messageTranslation": "Traducción del mensaje",
+ "translation_translateBeforeSending": "Traducir antes de enviar",
+ "translation_translateTo": "Traducir a {language}",
+ "translation_translationOptions": "Opciones de traducción",
+ "translation_systemLanguage": "Idioma del sistema",
+ "repeater_cliQuickDiscovery": "Descubrir Vecinos",
+ "repeater_cliQuickClockSync": "Sincronización del reloj",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Enviar automáticamente la función de \"sincronización de reloj\" después de un inicio de sesión exitoso.",
+ "repeater_clockSyncAfterLogin": "Sincronización del reloj después de iniciar sesión",
+ "repeater_guest": "Información sobre repetidores",
+ "chat_sendMessage": "Enviar mensaje",
+ "repeater_guestTools": "Herramientas para invitados",
+ "room_guest": "Información del servidor"
}
diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb
index 785ee37..a942aa2 100644
--- a/lib/l10n/app_fr.arb
+++ b/lib/l10n/app_fr.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Échec de la suppression de la chaîne \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "fr",
"appTitle": "MeshCore Open",
"nav_contacts": "Contacts",
@@ -104,7 +112,7 @@
"settings_timeSynchronized": "Synchronisation temporelle",
"settings_refreshContacts": "Rafraîchir les Contacts",
"settings_refreshContactsSubtitle": "Recharger la liste des contacts depuis l'appareil",
- "settings_rebootDevice": "Réinitialiser l'appareil",
+ "settings_rebootDevice": "Redémarrer l'appareil",
"settings_rebootDeviceSubtitle": "Redémarrer l'appareil MeshCore",
"settings_rebootDeviceConfirm": "Êtes-vous sûr de vouloir redémarrer l'appareil ? Vous serez déconnecté.",
"settings_debug": "Déboguer",
@@ -131,20 +139,15 @@
"settings_infoContactsCount": "Nombre de contacts",
"settings_infoChannelCount": "Nombre de canaux",
"settings_presets": "Préréglages",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Fréquence (MHz)",
"settings_frequencyHelper": "300,0 - 2 500,0",
"settings_frequencyInvalid": "Fréquence invalide (300-2500 MHz)",
"settings_bandwidth": "Bande passante",
- "settings_spreadingFactor": "Facteur de répartition",
- "settings_codingRate": "Taux de codage",
+ "settings_spreadingFactor": "Facteur de répartition (SF)",
+ "settings_codingRate": "Taux de codage (CR)",
"settings_txPower": "TX Puissance (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Puissance TX invalide (0-22 dBm)",
- "settings_longRange": "Portée Longue",
- "settings_fastSpeed": "Vitesse Rapide",
"settings_error": "Erreur : {message}",
"@settings_error": {
"placeholders": {
@@ -210,8 +213,8 @@
"appSettings_batteryLifepo4": "LiFePO4 (2,6-3,65V)",
"appSettings_batteryLipo": "LiPo (3,0-4,2V)",
"appSettings_mapDisplay": "Affichage de la carte",
- "appSettings_showRepeaters": "Afficher les répétiteurs",
- "appSettings_showRepeatersSubtitle": "Afficher les nœuds répétiteurs sur la carte",
+ "appSettings_showRepeaters": "Afficher les répéteurs",
+ "appSettings_showRepeatersSubtitle": "Afficher les nœuds répéteurs sur la carte",
"appSettings_showChatNodes": "Afficher les nœuds de discussion",
"appSettings_showChatNodesSubtitle": "Afficher les nœuds de chat sur la carte",
"appSettings_showOtherNodes": "Afficher d'autres nœuds",
@@ -266,8 +269,8 @@
}
}
},
- "contacts_manageRepeater": "Gérer le répétiteur",
- "contacts_roomLogin": "Connexion Salle",
+ "contacts_manageRepeater": "Gérer le répéteur",
+ "contacts_roomLogin": "Connexion Room Server",
"contacts_openChat": "Ouverture du Chat",
"contacts_editGroup": "Modifier le groupe",
"contacts_deleteGroup": "Supprimer le groupe",
@@ -279,9 +282,10 @@
}
}
},
- "contacts_newGroup": "Nouvelle Groupe",
+ "contacts_newGroup": "Nouveau Groupe",
"contacts_groupName": "Nom du groupe",
"contacts_groupNameRequired": "Le nom du groupe est obligatoire.",
+ "contacts_groupNameReserved": "Ce nom de groupe est réservé",
"contacts_groupAlreadyExists": "Le groupe \"{name}\" existe déjà.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -293,8 +297,8 @@
"contacts_filterContacts": "Filtrer les contacts...",
"contacts_noContactsMatchFilter": "Aucun contact ne correspond à votre filtre.",
"contacts_noMembers": "Aucun membre",
- "contacts_lastSeenNow": "Dernière fois vu maintenant",
- "contacts_lastSeenMinsAgo": "Dernière fois vu il y a {minutes} minutes.",
+ "contacts_lastSeenNow": "Vu maintenant",
+ "contacts_lastSeenMinsAgo": "~ {minutes} min.",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +306,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "Dernière fois vu il y a 1 heure.",
- "contacts_lastSeenHoursAgo": "Dernière fois vu il y a {hours} heures.",
+ "contacts_lastSeenHourAgo": "~ 1 heure",
+ "contacts_lastSeenHoursAgo": "~ {hours} heures",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +315,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "Dernière fois vu il y a 1 jour",
- "contacts_lastSeenDaysAgo": "Dernière activité il y a {days} jours",
+ "contacts_lastSeenDayAgo": "~ 1 jour",
+ "contacts_lastSeenDaysAgo": "~ {days} jours",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Canal public",
"channels_privateChannel": "Canal privé",
"channels_editChannel": "Modifier le canal",
+ "channels_muteChannel": "Désactiver les notifications du canal",
+ "channels_unmuteChannel": "Réactiver les notifications du canal",
"channels_deleteChannel": "Supprimer le canal",
"channels_deleteChannelConfirm": "Supprimer {name}? Cela ne peut pas être annulé.",
"@channels_deleteChannelConfirm": {
@@ -394,7 +400,7 @@
"channels_sortBy": "Trier par",
"channels_sortManual": "Manuel",
"channels_sortAZ": "A à Z",
- "channels_sortLatestMessages": "Dernières messages",
+ "channels_sortLatestMessages": "Derniers messages",
"channels_sortUnread": "Non lu",
"chat_noMessages": "Aucun message pour le moment.",
"chat_sendMessageToStart": "Envoyer un message pour commencer",
@@ -436,7 +442,7 @@
"chat_messageCopied": "Message copié",
"chat_messageDeleted": "Message supprimé",
"chat_retryingMessage": "Tentative de récupération.",
- "chat_retryCount": "Réessayer {current}/{max}",
+ "chat_retryCount": "Essai {current}/{max}",
"@chat_retryCount": {
"placeholders": {
"current": {
@@ -542,9 +548,9 @@
"chat_forceFloodMode": "Mode tout le réseau forcé",
"chat_recentAckPaths": "Chemins ACK récents (touchez pour utiliser) :",
"chat_pathHistoryFull": "L'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.",
- "chat_hopSingular": "Sautez",
- "chat_hopPlural": "sautez",
- "chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}",
+ "chat_hopSingular": "saut",
+ "chat_hopPlural": "sauts",
+ "chat_hopsCount": "{count} {count, plural, =1{saut} other{sauts}}",
"@chat_hopsCount": {
"placeholders": {
"count": {
@@ -561,7 +567,7 @@
"chat_clearPath": "Effacer le chemin",
"chat_clearPathSubtitle": "Forcer la redécouverte lors de la prochaine envoi",
"chat_pathCleared": "Le chemin est dégagé. Le prochain message redécouvrira le tracé.",
- "chat_floodModeSubtitle": "Utiliser le commutateur de routage dans la barre d'application",
+ "chat_floodModeSubtitle": "Désactive l'apprentissage du chemin (à éviter). Utiliser le commutateur de routage dans la barre d'application pour rebasculer en mode auto par la suite.",
"chat_floodModeEnabled": "Le mode envoi à tout le réseau est activé. Changer via l'icône de routage dans la barre d'outils.",
"chat_fullPath": "Chemin complet",
"chat_pathDetailsNotAvailable": "Les détails du chemin ne sont pas encore disponibles. Essayez d'envoyer un message pour rafraîchir.",
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Ouvrir le lien ?",
+ "chat_openLinkConfirmation": "Voulez-vous ouvrir ce lien dans votre navigateur ?",
+ "chat_open": "Ouvrir",
+ "chat_couldNotOpenLink": "Impossible d'ouvrir le lien : {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Format de lien invalide",
"map_title": "Carte des nœuds",
"map_noNodesWithLocation": "Aucun nœud avec des données de localisation",
"map_nodesNeedGps": "Les nœuds doivent partager leurs coordonnées GPS\npour apparaître sur la carte.",
@@ -624,8 +642,8 @@
}
},
"map_chat": "Chat",
- "map_repeater": "Répétiteur",
- "map_room": "Salle",
+ "map_repeater": "Répéteur",
+ "map_room": "Room Server",
"map_sensor": "Capteur",
"map_pinDm": "Clé (DM)",
"map_pinPrivate": "Verrouiller (Privé)",
@@ -664,8 +682,8 @@
"map_showSharedMarkers": "Afficher les marqueurs partagés",
"map_lastSeenTime": "Dernière fois vu",
"map_sharedPin": "Clé partagée",
- "map_joinRoom": "Rejoindre la salle",
- "map_manageRepeater": "Gérer le répétiteur",
+ "map_joinRoom": "Rejoindre le room server",
+ "map_manageRepeater": "Gérer le répéteur",
"mapCache_title": "Cache de Carte Hors Ligne",
"mapCache_selectAreaFirst": "Sélectionner une zone pour la mise en cache en premier",
"mapCache_noTilesToDownload": "Aucun tuilage à télécharger pour cette zone.",
@@ -687,7 +705,7 @@
}
}
},
- "mapCache_cachedTilesWithFailed": "Tiles mis en cache ({downloaded}) ({failed} ratés)",
+ "mapCache_cachedTilesWithFailed": "Tuiles mis en cache ({downloaded}) ({failed} ratés)",
"@mapCache_cachedTilesWithFailed": {
"placeholders": {
"downloaded": {
@@ -734,7 +752,7 @@
}
}
},
- "mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
+ "mapCache_boundsLabel": "N {north}, S {south}, E {east}, O {west}",
"@mapCache_boundsLabel": {
"placeholders": {
"north": {
@@ -751,7 +769,7 @@
}
}
},
- "time_justNow": "Il y a tout juste maintenant",
+ "time_justNow": "Maintenant",
"time_minutesAgo": "{minutes} minutes auparavant",
"@time_minutesAgo": {
"placeholders": {
@@ -788,13 +806,13 @@
"time_allTime": "Tout le temps",
"dialog_disconnect": "Déconnecter",
"dialog_disconnectConfirm": "Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?",
- "login_repeaterLogin": "Connexion au répétiteur",
- "login_roomLogin": "Connexion Salle",
+ "login_repeaterLogin": "Connexion au répéteur",
+ "login_roomLogin": "Connexion Room Server",
"login_password": "Mot de passe",
"login_enterPassword": "Entrez votre mot de passe",
"login_savePassword": "Sauvegarder le mot de passe",
"login_savePasswordSubtitle": "Le mot de passe sera stocké en toute sécurité sur cet appareil.",
- "login_repeaterDescription": "Entrez le mot de passe du répétiteur pour accéder aux paramètres et à l'état.",
+ "login_repeaterDescription": "Entrez le mot de passe du répéteur pour accéder aux paramètres et à l'état.",
"login_roomDescription": "Entrez le mot de passe de la pièce pour accéder aux paramètres et à l'état.",
"login_routing": "Redirection",
"login_routingMode": "Mode de routage",
@@ -847,7 +865,7 @@
"path_labelHexPrefixes": "Préfixes hexadécimaux",
"path_helperMaxHops": "Max 64 sauts. Chaque préfixe fait 2 caractères hexadécimaux (1 octet)",
"path_selectFromContacts": "Sélectionner à partir des contacts :",
- "path_noRepeatersFound": "Aucun répéteur ou serveur de salle n'a été trouvé.",
+ "path_noRepeatersFound": "Aucun répéteur ou room server n'a été trouvé.",
"path_customPathsRequire": "Les chemins personnalisés nécessitent des sauts intermédiaires qui peuvent transmettre des messages.",
"path_invalidHexPrefixes": "Préfixes hexadécimaux invalides : {prefixes}",
"@path_invalidHexPrefixes": {
@@ -859,17 +877,17 @@
},
"path_tooLong": "Le chemin est trop long. Maximum 64 sauts autorisés.",
"path_setPath": "Définir le chemin",
- "repeater_management": "Gestion des répétiteurs",
+ "repeater_management": "Gestion des répéteurs",
"repeater_managementTools": "Outils de Gestion",
"repeater_status": "État",
- "repeater_statusSubtitle": "Afficher l'état, les statistiques et les voisins du répétiteur",
+ "repeater_statusSubtitle": "Afficher l'état, les statistiques et les voisins du répéteur",
"repeater_telemetry": "Télémetrie",
"repeater_telemetrySubtitle": "Afficher la télémétrie des capteurs et les statistiques du système",
"repeater_cli": "CLI",
- "repeater_cliSubtitle": "Envoyer des commandes au répétiteur",
+ "repeater_cliSubtitle": "Envoyer des commandes au répéteur",
"repeater_settings": "Paramètres",
- "repeater_settingsSubtitle": "Configurer les paramètres du répétiteur",
- "repeater_statusTitle": "État du répétiteur",
+ "repeater_settingsSubtitle": "Configurer les paramètres du répéteur",
+ "repeater_statusTitle": "État du répéteur",
"repeater_routingMode": "Mode de routage",
"repeater_autoUseSavedPath": "Auto (utiliser le chemin sauvegardé)",
"repeater_forceFloodMode": "Mode tout le réseau forcé",
@@ -899,7 +917,7 @@
"repeater_packetStatistics": "Statistiques des paquets",
"repeater_sent": "Envoyé",
"repeater_received": "Reçu",
- "repeater_duplicates": "Dupliques",
+ "repeater_duplicates": "Doublons",
"repeater_daysHoursMinsSecs": "{days} jours {hours}h {minutes}m {seconds}s",
"@repeater_daysHoursMinsSecs": {
"placeholders": {
@@ -964,10 +982,10 @@
}
}
},
- "repeater_settingsTitle": "Paramètres du répétiteur",
+ "repeater_settingsTitle": "Paramètres du répéteur",
"repeater_basicSettings": "Paramètres de base",
- "repeater_repeaterName": "Nom du répétiteur",
- "repeater_repeaterNameHelper": "Afficher le nom de ce répétiteur",
+ "repeater_repeaterName": "Nom du répéteur",
+ "repeater_repeaterNameHelper": "Afficher le nom de ce répéteur",
"repeater_adminPassword": "Mot de passe Administrateur",
"repeater_adminPasswordHelper": "Mot de passe d'accès complet",
"repeater_guestPassword": "Mot de passe invité",
@@ -978,16 +996,16 @@
"repeater_txPower": "TX Puissance",
"repeater_txPowerHelper": "1-30 dBm",
"repeater_bandwidth": "Bande passante",
- "repeater_spreadingFactor": "Facteur de répartition",
- "repeater_codingRate": "Taux de codage",
+ "repeater_spreadingFactor": "Facteur de répartition (SF)",
+ "repeater_codingRate": "Taux de codage (CR)",
"repeater_locationSettings": "Paramètres de localisation",
"repeater_latitude": "Latitude",
"repeater_latitudeHelper": "Degrés décimaux (par exemple, 37.7749)",
"repeater_longitude": "Longitude",
"repeater_longitudeHelper": "Degrés décimaux (par exemple, -122,4194)",
"repeater_features": "Fonctionnalités",
- "repeater_packetForwarding": "Transfert de paquets",
- "repeater_packetForwardingSubtitle": "Activer le répétiteur pour transmettre des paquets",
+ "repeater_packetForwarding": "Mode répéteur",
+ "repeater_packetForwardingSubtitle": "Activer le répéteur pour transmettre des paquets",
"repeater_guestAccess": "Accès Invité",
"repeater_guestAccessSubtitle": "Autoriser l'accès invité en lecture seule",
"repeater_privacyMode": "Mode de confidentialité",
@@ -1012,16 +1030,16 @@
}
},
"repeater_encryptedAdvertInterval": "Intervalle d'annonces cryptées",
- "repeater_dangerZone": "Zone d'alerte",
+ "repeater_dangerZone": "Zone dangereuse",
"repeater_rebootRepeater": "Redémarrer Répéteur",
- "repeater_rebootRepeaterSubtitle": "Réinitialiser l'appareil répétiteur",
- "repeater_rebootRepeaterConfirm": "Êtes-vous sûr de vouloir redémarrer ce répétiteur ?",
+ "repeater_rebootRepeaterSubtitle": "Réinitialiser l'appareil répéteur",
+ "repeater_rebootRepeaterConfirm": "Êtes-vous sûr de vouloir redémarrer ce répéteur ?",
"repeater_regenerateIdentityKey": "Ré générer la clé d'identité",
"repeater_regenerateIdentityKeySubtitle": "Générer une nouvelle paire de clés publique/privée",
- "repeater_regenerateIdentityKeyConfirm": "Cela générera une nouvelle identité pour le répétiteur. Continuer ?",
+ "repeater_regenerateIdentityKeyConfirm": "Cela générera une nouvelle identité pour le répéteur. Continuer ?",
"repeater_eraseFileSystem": "Supprimer le système de fichiers",
- "repeater_eraseFileSystemSubtitle": "Formater le système de fichiers du répétiteur",
- "repeater_eraseFileSystemConfirm": "AVERTISSEMENT : Cela effacera toutes les données du répétiteur. Cela ne peut pas être annulé !",
+ "repeater_eraseFileSystemSubtitle": "Formater le système de fichiers du répéteur",
+ "repeater_eraseFileSystemConfirm": "AVERTISSEMENT : Cela effacera toutes les données du répéteur. Cela ne peut pas être annulé !",
"repeater_eraseSerialOnly": "Erase n'est disponible que via la console série.",
"repeater_commandSent": "Commande envoyée : {command}",
"@repeater_commandSent": {
@@ -1073,7 +1091,7 @@
}
}
},
- "repeater_cliTitle": "Répétiteur CLI",
+ "repeater_cliTitle": "Répéteur CLI",
"repeater_debugNextCommand": "Déboguer Prochaine Commande",
"repeater_commandHelp": "Aide",
"repeater_clearHistory": "Effacer l'historique",
@@ -1107,13 +1125,13 @@
"repeater_cliHelpClearStats": "Réinitialise divers compteurs de statistiques à zéro.",
"repeater_cliHelpSetAf": "Définit le facteur de temps d'air.",
"repeater_cliHelpSetTx": "Définit la puissance de transmission LoRa en dBm (réinitialisation requise pour appliquer).",
- "repeater_cliHelpSetRepeat": "Active ou désactive le rôle du répétiteur pour ce nœud.",
- "repeater_cliHelpSetAllowReadOnly": "(Serveur de pièce) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)",
+ "repeater_cliHelpSetRepeat": "Active ou désactive le rôle du répéteur pour ce nœud.",
+ "repeater_cliHelpSetAllowReadOnly": "(Room server) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)",
"repeater_cliHelpSetFloodMax": "Définit le nombre maximal de sauts pour les paquets de balayage entrants (si >= max, le paquet n'est pas acheminé).",
"repeater_cliHelpSetIntThresh": "Définit le seuil d'interférence (en dB). La valeur par défaut est de 14. Définir sur 0 désactive la détection des interférences de canal.",
"repeater_cliHelpSetAgcResetInterval": "Définit l'intervalle pour réinitialiser le contrôleur de gain automatique. Mettez à 0 pour désactiver.",
"repeater_cliHelpSetMultiAcks": "Active ou désactive la fonctionnalité « double ACKs ».",
- "repeater_cliHelpSetAdvertInterval": "Définit l'intervalle du minuteur pour envoyer un paquet d'annonce local (sans relais). Définir sur 0 pour désactiver.",
+ "repeater_cliHelpSetAdvertInterval": "Définit l'intervalle entre chaque émission d'une annonce locale (sans relais). Définir sur 0 pour désactiver.",
"repeater_cliHelpSetFloodAdvertInterval": "Définit l'intervalle du minuteur en heures pour envoyer un paquet d'annonce massive. Définir sur 0 pour désactiver.",
"repeater_cliHelpSetGuestPassword": "Définit/met à jour le mot de passe de l'invité. (pour les répéteurs, les connexions d'invités peuvent envoyer la requête \"Get Stats\")",
"repeater_cliHelpSetName": "Définit le nom de l'annonce.",
@@ -1135,7 +1153,7 @@
"repeater_cliHelpLogStart": "Démarre l'enregistrement des paquets dans le système de fichiers.",
"repeater_cliHelpLogStop": "Arrêter de journaliser les paquets vers le système de fichiers.",
"repeater_cliHelpLogErase": "Supprime les journaux de paquets du système de fichiers.",
- "repeater_cliHelpNeighbors": "Affiche une liste d'autres nœuds répétiteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4",
+ "repeater_cliHelpNeighbors": "Affiche une liste d'autres nœuds répéteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4",
"repeater_cliHelpNeighborRemove": "Supprime la première entrée correspondante (par préfixe de clé publique (hexadécimal)) de la liste des voisins.",
"repeater_cliHelpRegion": "(série uniquement) Liste toutes les régions définies et les autorisations actuelles d'annonces sur tout le réseau (flood).",
"repeater_cliHelpRegionLoad": "REMARQUE : il s'agit d'une invocation multi-commande spéciale. Chaque commande subséquente est un nom de région (indenté avec des espaces pour indiquer la hiérarchie parent, avec un minimum d'un espace). Terminé par l'envoi d'une ligne vide/commande.",
@@ -1159,8 +1177,8 @@
"repeater_settingsCategory": "Paramètres",
"repeater_bridge": "Pont",
"repeater_logging": "Journalisation",
- "repeater_neighborsRepeaterOnly": "Voisins (Uniquement répétiteur)",
- "repeater_regionManagementRepeaterOnly": "Gestion des régions (uniquement pour le répétiteur)",
+ "repeater_neighborsRepeaterOnly": "Voisins (Uniquement répéteur)",
+ "repeater_regionManagementRepeaterOnly": "Gestion des régions (uniquement pour le répéteur)",
"repeater_regionNote": "Les commandes de région ont été introduites pour gérer les définitions et les autorisations des régions.",
"repeater_gpsManagement": "Gestion GPS",
"repeater_gpsNote": "La commande GPS a été introduite pour gérer les sujets liés à la localisation.",
@@ -1229,7 +1247,7 @@
"channelPath_title": "Chemin de paquet",
"channelPath_viewMap": "Afficher la carte",
"channelPath_otherObservedPaths": "Autres chemins observés",
- "channelPath_repeaterHops": "Sauts du répétiteur",
+ "channelPath_repeaterHops": "Sauts du répéteur",
"channelPath_noHopDetails": "Les détails de l'envoi ne sont pas fournis pour ce paquet.",
"channelPath_messageDetails": "Détails du message",
"channelPath_senderLabel": "Expéditeur",
@@ -1294,7 +1312,7 @@
}
},
"channelPath_mapTitle": "Carte du chemin",
- "channelPath_noRepeaterLocations": "Aucune position de répétiteur disponible pour ce chemin.",
+ "channelPath_noRepeaterLocations": "Aucune position de répéteur disponible pour ce chemin.",
"channelPath_primaryPath": "Chemin {index} (Principal)",
"@channelPath_primaryPath": {
"placeholders": {
@@ -1327,16 +1345,16 @@
"channelPath_unknownRepeater": "Répéteur Inconnu",
"listFilter_tooltip": "Filtrer et trier",
"listFilter_sortBy": "Trier par",
- "listFilter_latestMessages": "Dernières messages",
+ "listFilter_latestMessages": "Derniers messages",
"listFilter_heardRecently": "Écoute récemment",
"listFilter_az": "A à Z",
"listFilter_filters": "Filtres",
"listFilter_all": "Tout",
"listFilter_users": "Utilisateurs",
"listFilter_repeaters": "Répéteurs",
- "listFilter_roomServers": "Serveurs de pièce",
+ "listFilter_roomServers": "Room servers",
"listFilter_unreadOnly": "Messages non lus seulement",
- "listFilter_newGroup": "Nouvelle groupe",
+ "listFilter_newGroup": "Nouveau groupe",
"@neighbors_errorLoading": {
"placeholders": {
"error": {
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighbours": "Voisins",
- "repeater_neighboursSubtitle": "Afficher les voisins de saut nuls.",
+ "repeater_neighbors": "Voisins",
+ "repeater_neighborsSubtitle": "Afficher les voisins de saut nuls.",
"neighbors_receivedData": "Données des voisins reçues",
"neighbors_requestTimedOut": "Les voisins demandent un délai.",
"neighbors_errorLoading": "Erreur lors du chargement des voisins : {error}",
- "neighbors_repeatersNeighbours": "Répéteurs Voisins",
+ "neighbors_repeatersNeighbors": "Répéteurs Voisins",
"neighbors_noData": "Aucune donnée concernant les voisins disponible.",
"channels_createPrivateChannelDesc": "Sécurisé avec une clé secrète.",
"channels_joinPrivateChannel": "Rejoindre un Canal Privé",
@@ -1359,10 +1377,10 @@
"channels_joinPublicChannelDesc": "Tout le monde peut rejoindre ce canal.",
"channels_joinHashtagChannel": "Rejoindre un Canal Hashtag",
"channels_joinHashtagChannelDesc": "N'importe qui peut rejoindre les canaux #hashtag.",
- "channels_scanQrCode": "Scanner un code QR",
+ "channels_scanQrCode": "Scanner un QR code",
"channels_scanQrCodeComingSoon": "Bientôt disponible",
"channels_enterHashtag": "Entrez le hashtag",
- "channels_hashtagHint": "ex. #équipe",
+ "channels_hashtagHint": "ex. #equipe",
"@neighbors_unknownContact": {
"placeholders": {
"pubkey": {
@@ -1379,12 +1397,12 @@
},
"neighbors_unknownContact": "Clé publique inconnue {pubkey}",
"neighbors_heardAgo": "Écouté : {time} auparavant",
- "settings_locationGPSEnable": "Habilita GPS",
- "settings_locationGPSEnableSubtitle": "Habilita la actualización automática de la ubicación mediante GPS.",
- "settings_locationIntervalSec": "Intervalo pour GPS (Segundos)",
- "settings_locationIntervalInvalid": "El intervalo debe ser de al menos 60 segundos y menor que 86400 segundos.",
- "contacts_manageRoom": "Gestionar Servidor de Habitación",
- "room_management": "Administración del Servidor de Habitación",
+ "settings_locationGPSEnable": "Activer le GPS",
+ "settings_locationGPSEnableSubtitle": "Activer la mise à jour automatique de la position via GPS",
+ "settings_locationIntervalSec": "Intervalle de mise-à-jour du GPS (Secondes)",
+ "settings_locationIntervalInvalid": "L'intervalle doit être compris entre 60 et 86400 secondes.",
+ "contacts_manageRoom": "Gérer le Room Server",
+ "room_management": "Administrattion Room Server",
"@community_joinConfirmation": {
"placeholders": {
"name": {
@@ -1448,8 +1466,8 @@
"community_join": "Rejoindre",
"community_joinTitle": "Rejoindre la communauté",
"community_joinConfirmation": "Souhaitez-vous rejoindre la communauté \"{name}\" ?",
- "community_scanQr": "Scanner la communauté QR",
- "community_scanInstructions": "Pointez l'appareil photo vers un code QR communautaire.",
+ "community_scanQr": "Scanner un QR code de communauté",
+ "community_scanInstructions": "Pointez l'appareil photo vers un QR code de communauté.",
"community_showQr": "Afficher le QR Code",
"community_publicChannel": "Communauté Publique",
"community_hashtagChannel": "Hashtag Communauté",
@@ -1460,29 +1478,600 @@
"community_qrTitle": "Partager Communauté",
"community_qrInstructions": "Scanner ce QR code pour rejoindre {name}",
"community_hashtagPrivacyHint": "Les canaux hashtag de la communauté ne sont accessibles qu'aux membres de la communauté",
- "community_invalidQrCode": "Code QR de communauté non valide",
+ "community_invalidQrCode": "QR code de communauté non valide",
"community_alreadyMember": "Déjà membre",
"community_alreadyMemberMessage": "Vous êtes déjà membre de \"{name}\".",
"community_addPublicChannel": "Ajouter un Canal Public de la Communauté",
"community_addPublicChannelHint": "Ajouter automatiquement le canal public pour cette communauté",
"community_noCommunities": "Aucun groupe n'a été rejoint pour le moment.",
- "community_scanOrCreate": "Scanner un code QR ou créer une communauté pour commencer",
+ "community_scanOrCreate": "Scanner un QR code ou créer une communauté pour commencer",
"community_manageCommunities": "Gérer les Communautés",
"community_delete": "Quitter la communauté",
"community_deleteConfirm": "Quitter \"{name}\" ?",
"community_deleteChannelsWarning": "Cela supprimera également {count} canal/canaux et leurs messages.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Communauté \"{name}\" quittée",
"community_addHashtagChannel": "Ajouter un Hashtag Communauté",
- "community_addHashtagChannelDesc": "Ajouter un canal hachage pour cette communauté",
+ "community_addHashtagChannelDesc": "Ajouter un canal hashtag pour cette communauté",
"community_selectCommunity": "Sélectionner Communauté",
"community_regularHashtag": "Hashtag régulier",
"community_regularHashtagDesc": "Hashtag public (tout le monde peut rejoindre)",
"community_communityHashtag": "Hashtag de la communauté",
"community_communityHashtagDesc": "Exclusif aux membres de la communauté",
- "community_forCommunity": "Pour {name}"
+ "community_forCommunity": "Pour {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "Régénérer le secret",
+ "community_regenerateSecretConfirm": "Régénérer la clé secrète pour \"{name}\" ? Tous les membres devront scanner le nouveau QR code pour continuer à communiquer.",
+ "community_regenerate": "Régénérer",
+ "community_secretRegenerated": "Mot de passe secret régénéré pour \"{name}\"",
+ "community_scanToUpdateSecret": "Scanner le nouveau QR code pour mettre à jour le mot de passe pour \"{name}\"",
+ "community_updateSecret": "Mettre à jour le secret",
+ "community_secretUpdated": "Modification secrète mise à jour pour \"{name}\"",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Vous",
+ "pathTrace_refreshTooltip": "Actualiser Path Trace",
+ "pathTrace_failed": "Traçage du chemin échoué.",
+ "pathTrace_notAvailable": "Tracé de chemin non disponible.",
+ "contacts_pathTrace": "Traçage de chemin",
+ "contacts_repeaterPathTrace": "Tracer le chemin vers le répéteur",
+ "contacts_repeaterPing": "Pinguer le répéteur",
+ "contacts_roomPathTrace": "Traçage du chemin vers le room server",
+ "contacts_chatTraceRoute": "Tracer le chemin",
+ "contacts_pathTraceTo": "Tracer l'itinéraire vers {name}",
+ "contacts_ping": "Ping",
+ "contacts_roomPing": "Pinguer le room server",
+ "contacts_invalidAdvertFormat": "Données de contact non valides",
+ "appSettings_languageUk": "Ukrainien",
+ "appSettings_languageRu": "Russe",
+ "appSettings_enableMessageTracing": "Activer le traçage des messages",
+ "appSettings_enableMessageTracingSubtitle": "Afficher les métadonnées détaillées de routage et de synchronisation des messages",
+ "contacts_clipboardEmpty": "Le presse-papiers est vide.",
+ "contacts_contactImported": "Le contact a été importé.",
+ "contacts_floodAdvert": "Annonce à tout le réseau",
+ "contacts_contactImportFailed": "Échec de l'importation du contact.",
+ "contacts_zeroHopAdvert": "Annonce Zero saut",
+ "contacts_copyAdvertToClipboard": "Copier l'annonce dans le presse-papiers",
+ "contacts_addContactFromClipboard": "Ajouter un contact depuis le presse-papiers",
+ "contacts_ShareContact": "Copier le contact dans le presse-papiers",
+ "contacts_ShareContactZeroHop": "Partager un contact par annonce",
+ "contacts_contactAdvertCopied": "Annonce copiée dans le presse-papiers.",
+ "contacts_contactAdvertCopyFailed": "La copie de l'annonce vers le presse-papiers a échoué.",
+ "contacts_zeroHopContactAdvertSent": "Envoyer un contact par annonce.",
+ "contacts_zeroHopContactAdvertFailed": "Échec de l'envoi du contact.",
+ "notification_activityTitle": "Activité MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{message} other{messages}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{message de canal} other{messages de canal}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{nouveau nœud} other{nouveaux nœuds}}",
+ "notification_newTypeDiscovered": "Nouveau {contactType} découvert",
+ "notification_receivedNewMessage": "Nouveau message reçu",
+ "settings_gpxExportRepeaters": "Exporter les répéteurs / room servers au format GPX",
+ "settings_gpxExportRepeatersSubtitle": "Exporte les répéteurs / roomserver avec une localisation vers un fichier GPX.",
+ "settings_gpxExportNoContacts": "Aucun contact à exporter.",
+ "settings_gpxExportNotAvailable": "Non pris en charge sur votre appareil/Système d'exploitation",
+ "settings_gpxExportError": "Une erreur s'est produite lors de l'exportation.",
+ "settings_gpxExportRepeatersRoom": "Emplacements des répéteurs et room servers",
+ "settings_gpxExportContacts": "Exporter les compagnons au format GPX",
+ "settings_gpxExportAll": "Exporter tous les contacts au format GPX",
+ "settings_gpxExportAllSubtitle": "Exporte tous les contacts avec une localisation vers un fichier GPX.",
+ "settings_gpxExportContactsSubtitle": "Exporte les compagnons avec un emplacement vers un fichier GPX.",
+ "settings_gpxExportChat": "Emplacements des compagnons",
+ "settings_gpxExportSuccess": "Fichier GPX exporté avec succès.",
+ "settings_gpxExportAllContacts": "Tous les emplacements des contacts",
+ "settings_gpxExportShareText": "Données de carte exportées à partir de meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open exporter les données de carte GPX",
+ "pathTrace_someHopsNoLocation": "Un ou plusieurs des sauts manquent d'une localisation !",
+ "map_tapToAdd": "Appuyez sur les nœuds pour les ajouter au chemin.",
+ "pathTrace_clearTooltip": "Effacer le chemin",
+ "map_pathTraceCancelled": "Traçage de chemin annulé",
+ "map_removeLast": "Supprimer le dernier",
+ "map_runTrace": "Exécuter la traçage de chemin",
+ "scanner_bluetoothOffMessage": "Veuillez activer le Bluetooth pour rechercher des appareils.",
+ "scanner_chromeRequired": "Navigateur Chrome requis",
+ "scanner_chromeRequiredMessage": "Cette application web nécessite Google Chrome ou un navigateur basé sur Chromium pour le support Bluetooth.",
+ "scanner_bluetoothOff": "Le Bluetooth est désactivé.",
+ "scanner_enableBluetooth": "Activer le Bluetooth",
+ "snrIndicator_lastSeen": "Dernière fois vu",
+ "snrIndicator_nearByRepeaters": "Répéteurs à proximité",
+ "chat_ShowAllPaths": "Afficher tous les chemins",
+ "settings_clientRepeatFreqWarning": "Pour les transmissions hors réseau, il est nécessaire d'utiliser les fréquences de 433, 869 ou 918 MHz.",
+ "settings_clientRepeatSubtitle": "Permettez à cet appareil de répéter les paquets de données pour les autres.",
+ "settings_clientRepeat": "Répétition hors réseau",
+ "settings_aboutOpenMeteoAttribution": "Données d'élévation LOS : Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Unités",
+ "appSettings_unitsMetric": "Métrique (m/km)",
+ "appSettings_unitsImperial": "Impérial (ft / mi)",
+ "map_lineOfSight": "Ligne de vue",
+ "map_losScreenTitle": "Ligne de vue",
+ "losSelectStartEnd": "Sélectionnez les nœuds de début et de fin pour LOS.",
+ "losRunFailed": "Échec de la vérification de la ligne de vue : {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Effacer tous les points",
+ "losRunToViewElevationProfile": "Exécutez LOS pour afficher le profil d'altitude",
+ "losMenuTitle": "Menu LOS",
+ "losMenuSubtitle": "Appuyez sur les nœuds ou appuyez longuement sur la carte pour des points personnalisés",
+ "losShowDisplayNodes": "Afficher les nœuds d'affichage",
+ "losCustomPoints": "Points personnalisés",
+ "losCustomPointLabel": "Personnalisé {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Point A",
+ "losPointB": "Point B",
+ "losAntennaA": "Antenne A : {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenne B : {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Exécuter la LOS",
+ "losNoElevationData": "Aucune donnée d'altitude",
+ "losProfileClear": "{distance} {distanceUnit}, LOS clair, clairance minimale {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, bloqué par {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS : vérification...",
+ "losStatusNoData": "LOS : aucune donnée",
+ "losStatusSummary": "LOS : {clear}/{total} clair, {blocked} bloqué, {unknown} inconnu",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Données d'altitude indisponibles pour un ou plusieurs échantillons.",
+ "losErrorInvalidInput": "Données de points/d'altitude non valides pour le calcul de la LOS.",
+ "losRenameCustomPoint": "Renommer le point personnalisé",
+ "losPointName": "Nom du point",
+ "losShowPanelTooltip": "Afficher le panneau LOS",
+ "losHidePanelTooltip": "Masquer le panneau LOS",
+ "losElevationAttribution": "Données d’altitude : Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Horizon radio",
+ "losLegendLosBeam": "Ligne de visée",
+ "losLegendTerrain": "Terrain",
+ "losFrequencyLabel": "Fréquence",
+ "losFrequencyInfoTooltip": "Voir les détails du calcul",
+ "losFrequencyDialogTitle": "Calcul de l’horizon radio",
+ "losFrequencyDialogDescription": "À partir de k={baselineK} à {baselineFreq} MHz, le calcul ajuste le facteur k pour la bande actuelle de {frequencyMHz} MHz, ce qui définit la limite incurvée de l'horizon radio.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_addToFavorites": "Ajouter à mes favoris",
+ "listFilter_removeFromFavorites": "Supprimer des favoris",
+ "listFilter_favorites": "Préférences",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Non lu",
+ "contacts_searchFavorites": "Rechercher {number}{str} Favoris...",
+ "contacts_searchUsers": "Rechercher {number}{str} utilisateurs...",
+ "contacts_searchRoomServers": "Rechercher {number}{str} room server...",
+ "contacts_searchRepeaters": "Rechercher {number}{str} Répéteurs...",
+ "contacts_searchContactsNoNumber": "Rechercher des contacts...",
+ "settings_contactSettings": "Paramètres de contact",
+ "settings_contactSettingsSubtitle": "Paramètres pour l'ajout de contacts",
+ "contactsSettings_autoAddRepeatersTitle": "Ajouter automatiquement les répéteurs",
+ "contactsSettings_autoAddRepeatersSubtitle": "Autoriser le compagnon à ajouter automatiquement les répéteurs découverts",
+ "contactsSettings_autoAddRoomServersTitle": "Ajouter automatiquement les room servers",
+ "contactsSettings_autoAddRoomServersSubtitle": "Autoriser le compagnon à ajouter automatiquement les room servers découverts",
+ "contactsSettings_otherTitle": "Autres paramètres liés aux contacts",
+ "contactsSettings_title": "Paramètres des contacts",
+ "contactsSettings_autoAddUsersTitle": "Ajouter automatiquement les utilisateurs",
+ "contactsSettings_autoAddTitle": "Découverte automatique",
+ "contactsSettings_autoAddSensorsTitle": "Ajouter automatiquement les capteurs",
+ "contactsSettings_autoAddUsersSubtitle": "Autoriser le compagnon à ajouter automatiquement les utilisateurs découverts",
+ "discoveredContacts_noMatching": "Aucun contact correspondant",
+ "discoveredContacts_contactAdded": "Contact ajouté",
+ "discoveredContacts_addContact": "Ajouter un contact",
+ "discoveredContacts_copyContact": "Copier le contact dans le presse-papiers",
+ "discoveredContacts_deleteContact": "Supprimer le contact",
+ "contactsSettings_overwriteOldestTitle": "Écraser le plus ancien",
+ "contactsSettings_autoAddSensorsSubtitle": "Autoriser le compagnon à ajouter automatiquement les capteurs découverts.",
+ "discoveredContacts_Title": "Contacts découverts",
+ "discoveredContacts_searchHint": "Rechercher des contacts découverts",
+ "contactsSettings_overwriteOldestSubtitle": "Lorsque la liste de contacts est pleine, le contact le plus ancien non favori sera remplacé.",
+ "common_deleteAll": "Supprimer tout",
+ "discoveredContacts_deleteContactAll": "Supprimer tous les contacts découverts",
+ "discoveredContacts_deleteContactAllContent": "Êtes-vous sûr de vouloir supprimer tous les contacts découverts ?",
+ "map_showGuessedLocations": "Afficher les emplacements des nœuds estimés",
+ "map_guessedLocation": "Lieu deviné",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenTitle": "Connectez via USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenSubtitle": "Sélectionnez un périphérique série détecté et connectez-vous directement à votre nœud MeshCore.",
+ "usbScreenStatus": "Sélectionnez un périphérique USB",
+ "usbScreenNote": "La communication série USB est active sur les appareils Android et les plateformes de bureau compatibles.",
+ "usbScreenEmptyState": "Aucun périphérique USB n'a été trouvé. Veuillez en brancher un et rafraîchir la page.",
+ "usbErrorPermissionDenied": "L'accès via USB a été refusé.",
+ "usbErrorDeviceMissing": "Le périphérique USB sélectionné n'est plus disponible.",
+ "usbErrorInvalidPort": "Sélectionnez un périphérique USB valide.",
+ "usbErrorBusy": "Une autre demande de connexion USB est déjà en cours.",
+ "usbErrorNotConnected": "Aucun appareil USB n'est connecté.",
+ "usbErrorOpenFailed": "Impossible d'ouvrir l'appareil USB sélectionné.",
+ "usbErrorConnectFailed": "Impossible de se connecter à l'appareil USB sélectionné.",
+ "usbErrorUnsupported": "La communication série USB n'est pas prise en charge sur cette plateforme.",
+ "usbErrorAlreadyActive": "Une connexion USB est déjà établie.",
+ "usbErrorNoDeviceSelected": "Aucun appareil USB n'a été sélectionné.",
+ "usbErrorPortClosed": "La connexion USB n'est pas établie.",
+ "usbFallbackDeviceName": "Dispositif de communication série sur le Web",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_notConnected": "Sélectionnez un périphérique USB",
+ "usbConnectionFailed": "Échec de la connexion USB : {error}",
+ "usbStatus_connecting": "Connexion au périphérique USB...",
+ "usbStatus_searching": "Recherche de périphériques USB...",
+ "usbErrorConnectTimedOut": "La connexion a expiré. Assurez-vous que l'appareil dispose du firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostLabel": "Adresse IP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "Établir une connexion via TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Entrez l'adresse de destination et connectez-vous.",
+ "tcpStatus_connectingTo": "Connexion à {endpoint}...",
+ "tcpErrorHostRequired": "Une adresse IP est obligatoire.",
+ "tcpErrorPortInvalid": "La taille du port doit être comprise entre 1 et 65535.",
+ "tcpErrorUnsupported": "Le protocole TCP n'est pas pris en charge sur cette plateforme.",
+ "tcpErrorTimedOut": "La connexion TCP a expiré.",
+ "tcpConnectionFailed": "Échec de la connexion TCP : {error}",
+ "map_showDiscoveryContacts": "Afficher les contacts de découverte",
+ "map_setAsMyLocation": "Définir comme ma localisation",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Paramètres de confidentialité",
+ "settings_privacySubtitle": "Contrôlez les informations partagées",
+ "settings_telemetryLocationMode": "Mode d'emplacement de télémétrie",
+ "settings_telemetryEnvironmentMode": "Mode d'environnement de télémétrie",
+ "settings_advertLocation": "Emplacement de l'annonce",
+ "settings_advertLocationSubtitle": "Inclure l'emplacement dans l'annonce",
+ "settings_denyAll": "Refuser tout",
+ "settings_allowByContact": "Autoriser par drapeaux de contact",
+ "settings_privacySettingsDescription": "Choisissez les informations que votre appareil partage avec les autres.",
+ "settings_allowAll": "Autoriser tout",
+ "contact_info": "Informations de contact",
+ "settings_telemetryBaseMode": "Mode de base Télémétrie",
+ "contact_teleBase": "Base de télémétrie",
+ "contact_teleLoc": "Emplacement de télémétrie",
+ "contact_teleLocSubtitle": "Autoriser le partage des données de localisation",
+ "contact_teleEnv": "Environnement Télémétrie",
+ "contact_teleEnvSubtitle": "Autoriser le partage des données des capteurs d'environnement",
+ "contact_telemetry": "Télémétrie",
+ "contact_settings": "Paramètres de contact",
+ "contact_lastSeen": "Dernière fois vu",
+ "contact_clearChat": "Effacer la conversation",
+ "contact_teleBaseSubtitle": "Autoriser le partage du niveau de batterie et de la télémétrie de base",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeightSubtitle": "Poids maximal qu'un itinéraire peut accumuler grâce à des livraisons réussies.",
+ "appSettings_initialRouteWeight": "Poids initial de l'itinéraire",
+ "appSettings_maxRouteWeight": "Poids maximal autorisé pour le trajet",
+ "appSettings_initialRouteWeightSubtitle": "Poids de départ pour les nouveaux chemins découverts",
+ "appSettings_routeWeightSuccessIncrement": "Augmentation du poids de réussite",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Poids ajouté à un itinéraire après une livraison réussie.",
+ "appSettings_routeWeightFailureDecrement": "Réduction du poids de pénalité",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Poids retiré d'un itinéraire après une tentative de livraison infructueuse.",
+ "appSettings_maxMessageRetries": "Nombre maximal de tentatives de récupération de messages",
+ "appSettings_maxMessageRetriesSubtitle": "Nombre de tentatives de relance avant de marquer un message comme ayant échoué.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_multiAck": "Multi-ACKs : {value}",
+ "settings_telemetryModeUpdated": "Le mode télémétrie a été mis à jour",
+ "map_showOverlaps": "Chevauchement de la touche répétitive",
+ "map_runTraceWithReturnPath": "Revenir sur le même chemin.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Veuillez patienter un instant avant de réessayer.",
+ "appSettings_jumpToOldestUnread": "Accéder au message le plus ancien non lu",
+ "appSettings_languageHu": "Hongrois",
+ "appSettings_jumpToOldestUnreadSubtitle": "Lorsque vous ouvrez une conversation contenant des messages non lus, faites défiler la page jusqu'au premier message non lu, plutôt que jusqu'au dernier.",
+ "appSettings_languageJa": "Japonais",
+ "appSettings_languageKo": "Coréen",
+ "radioStats_tooltip": "Statistiques des radios et des réseaux sans fil",
+ "radioStats_screenTitle": "Statistiques de radio",
+ "radioStats_notConnected": "Connectez-vous à un appareil pour visualiser les statistiques de la radio.",
+ "radioStats_firmwareTooOld": "Les statistiques radio nécessitent un firmware compatible v8 ou une version ultérieure.",
+ "radioStats_waiting": "En attente des données…",
+ "radioStats_noiseFloor": "Niveau de bruit : {noiseDbm} dBm",
+ "radioStats_lastRssi": "Dernier RSSI : {rssiDbm} dBm",
+ "radioStats_lastSnr": "Dernier SNR : {snr} dB",
+ "radioStats_txAir": "Temps d'antenne à la télévision du Texas (total) : {seconds} s",
+ "radioStats_rxAir": "Temps d'utilisation de l'appareil RX (total) : {seconds} s",
+ "radioStats_chartCaption": "Niveau de bruit (dBm) sur les échantillons récents.",
+ "radioStats_stripNoise": "Niveau de bruit : {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Récupération des statistiques de la radio…",
+ "radioStats_settingsTile": "Statistiques de radio",
+ "radioStats_settingsSubtitle": "Niveau de bruit, RSSI, rapport signal/bruit (SNR) et temps d'antenne",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Traduire avant d'envoyer",
+ "translation_enableTitle": "Activer la traduction",
+ "translation_title": "Traduction",
+ "translation_enableSubtitle": "Traduire les messages entrants et permettre la traduction avant l'envoi.",
+ "translation_composerSubtitle": "Contrôle l'état par défaut de l'icône de traduction du composant.",
+ "translation_targetLanguage": "Langue cible",
+ "translation_useAppLanguage": "Utiliser la langue de l'application",
+ "translation_downloadedModelLabel": "Modèle téléchargé",
+ "translation_presetModelLabel": "Modèle Hugging Face préconfiguré",
+ "translation_manualUrlLabel": "URL du modèle manuel",
+ "translation_downloadModel": "Télécharger le modèle",
+ "translation_downloading": "Téléchargement...",
+ "translation_working": "Au travail...",
+ "translation_stop": "Arrêtez",
+ "translation_mergingChunks": "Fusion des fragments téléchargés dans le fichier final...",
+ "translation_downloadedModels": "Modèles téléchargés",
+ "translation_deleteModel": "Supprimer le modèle",
+ "translation_modelDownloaded": "Modèle de traduction téléchargé.",
+ "translation_downloadStopped": "Le téléchargement a été interrompu.",
+ "translation_downloadFailed": "Échec du téléchargement : {error}",
+ "translation_enterUrlFirst": "Entrez d'abord l'URL du modèle.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerEnabledHint": "Les messages seront traduits avant d'être envoyés.",
+ "translation_translateBeforeSending": "Traduire avant d'envoyer",
+ "translation_composerDisabledHint": "Envoyez des messages dans la langue originale, telle que vous l'avez tapée.",
+ "translation_messageTranslation": "Traduction du message",
+ "translation_translateTo": "Traduire en {language}",
+ "translation_translationOptions": "Options de traduction",
+ "translation_systemLanguage": "Langue du système",
+ "scanner_linuxPairingPinTitle": "Code PIN pour la connexion Bluetooth",
+ "scanner_linuxPairingHidePin": "Masquer le code PIN",
+ "scanner_linuxPairingPinPrompt": "Entrez le code PIN pour {deviceName} (laissez vide si nécessaire).",
+ "scanner_linuxPairingShowPin": "Afficher le code PIN",
+ "repeater_cliQuickClockSync": "Synchronisation de l'horloge",
+ "repeater_cliQuickDiscovery": "Découvrir les voisins",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Envoyer automatiquement une notification \"synchronisation de l'heure\" après une connexion réussie.",
+ "repeater_clockSyncAfterLogin": "Synchronisation de l'horloge après la connexion",
+ "repeater_guestTools": "Outils pour les invités",
+ "chat_sendMessage": "Envoyer un message",
+ "room_guest": "Informations sur le serveur",
+ "repeater_guest": "Informations sur les répéteurs"
}
diff --git a/lib/l10n/app_hu.arb b/lib/l10n/app_hu.arb
new file mode 100644
index 0000000..6f43463
--- /dev/null
+++ b/lib/l10n/app_hu.arb
@@ -0,0 +1,2115 @@
+{
+ "@@locale": "hu",
+ "appTitle": "MeshCore Open",
+ "nav_contacts": "Kapcsolatok",
+ "nav_channels": "Csatornák",
+ "nav_map": "Térkép",
+ "common_cancel": "Át kell venni",
+ "common_ok": "Rendben",
+ "common_connect": "Kapcsolódj",
+ "common_unknownDevice": "Tudatlan eszköz",
+ "common_save": "Mentés",
+ "common_delete": "Töröl",
+ "common_deleteAll": "Minden törlés",
+ "common_close": "Bezárás",
+ "common_edit": "Szerkesztés",
+ "common_add": "Hozzáad",
+ "common_settings": "Beállítások",
+ "common_disconnect": "Csatlakozást megszakasztani",
+ "common_connected": "Kapcsolódó",
+ "common_disconnected": "Elválasztva",
+ "common_create": "Készítsd",
+ "common_continue": "Folytatás",
+ "common_share": "Ossza meg",
+ "common_copy": "Másolat",
+ "common_retry": "Újrapróbálja",
+ "common_hide": "Elrejt",
+ "common_remove": "Eltávolít",
+ "common_enable": "Engedélyezés",
+ "common_disable": "Leteteszt",
+ "common_reboot": "Újraindítás",
+ "common_loading": "Betöltés...",
+ "common_notAvailable": "—",
+ "common_voltageValue": "{volts} V",
+ "@common_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "common_percentValue": "{percent}%",
+ "@common_percentValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ }
+ }
+ },
+ "scanner_title": "MeshCore nyitott",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "TCP-n keresztül kapcsolódjon",
+ "tcpHostLabel": "IP-cím",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "Múzeum",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Adja meg a célpontot, majd kapcsolja össze.",
+ "tcpStatus_connectingTo": "Kapcsolat a {endpoint}-hez...",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpErrorHostRequired": "Az IP-címet meg kell adni.",
+ "tcpErrorPortInvalid": "Az érték 1 és 65535 között kell lennie.",
+ "tcpErrorUnsupported": "A TCP-protokoll nem támogatott ez a platformon.",
+ "tcpErrorTimedOut": "A TCP-kapcsolat időtúllépett.",
+ "tcpConnectionFailed": "A TCP-kapcsolat sikertelen: {error}",
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbScreenTitle": "USB-en keresztül csatlakoztassuk",
+ "usbScreenSubtitle": "Válasszon egy azonosított soros eszközt, és közvetlenül csatlakoztassa a MeshCore-hoz.",
+ "usbScreenStatus": "Válasszon egy USB-es eszközt",
+ "usbScreenNote": "Az USB-es soros kommunikáció a támogatott Android eszközökön és asztali rendszereken is elérhető.",
+ "usbScreenEmptyState": "Nincs USB eszköz megtalálva. Csatlakoztasson egyet, majd frissítse a rendszert.",
+ "usbErrorPermissionDenied": "A USB-es hozzáférés megtagadva.",
+ "usbErrorDeviceMissing": "Az kiválasztott USB eszköz már nem elérhető.",
+ "usbErrorInvalidPort": "Válasszon egy érvényes USB-eszközt.",
+ "usbErrorBusy": "Egy másik USB-csatlakozás kérése már folyamatban van.",
+ "usbErrorNotConnected": "Nincs csatlakoztatott USB eszköz.",
+ "usbErrorOpenFailed": "Nem sikerült megnyitni a kiválasztott USB-eszközöt.",
+ "usbErrorConnectFailed": "Nem sikerült kapcsolatot létesíteni a kiválasztott USB-eszközhöz.",
+ "usbErrorUnsupported": "Ez a platform nem támogat USB-es soros kommunikációt.",
+ "usbErrorAlreadyActive": "Az USB-kapcsolat már be van állítva.",
+ "usbErrorNoDeviceSelected": "Nincs kiválasztva USB eszköz.",
+ "usbErrorPortClosed": "Az USB-kapcsolat nem aktív.",
+ "usbErrorConnectTimedOut": "Kapcsolódás sikertelen. Ellenőrizze, hogy a eszköz rendelkezik-e USB-hez tartozó firmware-rel.",
+ "usbFallbackDeviceName": "Web-szériás eszköz",
+ "usbStatus_notConnected": "Válasszon egy USB-es eszközt",
+ "usbStatus_connecting": "USB eszközhez való csatlakozás...",
+ "usbStatus_searching": "USB eszközök keresése...",
+ "usbConnectionFailed": "USB-kapcsolat sikertelen: {error}",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_scanning": "Készülékek keresése...",
+ "scanner_connecting": "Kapcsolódás...",
+ "scanner_disconnecting": "Kapcsolat megszakad...",
+ "scanner_notConnected": "Nem csatlakozva",
+ "scanner_connectedTo": "Kapcsolódik a {deviceName}-hez",
+ "@scanner_connectedTo": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_searchingDevices": "MeshCore eszközök keresése...",
+ "scanner_tapToScan": "A Tap Scan funkció segítségével kereshet MeshCore eszközöket.",
+ "scanner_connectionFailed": "Kapcsolódás sikertelen: {error}",
+ "@scanner_connectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_stop": "Megállj",
+ "scanner_scan": "Szkenálás",
+ "scanner_bluetoothOff": "A Bluetooth kikapcsolva",
+ "scanner_bluetoothOffMessage": "Kérjük, kapcsolja be a Bluetooth-ot, hogy eszközök keresése lehessen.",
+ "scanner_chromeRequired": "Chrome böngésző szükséges",
+ "scanner_chromeRequiredMessage": "Ez az alkalmazás a Bluetooth funkcióhoz Google Chrome-ot vagy Chromium alapú böngészőt igényel.",
+ "scanner_enableBluetooth": "Engedje be a Bluetooth funkciót",
+ "device_quickSwitch": "Gyors váltás",
+ "device_meshcore": "MeshCore",
+ "settings_title": "Beállítások",
+ "settings_deviceInfo": "A készülék információi",
+ "settings_appSettings": "Alkalmazási beállítások",
+ "settings_appSettingsSubtitle": "Értesítések, üzenetküldés és térképi beállítások",
+ "settings_nodeSettings": "Műközép beállítások",
+ "settings_nodeName": "Vonal neve",
+ "settings_nodeNameNotSet": "Nem megállapított",
+ "settings_nodeNameHint": "Adja meg a csomópont nevét",
+ "settings_nodeNameUpdated": "Neve frissítve",
+ "settings_radioSettings": "Rádióbeállítások",
+ "settings_radioSettingsSubtitle": "Frekvencia, teljesítmény, szélesítési tényező",
+ "settings_radioSettingsUpdated": "A rádió beállítások frissítve",
+ "settings_location": "Helyszín",
+ "settings_locationSubtitle": "GPS koordináták",
+ "settings_locationUpdated": "A helyzet és a GPS beállítások frissítve",
+ "settings_locationBothRequired": "Kérjük, adja meg a földrajzi szélességet és hosszúságot.",
+ "settings_locationInvalid": "Érvénytelen szélesszög vagy hosszszög.",
+ "settings_locationGPSEnable": "GPS engedélyezve",
+ "settings_locationGPSEnableSubtitle": "Lehetővé teszi, hogy a GPS automatikusan frissítse a helyzetet.",
+ "settings_locationIntervalSec": "GPS-számolási intervallum (másodpercek)",
+ "settings_locationIntervalInvalid": "Az intervallum legalább 60 másodpercnek, de legfeljebb 86400 másodpercnak kell lennie.",
+ "settings_latitude": "Nyugat-–––––––––––––––––––––––––––––––",
+ "settings_longitude": "hosszúság",
+ "settings_contactSettings": "Kapcsolat beállítások",
+ "settings_contactSettingsSubtitle": "Beállítások, amelyek meghatározzák, hogyan lehet új kapcsolatokat hozzáadni.",
+ "settings_privacyMode": "Adatvédelem mód",
+ "settings_privacyModeSubtitle": "Elrejtsük a nevét/a helyszínt az űrianyagokban",
+ "settings_privacyModeToggle": "Engedje be a privát üzemmódot, hogy elrejtse a nevét és a helyét az online hirdetésekben.",
+ "settings_privacyModeEnabled": "Adatvédelem mód beállítva",
+ "settings_privacyModeDisabled": "Adatvédelem mód kikapcsolva",
+ "settings_actions": "Tevékenységek",
+ "settings_sendAdvertisement": "Hirdetés küldése",
+ "settings_sendAdvertisementSubtitle": "A nyilvános megjelenés",
+ "settings_advertisementSent": "Hirdetés elküldve",
+ "settings_syncTime": "Szinkronizációs idő",
+ "settings_syncTimeSubtitle": "Állítsa a készülék időzítését a telefon időjére",
+ "settings_timeSynchronized": "Időben szinkronizált",
+ "settings_refreshContacts": "Újraindítsd a kapcsolatok listát",
+ "settings_refreshContactsSubtitle": "Újra töltse a kontaktlista-adatokat a készülékről",
+ "settings_rebootDevice": "Újraindítás",
+ "settings_rebootDeviceSubtitle": "Indítsa újra a MeshCore eszközt.",
+ "settings_rebootDeviceConfirm": "Biztosan szeretné újraindítani a készüléket? Ebben az esetben a kapcsolat megszűnik.",
+ "settings_debug": "Hibakeresés",
+ "settings_bleDebugLog": "BLE hibaelhárítási napló",
+ "settings_bleDebugLogSubtitle": "BLE parancsok, válaszok és alapvető adatok",
+ "settings_appDebugLog": "App-debug log",
+ "settings_appDebugLogSubtitle": "Programozási hibajelzések",
+ "settings_about": "Ról",
+ "settings_aboutVersion": "MeshCore Open {version} verzió",
+ "@settings_aboutVersion": {
+ "placeholders": {
+ "version": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_aboutLegalese": "2026-os MeshCore nyílt forráskódú projekt",
+ "settings_aboutDescription": "Egy nyílt forráskódú Flutter kliens a MeshCore LoRa hálózati eszközök számára.",
+ "settings_aboutOpenMeteoAttribution": "LOS magassági adatok: Open-Meteo (CC BY 4.0)",
+ "settings_infoName": "Név",
+ "settings_infoId": "Az azonosító",
+ "settings_infoStatus": "Állapot",
+ "settings_infoBattery": "Akku",
+ "settings_infoPublicKey": "Nyelvkönyv",
+ "settings_infoContactsCount": "Kapcsolatok száma",
+ "settings_infoChannelCount": "Csatorna száma",
+ "settings_presets": "Előre beállított beállítások",
+ "settings_frequency": "Frekvencia (MHz)",
+ "settings_frequencyHelper": "300,0 – 2500,0",
+ "settings_frequencyInvalid": "Érvénytelen frekvencia (300-2500 MHz)",
+ "settings_bandwidth": "Kapacitás",
+ "settings_spreadingFactor": "Terjesztési tényező",
+ "settings_codingRate": "Kódolási sebesség",
+ "settings_txPower": "TX teljesítmény (dBm)",
+ "settings_txPowerHelper": "0 – 22",
+ "settings_txPowerInvalid": "Érvénytelen TX teljesítmény (0-22 dBm)",
+ "settings_clientRepeat": "Autonóm rendszer újra",
+ "settings_clientRepeatSubtitle": "Engedje, hogy ez a eszköz mások számára is ismételje a hálózati csomagokat.",
+ "settings_clientRepeatFreqWarning": "A hálózat nélküli kommunikációhoz 433, 869 vagy 918 MHz frekvenciát igényel.",
+ "settings_error": "Hiba: {message}",
+ "@settings_error": {
+ "placeholders": {
+ "message": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_title": "Alkalmazási beállítások",
+ "appSettings_appearance": "Megjelenés",
+ "appSettings_theme": "Téma",
+ "appSettings_themeSystem": "Alapértékek",
+ "appSettings_themeLight": "Világítás",
+ "appSettings_themeDark": "Sötét",
+ "appSettings_language": "Nyelv",
+ "appSettings_languageSystem": "Alapértékek",
+ "appSettings_languageEn": "Angol",
+ "appSettings_languageFr": "Francia",
+ "appSettings_languageEs": "Spanyol",
+ "appSettings_languageDe": "Német",
+ "appSettings_languagePl": "Lengyel",
+ "appSettings_languageSl": "szlovén nyelv",
+ "appSettings_languagePt": "Portugál",
+ "appSettings_languageIt": "Olasz",
+ "appSettings_languageZh": "Kínai",
+ "appSettings_languageSv": "Svéd",
+ "appSettings_languageNl": "Hollandi",
+ "appSettings_languageSk": "Szlovén nyelvre fordítás",
+ "appSettings_languageBg": "Bulgár",
+ "appSettings_languageRu": "Orosz",
+ "appSettings_languageUk": "Украинский",
+ "appSettings_enableMessageTracing": "Engedje meg a üzenetek nyomon követését",
+ "appSettings_enableMessageTracingSubtitle": "Adja meg a üzenetek részletes útvonal- és időzítési adatokat.",
+ "appSettings_notifications": "Értesítések",
+ "appSettings_enableNotifications": "Engedélyezze az értesítéseket",
+ "appSettings_enableNotificationsSubtitle": "Kapjon értesítéseket üzenetekről és hirdetésekről.",
+ "appSettings_notificationPermissionDenied": "A értesítési engedély megtagadva",
+ "appSettings_notificationsEnabled": "A figyelmeztetések engedélyezve",
+ "appSettings_notificationsDisabled": "A figyelmeztetések kikapcsolva",
+ "appSettings_messageNotifications": "Üzenet értesítések",
+ "appSettings_messageNotificationsSubtitle": "A figyelmeztetést megjelenítve, amikor új üzenet érkezik",
+ "appSettings_channelMessageNotifications": "Csatorna-üzenetek értesítése",
+ "appSettings_channelMessageNotificationsSubtitle": "A figyelmeztetést megjelenítve, amikor új üzenet érkezik a csatornáról",
+ "appSettings_advertisementNotifications": "Reklám értesítések",
+ "appSettings_advertisementNotificationsSubtitle": "A figyelmeztetést megjelenítve, amikor új csomópontok kerülnek felfedezésre.",
+ "appSettings_messaging": "Üzenetek küldése",
+ "appSettings_clearPathOnMaxRetry": "Egyértelmű út a Max Retry funkció használatával",
+ "appSettings_clearPathOnMaxRetrySubtitle": "A kapcsolat visszaállítás 5 sikertelen továbbítás után",
+ "appSettings_pathsWillBeCleared": "Ha 5-szer sikertelenül próbálunk, a útvonalat automatikusan tisztítjuk.",
+ "appSettings_pathsWillNotBeCleared": "A utak automatikusan nem tisztítódnak.",
+ "appSettings_autoRouteRotation": "Autóútok forgása",
+ "appSettings_autoRouteRotationSubtitle": "Válasszon a legjobb útvonalak között, vagy válassza a vízözön-módot.",
+ "appSettings_autoRouteRotationEnabled": "Az automatikus útvonalváltás engedélyezve",
+ "appSettings_autoRouteRotationDisabled": "Az automatikus útvonal-választás funkció kikapcsolva.",
+ "appSettings_maxRouteWeight": "Maximális útvonal súly",
+ "appSettings_maxRouteWeightSubtitle": "A lehető legnagyobb súly, amit egy útvonal sikeres szállítmányok során összegyűjthet.",
+ "appSettings_initialRouteWeight": "A kezdeti útvonal súlya",
+ "appSettings_initialRouteWeightSubtitle": "Az új, felfedezett útvonalakhoz tartozó kezdeti súly",
+ "appSettings_routeWeightSuccessIncrement": "Sikerhez vezető növelés",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "A sikeresen teljesített útvonalhoz hozzáadott súly.",
+ "appSettings_routeWeightFailureDecrement": "Hibás súly csökkenése",
+ "appSettings_routeWeightFailureDecrementSubtitle": "A jártatásból eltávolított súly, ami a sikertelen szállítás következménye.",
+ "appSettings_maxMessageRetries": "Maximális üzenetek újraküldési próbálkozások",
+ "appSettings_maxMessageRetriesSubtitle": "A próbálkozások száma, mielőtt egy üzenetet hibásnak jelölünk.",
+ "path_routeWeight": "{weight}/{max}",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_battery": "Akku",
+ "appSettings_batteryChemistry": "Aakkum töltés kémia",
+ "appSettings_batteryChemistryPerDevice": "Beállítások {deviceName}-hez",
+ "@appSettings_batteryChemistryPerDevice": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_batteryChemistryConnectFirst": "Csatlakozzon egy eszközhez, hogy kiválassza",
+ "appSettings_batteryNmc": "18650 NMC (3,0-4,2 V)",
+ "appSettings_batteryLifepo4": "LiFePO4 (2,6–3,65 V)",
+ "appSettings_batteryLipo": "LiPo (3,0-4,2 V)",
+ "appSettings_mapDisplay": "Térkép megjelenítése",
+ "appSettings_showRepeaters": "Megismétlés",
+ "appSettings_showRepeatersSubtitle": "A térképen megjelenítsük a repeater-eket.",
+ "appSettings_showChatNodes": "Megjeleníts kommunikációs pontokat",
+ "appSettings_showChatNodesSubtitle": "A chat-szobákat megjelenítsük a térképen",
+ "appSettings_showOtherNodes": "Mutasson további csomópontokat",
+ "appSettings_showOtherNodesSubtitle": "Mutassa meg a többi hálózati elemet a térképen",
+ "appSettings_timeFilter": "Időbeli szűrés",
+ "appSettings_timeFilterShowAll": "Mutassa meg az összes csomópontot",
+ "appSettings_timeFilterShowLast": "Mutasson az utolsó {hours} órából származó adatokat.",
+ "@appSettings_timeFilterShowLast": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_mapTimeFilter": "Térkép időszűrő",
+ "appSettings_showNodesDiscoveredWithin": "Megjeleníts olyan węzveket, amelyek a következő területen lettek felfedezve:",
+ "appSettings_allTime": "Minden időpont",
+ "appSettings_lastHour": "Az utolsó óra",
+ "appSettings_last6Hours": "Az utóban 6 óra",
+ "appSettings_last24Hours": "Az utóbbi 24 óra",
+ "appSettings_lastWeek": "A múlt héten",
+ "appSettings_offlineMapCache": "Offline térkép tárolás",
+ "appSettings_unitsTitle": "Egységek",
+ "appSettings_unitsMetric": "Méter (m / kilométer)",
+ "appSettings_unitsImperial": "Királyi (láb / mérföld)",
+ "appSettings_noAreaSelected": "Nincs kiválasztott terület.",
+ "appSettings_areaSelectedZoom": "Kiválasztott terület (zoom: {minZoom}-{maxZoom})",
+ "@appSettings_areaSelectedZoom": {
+ "placeholders": {
+ "minZoom": {
+ "type": "int"
+ },
+ "maxZoom": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_debugCard": "Hibakeresés",
+ "appSettings_appDebugLogging": "App-ban történő hibakereséshez használt naplózás",
+ "appSettings_appDebugLoggingSubtitle": "Log alkalmazás hibaelhárítási üzenetek",
+ "appSettings_appDebugLoggingEnabled": "Az alkalmazás hibaelhárítási naplózás engedélyezve",
+ "appSettings_appDebugLoggingDisabled": "Az alkalmazás hibaelhárítási naplózatának bekapcsolása kiküszöbölve",
+ "contacts_title": "Kapcsolatok",
+ "contacts_noContacts": "Jelenleg még nincs kapcsolat.",
+ "contacts_contactsWillAppear": "A kapcsolatok megjelennek, amikor a eszközök hirdetnek.",
+ "contacts_unread": "Olvasatlan",
+ "contacts_searchContactsNoNumber": "Kapcsolatok keresése...",
+ "contacts_searchContacts": "Keresés {number}-ban {str}…",
+ "@contacts_searchContacts": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchFavorites": "Keresés {number}{str}... Kedvencek",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "Search {number}{str} Users...",
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "Keresés {number}-on, {str} típusú adóállomások között...",
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "Keresés {number}-ban {str}...",
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_noUnreadContacts": "Nincs olvasatlan üzenetek",
+ "contacts_noContactsFound": "Nincs megtalálva semmilyen kapcsolat vagy csoport.",
+ "contacts_deleteContact": "Kapcsolattól töröl",
+ "contacts_removeConfirm": "Hogy töröljem a {contactName} nevű személyt a kontaktlistából?",
+ "@contacts_removeConfirm": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_manageRepeater": "Ellenőriző eszköz kezelése",
+ "contacts_manageRoom": "A szobai szerver kezelése",
+ "contacts_roomLogin": "Szoba szerverbe való bejelentkezés",
+ "contacts_openChat": "Nyitott beszélgetés",
+ "contacts_editGroup": "Edit csoport",
+ "contacts_deleteGroup": "Csoport törlése",
+ "contacts_deleteGroupConfirm": "Hogy töröljem a \"{groupName}\"-t?",
+ "@contacts_deleteGroupConfirm": {
+ "placeholders": {
+ "groupName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_newGroup": "Új csoport",
+ "contacts_groupName": "Csoport neve",
+ "contacts_groupNameRequired": "A csoportnak meg kell adni a nevét.",
+ "contacts_groupNameReserved": "Ez a csoportnév foglalt",
+ "contacts_groupAlreadyExists": "A \"{name}\" nevű csoport már létezik.",
+ "@contacts_groupAlreadyExists": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_filterContacts": "Szűrj kontaktokat...",
+ "contacts_noContactsMatchFilter": "Nincs találat a megadott szűrés alapján.",
+ "contacts_noMembers": "Nincsenek tagok",
+ "contacts_lastSeenNow": "utóbbi időben",
+ "contacts_lastSeenMinsAgo": "~ {minutes} perc",
+ "@contacts_lastSeenMinsAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenHourAgo": "Kb. 1 óra",
+ "contacts_lastSeenHoursAgo": "~ {hours} óra",
+ "@contacts_lastSeenHoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenDayAgo": "Kb. 1 nap",
+ "contacts_lastSeenDaysAgo": "~ {days} days",
+ "@contacts_lastSeenDaysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_title": "Csatornák",
+ "channels_noChannelsConfigured": "Nincs konfigurált csatorna.",
+ "channels_addPublicChannel": "Hozzon létre nyilvános csatornát",
+ "channels_searchChannels": "Keresési opciók...",
+ "channels_noChannelsFound": "Nincs megtalálható csatorna",
+ "channels_channelIndex": "{index}-os csatorna",
+ "@channels_channelIndex": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_hashtagChannel": "Hashtag-ok közössége",
+ "channels_public": "A nyilvánosság számára",
+ "channels_private": "Személyes",
+ "channels_publicChannel": "Össztávos csatorna",
+ "channels_privateChannel": "Személyes csatorna",
+ "channels_editChannel": "Csatorna szerkesztése",
+ "channels_muteChannel": "Csendes csatorna",
+ "channels_unmuteChannel": "Engedje be a hangot",
+ "channels_deleteChannel": "Mozdony törlése",
+ "channels_deleteChannelConfirm": "Törlés {name}? Ez nem visszafordítható.",
+ "@channels_deleteChannelConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleteFailed": "Nem sikerült törölni a \"{name}\" nevű csatornát.",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleted": "A \"{name}\" nevű csatorna törölve",
+ "@channels_channelDeleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_addChannel": "Csatorna hozzáadása",
+ "channels_channelIndexLabel": "Csatorna index",
+ "channels_channelName": "Csatorna neve",
+ "channels_usePublicChannel": "Használja a nyilvános csatornát",
+ "channels_standardPublicPsk": "Általános, állami által finanszírozott PSK",
+ "channels_pskHex": "PSK (Hexadecimális kód)",
+ "channels_generateRandomPsk": "Véletlenszerűen generáljon PSK-t",
+ "channels_enterChannelName": "Kérjük, adja meg egy csatorna nevét",
+ "channels_pskMustBe32Hex": "A PSK 32-bázisú hexadecimális karakterből áll.",
+ "channels_channelAdded": "A \"{name}\" csatorna hozzáadva",
+ "@channels_channelAdded": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_editChannelTitle": "Módosítsd a csatornát {index}",
+ "@channels_editChannelTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_smazCompression": "SMAZ kompresszió",
+ "channels_channelUpdated": "A {name} csatorna frissítve",
+ "@channels_channelUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_publicChannelAdded": "A nyilvános csatorna hozzáadva",
+ "channels_sortBy": "Szűrés",
+ "channels_sortManual": "Használati útmutató",
+ "channels_sortAZ": "A-Z",
+ "channels_sortLatestMessages": "Legfrissebb üzenetek",
+ "channels_sortUnread": "Olvasatlan",
+ "channels_createPrivateChannel": "Létrehoz egy privát csatornát",
+ "channels_createPrivateChannelDesc": "Titkos kulcs segítségével védelem.",
+ "channels_joinPrivateChannel": "Csatlakozzon egy privát csatornához",
+ "channels_joinPrivateChannelDesc": "Kézzel adja meg a titkos kulcsot.",
+ "channels_joinPublicChannel": "Csatlakozzon a nyilvános csatornához",
+ "channels_joinPublicChannelDesc": "Bárki csatlakozhat ehhez a csatornához.",
+ "channels_joinHashtagChannel": "Csatlakozzon egy hashtage-os csatornához",
+ "channels_joinHashtagChannelDesc": "Bárkinek lehet csatlakoznia a hashtagekhez tartozó csatornához.",
+ "channels_scanQrCode": "Scanned egy QR-kódot",
+ "channels_scanQrCodeComingSoon": "Hamarosan",
+ "channels_enterHashtag": "Írja be a hashtaget",
+ "channels_hashtagHint": "pl. #csapat",
+ "chat_noMessages": "Még nincs üzenet.",
+ "chat_sendMessageToStart": "Küldj egy üzenetet, hogy elindulj!",
+ "chat_originalMessageNotFound": "A eredeti üzenet nem található.",
+ "chat_replyingTo": "Replying to {name}",
+ "@chat_replyingTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_replyTo": "Reply to {name}",
+ "@chat_replyTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_location": "Helyszín",
+ "chat_sendMessageTo": "Küldj üzenetet {contactName}-nek",
+ "@chat_sendMessageTo": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_typeMessage": "Írjon üzenetet...",
+ "chat_messageTooLong": "A üzenet túl hosszú (a maximális {maxBytes} bájt).",
+ "@chat_messageTooLong": {
+ "placeholders": {
+ "maxBytes": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_messageCopied": "Üzenet másolva",
+ "chat_messageDeleted": "Üzenet törölve",
+ "chat_retryingMessage": "Újrapróbálási üzenet",
+ "chat_retryCount": "Újrapróbál {current}/{max}",
+ "@chat_retryCount": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendGif": "Küldj GIF-ot",
+ "chat_reply": "Válasz",
+ "chat_addReaction": "Hozzon létre reakciót",
+ "chat_me": "Én",
+ "emojiCategorySmileys": "Emoji",
+ "emojiCategoryGestures": "Testmozgások",
+ "emojiCategoryHearts": "Szívak",
+ "emojiCategoryObjects": "Tárgyak",
+ "gifPicker_title": "Válasszon egy GIF-et",
+ "gifPicker_searchHint": "GIF-ek keresése...",
+ "gifPicker_poweredBy": "Forrás: GIPHY",
+ "gifPicker_noGifsFound": "Nincsenek GIF-ek megtalálva.",
+ "gifPicker_failedLoad": "Nem sikerült betölteni a GIF-fájlokat.",
+ "gifPicker_failedSearch": "Nem sikerült a GIF-eket megtalálni.",
+ "gifPicker_noInternet": "Nincs internetkapcsolat.",
+ "debugLog_appTitle": "App-debug log",
+ "debugLog_bleTitle": "BLE hibajelentő napló",
+ "debugLog_copyLog": "Másolat napló",
+ "debugLog_clearLog": "Jelzett napló",
+ "debugLog_copied": "Hibajelentő napló másolva",
+ "debugLog_bleCopied": "BLE-log másolva",
+ "debugLog_noEntries": "Jelenleg még nem léteznek hibaelhárítási naplókat.",
+ "debugLog_enableInSettings": "Engedje be az alkalmazás hibaelhárítási naplózását a beállítások menüben.",
+ "debugLog_frames": "Keretek",
+ "debugLog_rawLogRx": "Az eredeti Log-RX",
+ "debugLog_noBleActivity": "Jelenleg nincs BLE-hez kapcsolódó tevékenység.",
+ "debugFrame_length": "Keret hossza: {count} bájt",
+ "@debugFrame_length": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_command": "Parancs: 0x{value}",
+ "@debugFrame_command": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textMessageHeader": "Címzett:",
+ "debugFrame_destinationPubKey": "- Célhely: {pubKey}",
+ "@debugFrame_destinationPubKey": {
+ "placeholders": {
+ "pubKey": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_timestamp": "- Időbélyeg: {timestamp}",
+ "@debugFrame_timestamp": {
+ "placeholders": {
+ "timestamp": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_flags": "- Jelvények: 0x{value}",
+ "@debugFrame_flags": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textType": "- Tartalom típusa: {type} ({label})",
+ "@debugFrame_textType": {
+ "placeholders": {
+ "type": {
+ "type": "int"
+ },
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textTypeCli": "Parancssori felület (CLI)",
+ "debugFrame_textTypePlain": "Egyszerű, alap, hagyományos",
+ "debugFrame_text": "- Tartalom: \"{text}\"",
+ "@debugFrame_text": {
+ "placeholders": {
+ "text": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_hexDump": "Hex-dump:",
+ "chat_pathManagement": "Útvonal-kezelés",
+ "chat_ShowAllPaths": "Mutasson meg minden útvonalat",
+ "chat_routingMode": "Útvonal-kezelési mód",
+ "chat_autoUseSavedPath": "Automatikus (az eddigi útvonal használata)",
+ "chat_forceFloodMode": "Erőforrás-alapú áramlás mód",
+ "chat_recentAckPaths": "Legutóbbi használt útvonalak (gombra kattintva):",
+ "chat_pathHistoryFull": "Az előző lépések listája teljes. Törölj ki a bejegyzéseket, hogy újokat hozzáadhatsd.",
+ "chat_hopSingular": "ugor",
+ "chat_hopPlural": "babér",
+ "chat_hopsCount": "{count} {count, plural, =1{ugrás} other{ugrások}}",
+ "@chat_hopsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_successes": "sikerek",
+ "chat_removePath": "Törölje a elérési útvonalat",
+ "chat_noPathHistoryYet": "Még nincs útvonal-történet.\nKüldjön egy üzenetet, hogy megtudja a lehetséges útvonalakat.",
+ "chat_pathActions": "Céltúrások:",
+ "chat_setCustomPath": "Beállítsd a saját útvonalat",
+ "chat_setCustomPathSubtitle": "Kézzel megadott útvonal",
+ "chat_clearPath": "Egyértelmű út",
+ "chat_clearPathSubtitle": "A parancs új küldéskor újra kell aktivizálnia.",
+ "chat_pathCleared": "Útvonal cleared. A következő üzenet újból feltérképezheti az útvonalat.",
+ "chat_floodModeSubtitle": "Használja a \"útvonal\" kapcsolót az alkalmazás sávjában.",
+ "chat_floodModeEnabled": "Árvízvédelmi mód bekapcsolva. A visszaállítás a alkalmazásban található útvonal ikon segítségével.",
+ "chat_fullPath": "Teljes elérési út",
+ "chat_pathDetailsNotAvailable": "Az útvonal részletei még nem elérhetők. Próbálja meg küldeni egy üzenetet, hogy frissítse az információkat.",
+ "chat_pathSetHops": "Path set: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
+ "@chat_pathSetHops": {
+ "placeholders": {
+ "hopCount": {
+ "type": "int"
+ },
+ "status": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_pathSavedLocally": "Helyileg mentve. Kapcsolódjon a szinkronizáláshoz.",
+ "chat_pathDeviceConfirmed": "A készülék megvan.",
+ "chat_pathDeviceNotConfirmed": "A készülék még nem bizonyított.",
+ "chat_type": "Típus",
+ "chat_path": "Út",
+ "chat_publicKey": "Nyelvkönyv",
+ "chat_compressOutgoingMessages": "A küldött üzenetek tömörítése",
+ "chat_floodForced": "Áradás (kényszerített)",
+ "chat_directForced": "Közvetlen (erélyes)",
+ "chat_hopsForced": "{count} ánusz (erővel)",
+ "@chat_hopsForced": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_floodAuto": "Vízosztás (autó)",
+ "chat_direct": "Közvetlen",
+ "chat_poiShared": "Közös erőforrás",
+ "chat_unread": "Olvasatlan: {count}",
+ "@chat_unread": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_openLink": "Nyisd meg a linket?",
+ "chat_openLinkConfirmation": "Szeretnéd megnyitni ezt a linket a böngésződben?",
+ "chat_open": "Nyitott",
+ "chat_couldNotOpenLink": "Nem sikerült megnyitni a hivat: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Érvénytelen hivatkozás formátum",
+ "map_title": "Grafikus ábrázás",
+ "map_lineOfSight": "Látási vonal",
+ "map_losScreenTitle": "Látási vonal",
+ "map_noNodesWithLocation": "Nincs olyan adatpont, amelyhez helyszín-információk tartoznak.",
+ "map_nodesNeedGps": "A pontoknak meg kell osztaniuk GPS koordinátáikat, hogy megjelenjenek a térképen.",
+ "map_nodesCount": "Csúcsok: {count}",
+ "@map_nodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_pinsCount": "Csapok: {count}",
+ "@map_pinsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_chat": "Csevegés",
+ "map_repeater": "Ismétlő",
+ "map_room": "szoba",
+ "map_sensor": "Érzékelő",
+ "map_pinDm": "Jel (DM)",
+ "map_pinPrivate": "Titkos (privát)",
+ "map_pinPublic": "Jelmez (nyilvános)",
+ "map_lastSeen": "Utoljára látva",
+ "map_disconnectConfirm": "Biztosan szeretné kiírni ezt a készüléket?",
+ "map_from": "Attól",
+ "map_source": "Forrás",
+ "map_flags": "Zászló",
+ "map_shareMarkerHere": "Osztja ezt a tartalmat itt",
+ "map_setAsMyLocation": "Állítsa be a jelenlegi helyzetemként",
+ "map_pinLabel": "Címkét ragasztani",
+ "map_label": "Címke",
+ "map_pointOfInterest": "Érdekes hely",
+ "map_sendToContact": "Kapcsolatfelvételi űrlap",
+ "map_sendToChannel": "Küldés a csatornán",
+ "map_noChannelsAvailable": "Nincs elérhető csatorna.",
+ "map_publicLocationShare": "Térköz, nyilvános hely",
+ "map_publicLocationShareConfirm": "Most egy helyszínt megosztasz a {channelLabel} csatornán. Ez a csatorna nyilvános, és bárki, aki rendelkezik a PSK-val, megtekintheti.",
+ "@map_publicLocationShareConfirm": {
+ "placeholders": {
+ "channelLabel": {
+ "type": "String"
+ }
+ }
+ },
+ "map_connectToShareMarkers": "Kapcsolódjon egy eszközhöz, hogy megoszthassa a vonalzókat.",
+ "map_filterNodes": "Szűrési pontok",
+ "map_nodeTypes": "Vonalak típusai",
+ "map_chatNodes": "Csevegési pontok",
+ "map_repeaters": "Újraküldők",
+ "map_otherNodes": "Egyéb csomópontok",
+ "map_keyPrefix": "Kulcsfontosságú előtag",
+ "map_filterByKeyPrefix": "Szűrj a kulcsos előtér szerint",
+ "map_publicKeyPrefix": "Névfelhasználó kulc-prefix",
+ "map_markers": "Jelölők",
+ "map_showSharedMarkers": "Mutassa meg a közös jeleket",
+ "map_showGuessedLocations": "Megjelenítsa a megjósolt csomópontok helyét",
+ "map_showDiscoveryContacts": "Megjelenítse a Discovery-nál elérhet kontaktokat",
+ "map_guessedLocation": "Tippolt hely",
+ "map_lastSeenTime": "Utoljára megjelent idő",
+ "map_sharedPin": "Gemeinsames PIN-kód",
+ "map_joinRoom": "Csatlakozás a szobához",
+ "map_manageRepeater": "Ellenőriző eszköz kezelése",
+ "map_tapToAdd": "Nyomj meg a csomópontokhoz, hogy hozzáadd őket az útvonalhoz.",
+ "map_runTrace": "Útvonal követés",
+ "map_removeLast": "Törölj utolsó",
+ "map_pathTraceCancelled": "Az útvonal követés megszakadt.",
+ "mapCache_title": "Offline térkép tárolás",
+ "mapCache_selectAreaFirst": "Válasszon egy területet, amelyet először cache-oljon.",
+ "mapCache_noTilesToDownload": "Nincsenek letölthető tile-ok ebben a területben.",
+ "mapCache_downloadTilesTitle": "Letöltsd a tile-okat",
+ "mapCache_downloadTilesPrompt": "Töltse le {count} darab tile-t offline használatra?",
+ "@mapCache_downloadTilesPrompt": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadAction": "Letöltés",
+ "mapCache_cachedTiles": "Tárolt {count} darab",
+ "@mapCache_cachedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_cachedTilesWithFailed": "Cached {downloaded} tiles ({failed} failed)",
+ "@mapCache_cachedTilesWithFailed": {
+ "placeholders": {
+ "downloaded": {
+ "type": "int"
+ },
+ "failed": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_clearOfflineCacheTitle": "Tiszta offline tárhely",
+ "mapCache_clearOfflineCachePrompt": "Távolítsa el az összes tárolt térképmegjelenítőt?",
+ "mapCache_offlineCacheCleared": "A helyi memóriát töröltük.",
+ "mapCache_noAreaSelected": "Nincs kiválasztott terület.",
+ "mapCache_cacheArea": "Tároló terület",
+ "mapCache_useCurrentView": "Használja a jelenlegi nézetet",
+ "mapCache_zoomRange": "Zoom tartomány",
+ "mapCache_estimatedTiles": "Becsült kerámiák: {count}",
+ "@mapCache_estimatedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadedTiles": "Letöltve {completed} / {total}",
+ "@mapCache_downloadedTiles": {
+ "placeholders": {
+ "completed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadTilesButton": "Letöltsd a tile-okat",
+ "mapCache_clearCacheButton": "Ósztótt adatokat",
+ "mapCache_failedDownloads": "Sikertelen letöltések: {count}",
+ "@mapCache_failedDownloads": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
+ "@mapCache_boundsLabel": {
+ "placeholders": {
+ "north": {
+ "type": "String"
+ },
+ "south": {
+ "type": "String"
+ },
+ "east": {
+ "type": "String"
+ },
+ "west": {
+ "type": "String"
+ }
+ }
+ },
+ "time_justNow": "Most",
+ "time_minutesAgo": "{minutes} perckel ezelőtt",
+ "@time_minutesAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hoursAgo": "{hours} óva",
+ "@time_hoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "time_daysAgo": "{days}d ago",
+ "@time_daysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hour": "óra",
+ "time_hours": "órák",
+ "time_day": "nap",
+ "time_days": "napok",
+ "time_week": "het",
+ "time_weeks": "het, hetek",
+ "time_month": "hónap",
+ "time_months": "hónapok",
+ "time_minutes": "percek",
+ "time_allTime": "Bármely időpont",
+ "dialog_disconnect": "Csatlakozást megszakasztani",
+ "dialog_disconnectConfirm": "Biztosan szeretné kiírni ezt a készüléket?",
+ "login_repeaterLogin": "Ismételt bejelentkezés",
+ "login_roomLogin": "Szoba szerverbe való bejelentkezés",
+ "login_password": "Jelszó",
+ "login_enterPassword": "Adja meg a jelszót",
+ "login_savePassword": "Mentse el a jelszót",
+ "login_savePasswordSubtitle": "A jelszó biztonságosan tárolódik ezen a készüléken.",
+ "login_repeaterDescription": "Adja meg a repeater (ismétítő) jelszót, hogy hozzáférhessen a beállításokhoz és az állapot információkhoz.",
+ "login_roomDescription": "Adja meg a belépési kódot, hogy hozzáférhessen a beállításokhoz és az állapot információkhoz.",
+ "login_routing": "Útvonal meghatározás",
+ "login_routingMode": "Útvonal-kezelési mód",
+ "login_autoUseSavedPath": "Automatikus (az eddigi útvonal használata)",
+ "login_forceFloodMode": "Erőforrás-alapú áramlás mód",
+ "login_managePaths": "Útvonalak kezelése",
+ "login_login": "Bejelentkezés",
+ "login_attempt": "Megpróbálás {current}/{max}-adik",
+ "@login_attempt": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "login_failed": "Belépés sikertelen: {error}",
+ "@login_failed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "login_failedMessage": "Belépés sikertelen. Vagy a jelszó helytelen, vagy a hálózati kapcsolat nem létesül.",
+ "common_reload": "Újra töltés",
+ "common_clear": "Egyértelmű",
+ "path_currentPath": "Jelenlegi útvonal: {path}",
+ "@path_currentPath": {
+ "placeholders": {
+ "path": {
+ "type": "String"
+ }
+ }
+ },
+ "path_usingHopsPath": "{count} {count, plural, =1{ugrás} other{ugrások}} útvonal használata",
+ "@path_usingHopsPath": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "path_enterCustomPath": "Adja meg a saját elérési útvonalat",
+ "path_currentPathLabel": "Jelenlegi útvonal",
+ "path_hexPrefixInstructions": "Adja meg a 2 karakteres hexadecimális előtagokat minden lépéshez, tagolva kommával.",
+ "path_hexPrefixExample": "Példa: A1, F2, 3C (minden csomó az első részét használja a nyilvános kulcsából)",
+ "path_labelHexPrefixes": "Út (hex-prefixek)",
+ "path_helperMaxHops": "A maximális hossz 64 karakter. Minden előző rész 2 hatos számjegyből áll (1 bájt).",
+ "path_selectFromContacts": "Válasszon a kontaktlista elembek közül:",
+ "path_noRepeatersFound": "Nincs megtalálva semmilyen ismétlődő vagy helyiség-szolgáltató szervert.",
+ "path_customPathsRequire": "Az egyedi útvonalaknak szükségük van átjáró pontokra, amelyek képesek üzeneteket továbbítani.",
+ "path_invalidHexPrefixes": "Érvénytelen hexadecimális előtagok: {prefixes}",
+ "@path_invalidHexPrefixes": {
+ "placeholders": {
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "path_tooLong": "Az út túl hosszú. A maximális engedélyezett lépések száma 64.",
+ "path_setPath": "Útvonal meghatározása",
+ "repeater_management": "Adatkapcsolás kezelése",
+ "room_management": "Szoba-szerver kezelés",
+ "repeater_managementTools": "Menedzsmentes eszközök",
+ "repeater_status": "Állapot",
+ "repeater_statusSubtitle": "Megtekintheted a repeater állapotát, statisztikáit és a környező eszközök adatait.",
+ "repeater_telemetry": "Adatvisszaadás",
+ "repeater_telemetrySubtitle": "Tekintsük a szenzorok és a rendszer állapotának adatát",
+ "repeater_cli": "Parancssori felület (CLI)",
+ "repeater_cliSubtitle": "Küldj parancsokat a repeaternek.",
+ "repeater_neighbors": "Szomszédok",
+ "repeater_neighborsSubtitle": "Tekintsük a nullás lépésű szomszédokat.",
+ "repeater_settings": "Beállítások",
+ "repeater_settingsSubtitle": "Állítsa be a repeater paramétereket",
+ "repeater_statusTitle": "Adatkapcsolódás állapot",
+ "repeater_routingMode": "Útvonal-kezelési mód",
+ "repeater_autoUseSavedPath": "Automatikus (az eddigi útvonal használata)",
+ "repeater_forceFloodMode": "Erőforrás-alapú áramlás mód",
+ "repeater_pathManagement": "Útvonal-kezelés",
+ "repeater_refresh": "Újrafriszol",
+ "repeater_statusRequestTimeout": "Az állapotkérés időtúlt.",
+ "repeater_errorLoadingStatus": "Hiba a státusz betöltés közben: {error}",
+ "@repeater_errorLoadingStatus": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_systemInformation": "Rendszerinformációk",
+ "repeater_battery": "Akku",
+ "repeater_clockAtLogin": "Óra (bejelentkezéskor)",
+ "repeater_uptime": "A rendszer elérhetősége",
+ "repeater_queueLength": "Várakozási sor hossza",
+ "repeater_debugFlags": "Hibakeresési beállítások",
+ "repeater_radioStatistics": "Rádió statisztika",
+ "repeater_lastRssi": "Utolsó RSSI érték",
+ "repeater_lastSnr": "Utolsó SNR",
+ "repeater_noiseFloor": "Háttérzaj szint",
+ "repeater_txAirtime": "TX Airtime",
+ "repeater_rxAirtime": "RX Airtime",
+ "repeater_packetStatistics": "Csomagok statisztikája",
+ "repeater_sent": "Elküldve",
+ "repeater_received": "Megérkezett",
+ "repeater_duplicates": "Duplák",
+ "repeater_daysHoursMinsSecs": "{days} days {hours}h {minutes}m {seconds}s",
+ "@repeater_daysHoursMinsSecs": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_packetTxTotal": "Összesen: {total}, Árvíz: {flood}, Közvetlen: {direct}",
+ "@repeater_packetTxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_packetRxTotal": "Összesen: {total}, Árvíz: {flood}, Közvetlen: {direct}",
+ "@repeater_packetRxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesFloodDirect": "Áradás: {flood}, Közvetlen: {direct}",
+ "@repeater_duplicatesFloodDirect": {
+ "placeholders": {
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesTotal": "Összesen: {total}",
+ "@repeater_duplicatesTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_settingsTitle": "Adatátvisszaadási beállítások",
+ "repeater_basicSettings": "Alapbeállítások",
+ "repeater_repeaterName": "Adóállomás neve",
+ "repeater_repeaterNameHelper": "Ez a repeater neve",
+ "repeater_adminPassword": "Adminisztrátori jelszó",
+ "repeater_adminPasswordHelper": "Teljes jogosultságú jelszó",
+ "repeater_guestPassword": "Vendég felhasználói név/jelszó",
+ "repeater_guestPasswordHelper": "Csak olvasási jogosítást biztosító jelszó",
+ "repeater_radioSettings": "Rádióbeállítások",
+ "repeater_frequencyMhz": "Frekvencia (MHz)",
+ "repeater_frequencyHelper": "300–2500 MHz",
+ "repeater_txPower": "TX Power",
+ "repeater_txPowerHelper": "1-30 dBm",
+ "repeater_bandwidth": "Adatkapacitás",
+ "repeater_spreadingFactor": "Terjesztési tényező",
+ "repeater_codingRate": "Kódolási sebesség",
+ "repeater_locationSettings": "Helyszínbeállítások",
+ "repeater_latitude": "Nyugat–keleti szélesség",
+ "repeater_latitudeHelper": "Desztes fokok (pl. 37,7749)",
+ "repeater_longitude": "hosszúság",
+ "repeater_longitudeHelper": "Desztes fokok (pl. -122.4194)",
+ "repeater_features": "Jellemzők",
+ "repeater_packetForwarding": "Csomagok továbbítás",
+ "repeater_packetForwardingSubtitle": "Engedje, hogy a repeater továbbítsa a csomagokat.",
+ "repeater_guestAccess": "Vendégek számára elérhető",
+ "repeater_guestAccessSubtitle": "Engedje meg a vendégek számára, hogy csak olvassák a tartalmat",
+ "repeater_privacyMode": "Adatvédelem mód",
+ "repeater_privacyModeSubtitle": "Elrejtse a nevét/a helyszínt az űrlapon",
+ "repeater_advertisementSettings": "Reklámbeállítások",
+ "repeater_localAdvertInterval": "Helyi hirdetés időtartama",
+ "repeater_localAdvertIntervalMinutes": "{minutes} perc",
+ "@repeater_localAdvertIntervalMinutes": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_floodAdvertInterval": "Vízosztály-hirdetés időtartama",
+ "repeater_floodAdvertIntervalHours": "{hours} óra",
+ "@repeater_floodAdvertIntervalHours": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_encryptedAdvertInterval": "Kódolt hirdetés-szünet",
+ "repeater_dangerZone": "Veszélyzóna",
+ "repeater_rebootRepeater": "Újraindítás",
+ "repeater_rebootRepeaterSubtitle": "Indítsa újra a repeater-t.",
+ "repeater_rebootRepeaterConfirm": "Biztosan szeretné újraindítani ezt a repeatert?",
+ "repeater_regenerateIdentityKey": "Újra generálja az azonosító kulcsot",
+ "repeater_regenerateIdentityKeySubtitle": "Új nyilvános/személyes kulcs-párt generáljon",
+ "repeater_regenerateIdentityKeyConfirm": "Ez új azonosítást fog létrehozni a repeater számára. Folytatni?",
+ "repeater_eraseFileSystem": "Törölje a fájlrendszert",
+ "repeater_eraseFileSystemSubtitle": "Formázza a duplázó fájlrendszert.",
+ "repeater_eraseFileSystemConfirm": "FIGYELEM: Ez törli az összes adatot a repeater-en. Ez nem visszafordítható!",
+ "repeater_eraseSerialOnly": "Az Erase funkció csak a soros konzolon érhető el.",
+ "repeater_commandSent": "Parancs elküldve: {command}",
+ "@repeater_commandSent": {
+ "placeholders": {
+ "command": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorSendingCommand": "Hibás parancs küldés: {error}",
+ "@repeater_errorSendingCommand": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_confirm": "Beküldve",
+ "repeater_settingsSaved": "Beállítások sikeresen mentve",
+ "repeater_errorSavingSettings": "Hibás beállítások mentése: {error}",
+ "@repeater_errorSavingSettings": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_refreshBasicSettings": "Visszaállítás az alapértékekre",
+ "repeater_refreshRadioSettings": "Frissítse a rádió beállításait",
+ "repeater_refreshTxPower": "Újraindítás TX-támogatással",
+ "repeater_refreshLocationSettings": "Újraindítás helyszín beállításokkal",
+ "repeater_refreshPacketForwarding": "Csomagok továbbításának frissítése",
+ "repeater_refreshGuestAccess": "Újraindítás vendégHozzáférés",
+ "repeater_refreshPrivacyMode": "Visszaállítás a magánéletvédő módra",
+ "repeater_refreshAdvertisementSettings": "Újraindítás hirdetés beállítások",
+ "repeater_refreshed": "{label} frissítve",
+ "@repeater_refreshed": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorRefreshing": "Hiba a {label} frissítés közben",
+ "@repeater_errorRefreshing": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliTitle": "CLI (parancssori felület)",
+ "repeater_debugNextCommand": "Hibakeresés, következő parancs",
+ "repeater_commandHelp": "Segítség",
+ "repeater_clearHistory": "Egyértelmű történet",
+ "repeater_noCommandsSent": "Még egyik parancsot sem küldtünk.",
+ "repeater_typeCommandOrUseQuick": "Írja be a parancsot alább, vagy használja a gyors parancsokat.",
+ "repeater_enterCommandHint": "Írja be a parancsot...",
+ "repeater_previousCommand": "Előző parancs",
+ "repeater_nextCommand": "Következő parancs",
+ "repeater_enterCommandFirst": "Add meg először egy parancsot",
+ "repeater_cliCommandFrameTitle": "CLI parancssor felépítése",
+ "repeater_cliCommandError": "Hiba: {error}",
+ "@repeater_cliCommandError": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliQuickGetName": "Kapcsold össze a nevet",
+ "repeater_cliQuickGetRadio": "Szerezd a rádiót",
+ "repeater_cliQuickGetTx": "Szerezd a TX-t",
+ "repeater_cliQuickNeighbors": "Szomszédok",
+ "repeater_cliQuickVersion": "Verzió",
+ "repeater_cliQuickAdvertise": "Hirdetés",
+ "repeater_cliQuickClock": "óra",
+ "repeater_cliHelpAdvert": "Elküldi egy hirdetési csomagot",
+ "repeater_cliHelpReboot": "Újraindítja a készüléket. (Kérjük, vegye figyelembe, hogy valószínűleg \"Időhiba\" üzenetet fog kapni, ami normális)",
+ "repeater_cliHelpClock": "A jelenlegi időt mutatja az egyes eszközök karórája alapján.",
+ "repeater_cliHelpPassword": "Új adminisztrációs jelszót állít be a eszköz számára.",
+ "repeater_cliHelpVersion": "Megjeleníti a készülék verzióját és a szoftver verziószámát.",
+ "repeater_cliHelpClearStats": "Visszaállítja a különböző statisztikai mérőszámokat a nullára.",
+ "repeater_cliHelpSetAf": "Beállítja az idő-szabályozási tényezőt.",
+ "repeater_cliHelpSetTx": "Beállítja a LoRa átviteli teljesítményt dBm-ben (a rendszer újraindításával alkalmazható).",
+ "repeater_cliHelpSetRepeat": "Engedélyezi vagy tiltja meg a repeater szerepet ezen a csomón.",
+ "repeater_cliHelpSetAllowReadOnly": "(Szoba szerver) Ha \"igen\", akkor üres jelszóval történő bejelentkezés engedélyezett lesz, de nem lehet üzeneteket küldeni a szobában. (Csak olvasási funkció)",
+ "repeater_cliHelpSetFloodMax": "Beállítja a bejövő adatcsomagok maximális számát (ha ez a érték nagyobb vagy egyenlő a maximális értékkel, a csomag nem továbbítódik).",
+ "repeater_cliHelpSetIntThresh": "Beállítja az interferencia határértéket (dB-ben). Az alapérték 14. Ha 0-ra állítja, kiküntheti a csatornák közötti interferencia detektálást.",
+ "repeater_cliHelpSetAgcResetInterval": "Beállítja az intervallumot, amely a \"Automatikus gain\" szabályozó újraindításához szükséges. Beállítás értéke 0, ha a funkciót le kell tiltani.",
+ "repeater_cliHelpSetMultiAcks": "Engedélyezi vagy kikapcsolja a „dupla visszaigazolás” funkciót.",
+ "repeater_cliHelpSetAdvertInterval": "Beállítja az időzítő intervallumot percenként, hogy egy helyi (nincs átjáró) hirdetési csomagot küldjen. Beállítás értéke 0, ha a funkciót le szeretné tiltani.",
+ "repeater_cliHelpSetFloodAdvertInterval": "Beállítja az időzítő intervallumot órában, hogy egy \"áramló\" hirdetési üzenetet küldjön. Beállítás értéke 0, ha a funkciót kikapcsolni kell.",
+ "repeater_cliHelpSetGuestPassword": "Beállítja/frissíti a vendég felhasználói fiókot. (Ez lehetővé teszi a visszatérő felhasználók számára, hogy a \"Statistika lekérdezése\" kérést elküldjék)",
+ "repeater_cliHelpSetName": "Megadja az űrlap neve.",
+ "repeater_cliHelpSetLat": "Beállítja az hirdetés térképen megjelenő pont koordinátájának (tizedes fokokban) a latitude-ját.",
+ "repeater_cliHelpSetLon": "Beállítja az hirdetés térképen megjelenő hosszúság koordinátát (tizedes fokokban).",
+ "repeater_cliHelpSetRadio": "Teljesen új rádióparamétereket állít be, és azokat a beállításokba menti. Az alkalmazásához \"újraindítás\" parancs szükséges.",
+ "repeater_cliHelpSetRxDelay": "Beállítások (kísérleti): Alapérték (legalább 1 értékre kell állítani, hogy hatás legyen), amely alapján a fogadott csomagokhoz enyhe késést alkalmazunk, a jelet ereje/pontszám alapján. 0-ra állítva a funkciót lekapcsoljuk.",
+ "repeater_cliHelpSetTxDelay": "Beállítja egy tényezőt, amely a légköri idővel szorozva, egy áramlás-üzem módú csomaghoz, valamint egy véletlenszerű slot-rendszerhez, hogy késleltesse a továbbítását. (az ütközések valószínűségének csökkentése érdekében)",
+ "repeater_cliHelpSetDirectTxDelay": "Hasonló a txdelay-hez, de ebben az esetben egy véletlenszerű késést alkalmazunk a közvetlen módú csomagok továbbításakor.",
+ "repeater_cliHelpSetBridgeEnabled": "Engedélyez/Tiltás a híd funkciójának.",
+ "repeater_cliHelpSetBridgeDelay": "Állíts be egy késleztatást a csomagok újbóli továbbításakor.",
+ "repeater_cliHelpSetBridgeSource": "Döntse el, hogy a híd fogadott vagy elküldött csomagokat fogja-e továbbítani.",
+ "repeater_cliHelpSetBridgeBaud": "Állítsa be a soros kommunikáció sebességét az RS232 hídok számára.",
+ "repeater_cliHelpSetBridgeSecret": "Állítsa be a titkos kapcsolatot az ESPNOW hídokhoz.",
+ "repeater_cliHelpSetAdcMultiplier": "Lehetővé teszi a felhasználónak, hogy egyedi tényezőt állíts be a riportolt akkumulátor feszültségének módosításához (ez csak bizonyos alkatrészeken támogatott).",
+ "repeater_cliHelpTempRadio": "Időjárás szerinti rádióparamétereket állít be a megadott időtartamra, majd visszaállítja az eredeti beállításokat. (Nem menti a beállításokat a beállítások részben).",
+ "repeater_cliHelpSetPerm": "A ACL-t módosítja. Ha a \"permissions\" érték 0, akkor eltávolítja a megfelelő bejegyzést (a pubkey előtag alapján). Új bejegyzést hoz létre, ha a pubkey-hex teljes hossza, és jelenleg nem szerepel az ACL-ben. A bejegyzést frissíti a megfelelő pubkey előtag alapján. A engedélyek különbözőek a különböző firmware szerepek között, de az alsó 2 bit a következő értékeket képviseli: 0 (Vendég), 1 (Csak olvasás), 2 (Olvasás és írás), 3 (Adminisztrátor)",
+ "repeater_cliHelpGetBridgeType": "Kapcsolatok: hid típusú, RS232, ESPNOW",
+ "repeater_cliHelpLogStart": "Elindítja a csomagok naplózását a fájlrendszerbe.",
+ "repeater_cliHelpLogStop": "Megállítja a csomagok naplózását a fájlrendszerbe.",
+ "repeater_cliHelpLogErase": "Törli a fájlrendszerből a csomagok log-fájljait.",
+ "repeater_cliHelpNeighbors": "Mutat egy listát, amely tartalmazza a más repeater-ek által hallott adatok listáját, amelyek 0-hop hirdetések révén érhetők el. Minden sor az alábbi formát követi: id-prefix-hex:timestamp:snr-times-4",
+ "repeater_cliHelpNeighborRemove": "Törli az első, a megadott kulcs-prefix (hexadecimális formában) alapján megegyező bejegyzést a szomszédok listájából.",
+ "repeater_cliHelpRegion": "(sorozat) Lista az összes meghatározott területet és a jelenlegi árvízvédelmi engedélyeket.",
+ "repeater_cliHelpRegionLoad": "FIGYELEM: ez egy speciális, több parancsot tartalmazó futtatás. Minden következő parancs egy területtel kapcsolatos, amely egyenletes szóközökkel (a szülő-gyermek kapcsolatot jelző) megkülönböztethető. A parancs végrehajtása egy üres sor/parancs küldésével történik.",
+ "repeater_cliHelpRegionGet": "Keresések egy adott név előtérrel (vagy \"*\" globális hatókörre). Válasz: \"-> region-név (szülő-név) 'F'\"",
+ "repeater_cliHelpRegionPut": "Hozzáad vagy frissíti egy régió definíciót megadott néven.",
+ "repeater_cliHelpRegionRemove": "Eltávolítja a megadott nevet használó régió-definíciót. (pontosan meg kell egyeznie, és nem lehet gyermekrégiója)",
+ "repeater_cliHelpRegionAllowf": "Beállítja a megadott területre vonatkozó \"víz\" jogosultságot. (A globális/régi beállítások esetén a \"*\" jelölő)",
+ "repeater_cliHelpRegionDenyf": "Eltávolítja a megadott területre vonatkozó \"F\"lood (víz) engedélyt. (FIGYELEM: jelenleg nem javasolt ezt a globális/régi verzióban használni!!)",
+ "repeater_cliHelpRegionHome": "Visszaállítja a jelenlegi „otthoni” régiót. (Ez a beállítás még nem került alkalmazásra, csak jövőbeli használatra fenyelve)",
+ "repeater_cliHelpRegionHomeSet": "Beállítja a \"házi\" régiót.",
+ "repeater_cliHelpRegionSave": "Megőrzi a régió listát/térképet a tárolóban.",
+ "repeater_cliHelpGps": "Megadja a GPS állapotát. Ha a GPS kikapcsolva van, akkor csak \"ki\" választot ad, ha be van, akkor \"be\", \"állapot\", \"pozíció\", \"satellitok száma\" értékeket ad.",
+ "repeater_cliHelpGpsOnOff": "Engedi a GPS működés állapotát.",
+ "repeater_cliHelpGpsSync": "A hálózati időt az GPS óra időjével szinkronizálja.",
+ "repeater_cliHelpGpsSetLoc": "Beállítja a węsz pozícióját GPS koordináták alapján, és menti a beállításokat.",
+ "repeater_cliHelpGpsAdvert": "Adja meg a hirdetés konfigurációjának helyszín-információját:\n- none: ne tartalmazza a helyszínt a hirdetésekben\n- share: megosztja a GPS-helyszínt (SensorManager-ből)\n- prefs: hirdeti a beállításokban tárolt helyszínt",
+ "repeater_cliHelpGpsAdvertSet": "Beállítja a hirdetés helyszín-specifikus beállításait.",
+ "repeater_commandsListTitle": "Parancsok listája",
+ "repeater_commandsListNote": "FIGYELEM: a különböző \"set ...\" parancsok mellett létezik egy \"get ...\" parancs is.",
+ "repeater_general": "Általános",
+ "repeater_settingsCategory": "Beállítások",
+ "repeater_bridge": "Híd",
+ "repeater_logging": "Naplózás",
+ "repeater_neighborsRepeaterOnly": "Szomszédok (Csak ismétlő funkció)",
+ "repeater_regionManagementRepeaterOnly": "Regionális menedzsment (Csak egyirányú kommunikáció)",
+ "repeater_regionNote": "Region-specifikus parancsokat vezettek be a régiók definiálására és a hozzájuk tartozó engedélyek kezelésére.",
+ "repeater_gpsManagement": "GPS-vezérlés",
+ "repeater_gpsNote": "Az GPS-al kapcsolatos funkciók lehetővé teszik a helyszín-személyesítéssel kapcsolatos feladatok kezelését.",
+ "telemetry_receivedData": "Kapott adatokat a szenzorokról",
+ "telemetry_requestTimeout": "Az adatkapcsolati kérés sikertelen.",
+ "telemetry_errorLoading": "Hiba az adatok begyűjtésében: {error}",
+ "@telemetry_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_noData": "Nincsenek elérhető telemetriadatok.",
+ "telemetry_channelTitle": "{channel} csatorna",
+ "@telemetry_channelTitle": {
+ "placeholders": {
+ "channel": {
+ "type": "int"
+ }
+ }
+ },
+ "telemetry_batteryLabel": "Akku",
+ "telemetry_voltageLabel": "Feszültség",
+ "telemetry_mcuTemperatureLabel": "MCU hőmérséklet",
+ "telemetry_temperatureLabel": "Hőmérséklet",
+ "telemetry_currentLabel": "Jelenlegi",
+ "telemetry_batteryValue": "{percent}% / {volts}V",
+ "@telemetry_batteryValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ },
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_voltageValue": "{volts}V",
+ "@telemetry_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_currentValue": "{amps}A",
+ "@telemetry_currentValue": {
+ "placeholders": {
+ "amps": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_temperatureValue": "{celsius} °C / {fahrenheit} °F",
+ "@telemetry_temperatureValue": {
+ "placeholders": {
+ "celsius": {
+ "type": "String"
+ },
+ "fahrenheit": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_receivedData": "Kapott szomszédok adatait",
+ "neighbors_requestTimedOut": "A szomszédok kérik, hogy tiltsák le a kamerát.",
+ "neighbors_errorLoading": "Hiba a szomszédok betöltésében: {error}",
+ "@neighbors_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_repeatersNeighbors": "Ismétlő eszközök, szomszédok",
+ "neighbors_noData": "Nincsenek elérhető szomszédokról adatok.",
+ "neighbors_unknownContact": "Tudatlan {pubkey}",
+ "@neighbors_unknownContact": {
+ "placeholders": {
+ "pubkey": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_heardAgo": "Értsd: {time} sitten",
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_title": "Csomagok útvonala",
+ "channelPath_viewMap": "Megtekinthető térkép",
+ "channelPath_otherObservedPaths": "Egyéb megfigyelt utak",
+ "channelPath_repeaterHops": "Adat továbbító lépések",
+ "channelPath_noHopDetails": "Ez a csomag nem tartalmaz részletes információkat a \"hop\" (vagy más hasonló) szót használó kifejezésekről.",
+ "channelPath_messageDetails": "Üzenet részletei",
+ "channelPath_senderLabel": "Megküldő",
+ "channelPath_timeLabel": "Idő",
+ "channelPath_repeatsLabel": "Ismétli",
+ "channelPath_pathLabel": "Útvonal {index}",
+ "channelPath_observedLabel": "Megfigyelt",
+ "channelPath_observedPathTitle": "Megfigyelt útvonal: {index} • {hops}",
+ "@channelPath_observedPathTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ },
+ "hops": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noLocationData": "Nincs helyszínadat.",
+ "channelPath_timeWithDate": "{day}/{month} {time}",
+ "@channelPath_timeWithDate": {
+ "placeholders": {
+ "day": {
+ "type": "int"
+ },
+ "month": {
+ "type": "int"
+ },
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_timeOnly": "{time}",
+ "@channelPath_timeOnly": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_unknownPath": "Megfejt",
+ "channelPath_floodPath": "Árvíz",
+ "channelPath_directPath": "Közvetlen",
+ "channelPath_observedZeroOf": "0-ból {total}",
+ "@channelPath_observedZeroOf": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_observedSomeOf": "{observed} of {total} hops",
+ "@channelPath_observedSomeOf": {
+ "placeholders": {
+ "observed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_mapTitle": "Útvonal térkép",
+ "channelPath_noRepeaterLocations": "Ez a útvonal nem támogat repeater-t.",
+ "channelPath_primaryPath": "Útvonal {index} (Elsődleges)",
+ "@channelPath_primaryPath": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "@channelPath_pathLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_pathLabelTitle": "Út",
+ "channelPath_observedPathHeader": "Megfigyelt útvonal",
+ "channelPath_selectedPathLabel": "{label} • {prefixes}",
+ "@channelPath_selectedPathLabel": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ },
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noHopDetailsAvailable": "Ez a csomag nem tartalmaz részletes információkat a szállításhoz.",
+ "channelPath_unknownRepeater": "Tudatlan erősítő",
+ "community_title": "Helyi közösség",
+ "community_create": "Teremtsd meg a közösséget",
+ "community_createDesc": "Légyon létre egy új közösséget, és osszák meg QR-kód segítségével.",
+ "community_join": "Csatlakozjon",
+ "community_joinTitle": "Csatlakozzon a közösséghez",
+ "community_joinConfirmation": "Szeretne csatlakozni a közösséghez, {name}?",
+ "@community_joinConfirmation": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanQr": "QR-kód olvasó a közösség számára",
+ "community_scanInstructions": "Fordítsa a kamerát egy közösségi QR-kód irányába.",
+ "community_showQr": "Megjelenítse a QR-kódot",
+ "community_publicChannel": "Összetartó, közösségi",
+ "community_hashtagChannel": "Helyi hashtaget",
+ "community_name": "Helyi közösség neve",
+ "community_enterName": "Kérjük, a közösség nevét írja be.",
+ "community_created": "A \"{name}\" nevű közösség létrehozva",
+ "@community_created": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_joined": "Csatlakozott a {name} közösséghez",
+ "@community_joined": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_qrTitle": "Osszpontosítás a közösségben",
+ "community_qrInstructions": "Scanned this QR-kódot, hogy csatlakozhat a {name} csoporthoz.",
+ "@community_qrInstructions": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_hashtagPrivacyHint": "A közösségi hashtagekhez tartozó csatornák csak a közösség tagjai számára érhetők el.",
+ "community_invalidQrCode": "Érvénytelen közösségi QR-kód",
+ "community_alreadyMember": "Már tag vagy",
+ "community_alreadyMemberMessage": "Már tagja {name}-nek.",
+ "@community_alreadyMemberMessage": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addPublicChannel": "Hozzon létre egy közösségi nyilvános csatornát",
+ "community_addPublicChannelHint": "Automatikusan hozzon létre ezt a csatornát a közösség számára.",
+ "community_noCommunities": "Még egyik közösség sem csatlakozott.",
+ "community_scanOrCreate": "Scelle egy QR-kódot, vagy hozzon létre egy közösséget, hogy elinduljon.",
+ "community_manageCommunities": "Közösségek kezelése",
+ "community_delete": "Hagyományos közösségi élet",
+ "community_deleteConfirm": "Hagyom {name}-et?",
+ "@community_deleteConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_deleteChannelsWarning": "Ezem törli is {count} csatornát és a hozzá tartozó üzeneteket.",
+ "@community_deleteChannelsWarning": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "community_deleted": "A közösség, amely {name}",
+ "@community_deleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "Titkos visszaállítás",
+ "community_regenerateSecretConfirm": "Újra kell generálni a titkos kulcsot {name} számára? Minden tagnak be kell szkennelnie az új QR-kódot, hogy továbbra is kommunikálhasson.",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "Újraalakítás",
+ "community_secretRegenerated": "Titkos kulcs megújult {name} számára.",
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_updateSecret": "Frissítési titok",
+ "community_secretUpdated": "Titkos információ frissítve {name} számára",
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanToUpdateSecret": "Scanned a új QR-kódot, hogy frissítsük a {name} számára megőrzött titkos információt.",
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addHashtagChannel": "Adjon egy közösségi hashtaget",
+ "community_addHashtagChannelDesc": "Hozz létre egy hashtage-os csatornát ennek a közösségnek",
+ "community_selectCommunity": "Válasszon közösséget",
+ "community_regularHashtag": "Rendszeres hashtag",
+ "community_regularHashtagDesc": "Önmagas szintű hashtaget (bárki csatlakozhat)",
+ "community_communityHashtag": "Helyi hashtaget",
+ "community_communityHashtagDesc": "Csak a közösség tagjai számára",
+ "community_forCommunity": "{name} számára",
+ "@community_forCommunity": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "listFilter_tooltip": "Szűrés és rendezés",
+ "listFilter_sortBy": "Szűrés",
+ "listFilter_latestMessages": "Legfrissebb üzenetek",
+ "listFilter_heardRecently": "Úgy hallottam, hogy...",
+ "listFilter_az": "A-Z",
+ "listFilter_filters": "Szűrők",
+ "listFilter_all": "Mind",
+ "listFilter_favorites": "Kedvencek",
+ "listFilter_addToFavorites": "Megerősítés kívánságlistára",
+ "listFilter_removeFromFavorites": "Törölj a kedvencekből",
+ "listFilter_users": "Felhasználók",
+ "listFilter_repeaters": "Újraküldők",
+ "listFilter_roomServers": "Szoba-szolgálatok",
+ "listFilter_unreadOnly": "Csak olvasatlan",
+ "listFilter_newGroup": "Új csoport",
+ "pathTrace_you": "Te",
+ "pathTrace_failed": "A útvonal követése sikertelen.",
+ "pathTrace_notAvailable": "Az útvonal követési funkció nem elérhető.",
+ "pathTrace_refreshTooltip": "Út mentesség frissítése.",
+ "pathTrace_someHopsNoLocation": "Egy vagy több búzavirág hiányozik a helyszínéről!",
+ "pathTrace_clearTooltip": "Egyértelmű út.",
+ "losSelectStartEnd": "Válassza ki a kezdő és a végpontokat a LOS-hoz.",
+ "losRunFailed": "A látószög ellenőrzése sikertelen: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Teljesen tisztázzuk az összes pontot",
+ "losRunToViewElevationProfile": "Használja a LOS-t, hogy megtekinthesse a magasságkülönbségek diagramját.",
+ "losMenuTitle": "LOS menü",
+ "losMenuSubtitle": "A térképen található pontok kiválasztására vagy a térképen hosszúra nyomva, hogy egyedi pontokat definiálhassunk.",
+ "losShowDisplayNodes": "Megjelenítsen a megjelenítési egységeket",
+ "losCustomPoints": "Egyedi pontok",
+ "losCustomPointLabel": "Egyedi {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "A pont A",
+ "losPointB": "Pont B",
+ "losAntennaA": "Antenna A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenna B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Futtass a LOS-on",
+ "losNoElevationData": "Nincsenek emelkedési adatok.",
+ "losProfileClear": "{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, amelyet {obstruction} akadályoz meg {heightUnit}-ban",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: ellenőrzés...",
+ "losStatusNoData": "LOS: nincs adat",
+ "losStatusSummary": "LOS: {clear}/{total} tisztított, {blocked} blokkolt, {unknown} ismeretlen",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Az alábbi minták esetében nem áll rendelkezésre magasságadat.",
+ "losErrorInvalidInput": "Hibás vagy hiányos táblázatok a LOS (Loss of Signal) számításához.",
+ "losRenameCustomPoint": "Állítsa meg a saját pont nevét",
+ "losPointName": "Pont neve",
+ "losShowPanelTooltip": "Megjelenítse a LOS paneelt",
+ "losHidePanelTooltip": "Rejtse el a LOS paneelt",
+ "losElevationAttribution": "Magasságadatok: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Radio Horizont",
+ "losLegendLosBeam": "LOS jelzés",
+ "losLegendTerrain": "Terület",
+ "losFrequencyLabel": "Hatósság",
+ "losFrequencyInfoTooltip": "Lásd a számítás részleteit",
+ "losFrequencyDialogTitle": "A rádióhullámok hatótávolságának kiszámítása",
+ "losFrequencyDialogDescription": "A {baselineK} értékből kezdve, {baselineFreq} MHz-os frekvencián, a számítás az aktuális {frequencyMHz} MHz-os sávhoz igazítja a k-tényezőt, amely meghatározza a görbös rádióhatótávolság határát.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "contacts_pathTrace": "Útvonal követése",
+ "contacts_ping": "Ping",
+ "contacts_repeaterPathTrace": "Az útvonal követése a repeaterig",
+ "contacts_repeaterPing": "Ping-szinkronizáló",
+ "contacts_roomPathTrace": "Kapcsolat a szobai szerverrel",
+ "contacts_roomPing": "Ping-szolgáló szerver",
+ "contacts_chatTraceRoute": "Útvonal meghatározása",
+ "contacts_pathTraceTo": "Keresse meg a {name} címét.",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_clipboardEmpty": "A kiválasztott szöveg üres.",
+ "contacts_invalidAdvertFormat": "Érvénytelen kontaktinformáció",
+ "contacts_contactImported": "Kapcsolat létrejött.",
+ "contacts_contactImportFailed": "Nem sikerült a kapcsolatot importálni.",
+ "contacts_zeroHopAdvert": "Zero Hop reklám",
+ "contacts_floodAdvert": "Árvízre vonatkozó hirdetés",
+ "contacts_copyAdvertToClipboard": "Másolja a hirdetést a kiválasztási ablakba",
+ "contacts_addContactFromClipboard": "Adjon hozzá egy kapcsolatot a kiválasztott listából",
+ "contacts_ShareContact": "Másolja a kapcsolatot a kiválasztóba",
+ "contacts_ShareContactZeroHop": "Ossza meg a kapcsolatot hirdetés segítségével",
+ "contacts_zeroHopContactAdvertSent": "Kapcsolatot a hirdetésen keresztül.",
+ "contacts_zeroHopContactAdvertFailed": "Nem sikerült a kapcsolatot elküldeni.",
+ "contacts_contactAdvertCopied": "A hirdetés másolva a vágólapra.",
+ "contacts_contactAdvertCopyFailed": "Az hirdetés másolása a vágólapra sikertelen.",
+ "notification_activityTitle": "MeshCore tevékenységek",
+ "notification_messagesCount": "{count} {count, plural, =1{üzenet} other{üzenetek}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{csatornaüzenet} other{csatornaüzenetek}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{új csomópont} other{új csomópontok}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "Új {contactType} megtalálva",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "Új üzenetet kaptam",
+ "settings_gpxExportRepeaters": "Külső eszközök / helyi szerver a GPX formátumba",
+ "settings_gpxExportRepeatersSubtitle": "Exportálható repeater/szobaterm-szerver, amely egy GPX fájlban tárolja a helyzetet.",
+ "settings_gpxExportContacts": "GPX export funkciók",
+ "settings_gpxExportContactsSubtitle": "Az export funkció lehetővé teszi, hogy a GPS fájlban megadott helyszínen is megőrizzük az útvonalat.",
+ "settings_gpxExportAll": "Exportálja az összes kapcsolatot GPX formátumban.",
+ "settings_gpxExportAllSubtitle": "Az összes elérhetőséget, amelyekhez egy helyszín tartozik, egy GPX fájlba exportálja.",
+ "settings_gpxExportSuccess": "A GPX fájl sikeresen exportálva lett.",
+ "settings_gpxExportNoContacts": "Nincs exportálható kapcsolatok.",
+ "settings_gpxExportNotAvailable": "Nem támogatott a jelenlegi eszközön/rendszeren.",
+ "settings_gpxExportError": "Hiba történt az export során.",
+ "settings_gpxExportRepeatersRoom": "Adatátvisszaadó eszközök és helyiségi szerverek helyei",
+ "settings_gpxExportChat": "Kapcsolódó helyszínek",
+ "settings_gpxExportAllContacts": "Az összes kapcsolat helyszíne",
+ "settings_gpxExportShareText": "A meshcore-open-ból exportált térkéadatumok",
+ "settings_gpxExportShareSubject": "meshcore-open GPX formátumú térképi adatok export",
+ "snrIndicator_nearByRepeaters": "Helyszíni erősítők",
+ "snrIndicator_lastSeen": "Utoljára, amikor látták",
+ "contactsSettings_title": "Kapcsolatok beállításai",
+ "contactsSettings_autoAddTitle": "Automatikus felfedezés",
+ "contactsSettings_otherTitle": "Egyéb kapcsolattal kapcsolatos beállítások",
+ "contactsSettings_autoAddUsersTitle": "Automatikus felhasználói hozzáadás",
+ "contactsSettings_autoAddUsersSubtitle": "Engedje, hogy a segítő automatikusan hozzáadja az új felhasználókat.",
+ "contactsSettings_autoAddRepeatersTitle": "Automatikus visszatöltés",
+ "contactsSettings_autoAddRepeatersSubtitle": "Engedje, hogy a segítő eszköz automatikusan hozzáadja az új, megtalált jelzőállomásokat.",
+ "contactsSettings_autoAddRoomServersTitle": "Automatikus szobák szerverek hozzáadása",
+ "contactsSettings_autoAddRoomServersSubtitle": "Engedje, hogy a segítő automatikusan hozzáadja az új, megtalált hálózati szervereket.",
+ "contactsSettings_autoAddSensorsTitle": "Automatikus érzékelők hozzáadása",
+ "contactsSettings_autoAddSensorsSubtitle": "Engedje, hogy a kísérő automatikusan hozzáadja az új, megtalált szenzorokat.",
+ "contactsSettings_overwriteOldestTitle": "Felülírja a legrégebbet",
+ "contactsSettings_overwriteOldestSubtitle": "Amikor a névsor telítődik, a legidősebb, de még nem kedvencként jelölt személyt helyettesíti egy újabb.",
+ "discoveredContacts_Title": "Megtalált kapcsolatok",
+ "discoveredContacts_noMatching": "Nincs megegyező kapcsolat.",
+ "discoveredContacts_searchHint": "Keress új kapcsolatokat",
+ "discoveredContacts_contactAdded": "Kapcsolat hozzáadva",
+ "discoveredContacts_addContact": "Adjon személyhez",
+ "discoveredContacts_copyContact": "Másolja a kapcsolatot a vágólapra",
+ "discoveredContacts_deleteContact": "Törölj a feltalált kapcsolatot",
+ "discoveredContacts_deleteContactAll": "Törölj minden megtalált kapcsolatot",
+ "discoveredContacts_deleteContactAllContent": "Biztos, hogy szeretné törölni az összes eddig megtalált kapcsolatot?",
+ "chat_sendCooldown": "Kérjük, várjon egy pillanatot, mielőtt újra elküldené.",
+ "appSettings_jumpToOldestUnread": "Jelentkezzen az legörebb, olvasatlan üzenetre",
+ "appSettings_jumpToOldestUnreadSubtitle": "Amikor egy új csevet indítunk, amelyben vannak olvashatatlan üzenetek, görgessük a listát, hogy a legelső, olvashatatlan üzenet megjelenjen, nem pedig az utolsó.",
+ "appSettings_languageHu": "Magyar",
+ "appSettings_languageJa": "Japán",
+ "appSettings_languageKo": "Koreai",
+ "radioStats_tooltip": "Rádió és hálózati statisztikák",
+ "radioStats_screenTitle": "Rádió statisztikák",
+ "radioStats_notConnected": "Csatlakozzon egy eszközhöz, hogy megtekinthesse a rádió adatok statisztikáit.",
+ "radioStats_firmwareTooOld": "A rádió statisztikákhoz v8 vagy újabb verziójú szoftver szükséges.",
+ "radioStats_waiting": "Adatokra vár…",
+ "radioStats_noiseFloor": "Háttérzaj szint: {noiseDbm} dBm",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastRssi": "Utolsó RSSI érték: {rssiDbm} dBm",
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastSnr": "Utolsó SNR: {snr} dB",
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "radioStats_txAir": "TX-es idő (összesen): {seconds} másodperc",
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_rxAir": "RX használat időtartama (összesen): {seconds} s",
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_chartCaption": "Háttérzaj szint (dBm) a legutóbbi minták alapján.",
+ "radioStats_stripNoise": "Háttérzaj szint: {noiseDbm} dBm",
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_stripWaiting": "Rádió adatok begyűjtése…",
+ "radioStats_settingsTile": "Rádió statisztikák",
+ "radioStats_settingsSubtitle": "Háttérzaj, RSSI, zaj-sűrűség, és a használat időtartama",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_denyAll": "Elutasítom",
+ "settings_privacySettingsDescription": "Válassza ki, hogy az eszközének melyik információkat oszt meg másokkal.",
+ "settings_privacySubtitle": "Ellenőrizd, hogy milyen információkat osztanak meg.",
+ "settings_privacy": "Adatvédelem beállítások",
+ "settings_allowByContact": "Lehetővé teszi a kapcsolatok kezelését",
+ "settings_allowAll": "Engedje meg mindent",
+ "settings_telemetryBaseMode": "Adatkapcsolati alapállapot",
+ "settings_telemetryLocationMode": "Adatkapcsolási helyszín mód",
+ "settings_telemetryEnvironmentMode": "Adatkapcsolati környezeti mód",
+ "settings_advertLocation": "Reklám megjelenési hely",
+ "settings_advertLocationSubtitle": "A hirdetés tartalmazza a helyszínt.",
+ "settings_multiAck": "Többszöri visszaigazolások: {value}",
+ "settings_telemetryModeUpdated": "A telemetriamód frissítve",
+ "contact_info": "Kapcsolattartási információk",
+ "contact_settings": "Kapcsolat beállítások",
+ "contact_telemetry": "Adatvisszaadás",
+ "contact_lastSeen": "Utoljára, amikor látták",
+ "contact_clearChat": "Tiszta beszélgetés",
+ "contact_teleBase": "Adatgyűjtő központ",
+ "contact_teleBaseSubtitle": "Engedje meg a akkumulátor töltöttségi szintjének és alapvető adatoknak megosztását.",
+ "contact_teleLoc": "Adatkapcsolati helyszín",
+ "contact_teleLocSubtitle": "Engedje meg a helyadatok megosztását",
+ "contact_teleEnv": "Adatkapcsolati környezet",
+ "contact_teleEnvSubtitle": "Engedje meg az érzékelő adatok megosztását",
+ "map_showOverlaps": "Az ismétlő kulcsok ütköznek",
+ "map_runTraceWithReturnPath": "Visszaforduljon az eredeti úton.",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_title": "Fordítás",
+ "translation_enableTitle": "Engedje meg a fordítást",
+ "translation_enableSubtitle": "Fordítsa az érkező üzeneteket, és lehetővé tegye a küldés előtti fordítást.",
+ "translation_composerTitle": "Fordítsa el, mielőtt elküldi",
+ "translation_composerSubtitle": "Ellenőrzi a zeneszerző fordítási ikon alapértékét.",
+ "translation_targetLanguage": "Célnyelv",
+ "translation_useAppLanguage": "Használja az alkalmazás nyelvének beállítását.",
+ "translation_downloadedModelLabel": "Letöltött modell",
+ "translation_presetModelLabel": "Előre definiált Hugging Face-modell",
+ "translation_manualUrlLabel": "Manuális modell URL",
+ "translation_downloadModel": "Letöltés",
+ "translation_downloading": "Letöltés...",
+ "translation_working": "Munkában vagyok...",
+ "translation_stop": "Halt",
+ "translation_mergingChunks": "A letöltött részek összeállítása a végleges fájlba...",
+ "translation_downloadedModels": "Letöltött modelok",
+ "translation_deleteModel": "Törölje a modellt",
+ "translation_modelDownloaded": "Fordítási modell letöltve.",
+ "translation_downloadStopped": "A letöltés leállt.",
+ "translation_downloadFailed": "Letöltés sikertelen: {error}",
+ "translation_enterUrlFirst": "Addon először egy modell URL-t.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_linuxPairingShowPin": "Megjelenítse a PIN-kódot",
+ "scanner_linuxPairingPinPrompt": "Adja meg a(z) {deviceName} PIN-kódját (hagyja üresen, ha nincs).",
+ "scanner_linuxPairingHidePin": "Rejtse el a PIN-kódot",
+ "scanner_linuxPairingPinTitle": "Bluetooth párosítási PIN",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_translateBeforeSending": "Fordítsa el, mielőtt elküldi",
+ "translation_composerEnabledHint": "A üzenetek fordítását a küldés előtt elvégezzük.",
+ "translation_messageTranslation": "Üzenet fordítása",
+ "translation_composerDisabledHint": "Küldj üzeneteket az eredeti, nyomtatott nyelven.",
+ "translation_translateTo": "Fordítás {language}-ra",
+ "translation_translationOptions": "Fordítási lehetőségek",
+ "translation_systemLanguage": "Rendszer nyelvé",
+ "repeater_cliQuickClockSync": "Óra szinkronizálás",
+ "repeater_cliQuickDiscovery": "Fedezd fel a szomszédokat",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Automatikusan küldje el a \"óra szinkronizálás\" üzenetet a sikeres bejelentkezés után.",
+ "repeater_clockSyncAfterLogin": "Óra szinkronizálás bejelentkezés után",
+ "repeater_guestTools": "Vendégek számára elérhető eszközök",
+ "room_guest": "Szoba szerver információk",
+ "chat_sendMessage": "Üzenet küldése",
+ "repeater_guest": "Adatok a repeaterről"
+}
diff --git a/lib/l10n/app_it.arb b/lib/l10n/app_it.arb
index b0c13a0..387c8cf 100644
--- a/lib/l10n/app_it.arb
+++ b/lib/l10n/app_it.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Impossibile eliminare il canale \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "it",
"appTitle": "MeshCore Open",
"nav_contacts": "Contatti",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Numero contatti",
"settings_infoChannelCount": "Numero Canale",
"settings_presets": "Preset",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frequenza (MHz)",
"settings_frequencyHelper": "300,0 - 2500,0",
"settings_frequencyInvalid": "Frequenza non valida (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX Potenza (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Potere TX non valido (0-22 dBm)",
- "settings_longRange": "Lungo Raggio",
- "settings_fastSpeed": "Velocità Rapida",
"settings_error": "Errore: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Nuovo Gruppo",
"contacts_groupName": "Nome gruppo",
"contacts_groupNameRequired": "Il nome del gruppo è obbligatorio.",
+ "contacts_groupNameReserved": "Questo nome del gruppo è riservato",
"contacts_groupAlreadyExists": "Il gruppo \"{name}\" esiste già.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Canale pubblico",
"channels_privateChannel": "Canale privato",
"channels_editChannel": "Modifica canale",
+ "channels_muteChannel": "Silenzia canale",
+ "channels_unmuteChannel": "Attiva notifiche canale",
"channels_deleteChannel": "Elimina canale",
"channels_deleteChannelConfirm": "Eliminare \"{name}\"? Non può essere annullato.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Aprire il link?",
+ "chat_openLinkConfirmation": "Vuoi aprire questo link nel tuo browser?",
+ "chat_open": "Apri",
+ "chat_couldNotOpenLink": "Impossibile aprire il link: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Formato di link non valido",
"map_title": "Mappa Nodi",
"map_noNodesWithLocation": "Nessun nodo con dati di posizione",
"map_nodesNeedGps": "I nodi devono condividere le loro coordinate GPS\nper apparire sulla mappa",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighbours": "Vicini",
- "repeater_neighboursSubtitle": "Visualizza vicini di salto pari a zero.",
+ "repeater_neighbors": "Vicini",
+ "repeater_neighborsSubtitle": "Visualizza vicini di salto pari a zero.",
"neighbors_receivedData": "Ricevute dati vicini",
"neighbors_requestTimedOut": "I vicini richiedono un timeout.",
"neighbors_errorLoading": "Errore nel caricamento dei vicini: {error}",
- "neighbors_repeatersNeighbours": "Ripetitori Vicini",
+ "neighbors_repeatersNeighbors": "Ripetitori Vicini",
"neighbors_noData": "Nessun dato sugli vicini disponibile.",
"channels_createPrivateChannel": "Crea un Canale Privato",
"channels_createPrivateChannelDesc": "Protetta con una chiave segreta.",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Questo eliminerà anche {count} canale/i e i loro messaggi.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Hai lasciato la comunità \"{name}\"",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Hashtag pubblico (chiunque può unirsi)",
"community_communityHashtag": "Hashtag della Comunità",
"community_communityHashtagDesc": "Visibile solo ai membri della comunità",
- "community_forCommunity": "Per {name}"
+ "community_forCommunity": "Per {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecretConfirm": "Regenera la chiave segreta per \"{name}\"? Tutti i membri dovranno scansionare il nuovo codice QR per continuare a comunicare.",
+ "community_regenerateSecret": "Ri genera la chiave segreta",
+ "community_regenerate": "Rigenera",
+ "community_secretRegenerated": "Codice segreto rigenerato per \"{name}\"",
+ "community_updateSecret": "Aggiorna Segreto",
+ "community_secretUpdated": "Segreto aggiornato per \"{name}\"",
+ "community_scanToUpdateSecret": "Scansiona il nuovo codice QR per aggiornare il segreto di \"{name}\"",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_failed": "Tracciamento del percorso fallito.",
+ "pathTrace_you": "Tu",
+ "pathTrace_notAvailable": "Tracciamento del percorso non disponibile.",
+ "pathTrace_refreshTooltip": "Aggiorna Path Trace.",
+ "contacts_ping": "Ping",
+ "contacts_repeaterPathTrace": "Traccia percorso al ripetitore",
+ "contacts_roomPathTrace": "Traccia del percorso al server della stanza",
+ "contacts_pathTrace": "Traccia Percorso",
+ "contacts_repeaterPing": "Ripetitore ping",
+ "contacts_pathTraceTo": "Traccia percorso verso {name}",
+ "contacts_roomPing": "Ping al server della stanza",
+ "contacts_chatTraceRoute": "Traccia percorso path",
+ "appSettings_languageRu": "Russo",
+ "contacts_invalidAdvertFormat": "Dati di contatto non validi",
+ "appSettings_languageUk": "Ucraino",
+ "appSettings_enableMessageTracing": "Abilita tracciamento messaggi",
+ "appSettings_enableMessageTracingSubtitle": "Mostra metadati dettagliati su instradamento e tempi per i messaggi",
+ "contacts_zeroHopAdvert": "Annuncio Zero Hop",
+ "contacts_floodAdvert": "Annuncio alluvionale",
+ "contacts_copyAdvertToClipboard": "Copia Annuncio negli Appunti",
+ "contacts_addContactFromClipboard": "Aggiungere contatto dalla clipboard",
+ "contacts_clipboardEmpty": "La clipboard è vuota.",
+ "contacts_ShareContact": "Copia contatto negli Appunti",
+ "contacts_contactImported": "Il contatto è stato importato.",
+ "contacts_contactImportFailed": "Contatto non importato con successo.",
+ "contacts_zeroHopContactAdvertSent": "Inviato contatto tramite annuncio.",
+ "contacts_contactAdvertCopyFailed": "Copia dell'annuncio nella Clipboard non riuscita.",
+ "contacts_ShareContactZeroHop": "Condividi contatto tramite annuncio",
+ "contacts_zeroHopContactAdvertFailed": "Invio del contatto non riuscito.",
+ "contacts_contactAdvertCopied": "Annuncio copiato negli Appunti.",
+ "notification_activityTitle": "Attività MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{messaggio} other{messaggi}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{messaggio del canale} other{messaggi del canale}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{nuovo nodo} other{nuovi nodi}}",
+ "notification_newTypeDiscovered": "Nuovo {contactType} scoperto",
+ "notification_receivedNewMessage": "Nuovo messaggio ricevuto",
+ "settings_gpxExportRepeaters": "Esporta ripetitori / server di stanza in GPX",
+ "settings_gpxExportContacts": "Esporta compagni in GPX",
+ "settings_gpxExportSuccess": "Esportazione del file GPX completata con successo.",
+ "settings_gpxExportNoContacts": "Nessun contatto da esportare.",
+ "settings_gpxExportNotAvailable": "Non supportato sul tuo dispositivo/Sistema Operativo",
+ "settings_gpxExportError": "Si è verificato un errore durante l'esportazione.",
+ "settings_gpxExportRepeatersSubtitle": "Esporta ripetitori / roomserver con una posizione in un file GPX.",
+ "settings_gpxExportContactsSubtitle": "Esporta i compagni con una posizione in un file GPX.",
+ "settings_gpxExportAll": "Esporta tutti i contatti in GPX",
+ "settings_gpxExportAllSubtitle": "Esporta tutti i contatti con una posizione in un file GPX.",
+ "settings_gpxExportChat": "Posizioni dei compagni",
+ "settings_gpxExportRepeatersRoom": "Posizioni del server ripetitore e della stanza",
+ "settings_gpxExportAllContacts": "Tutte le posizioni dei contatti",
+ "settings_gpxExportShareText": "Dati mappa esportati da meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open esportazione dati mappa GPX",
+ "pathTrace_someHopsNoLocation": "Uno o più dei luppoli mancano di una posizione!",
+ "map_removeLast": "Rimuovi ultimo",
+ "map_pathTraceCancelled": "Tracciamento del percorso annullato.",
+ "pathTrace_clearTooltip": "Pulisci percorso",
+ "map_runTrace": "Esegui Path Trace",
+ "map_tapToAdd": "Tocca i nodi per aggiungerli al percorso.",
+ "scanner_bluetoothOff": "Il Bluetooth è disattivato.",
+ "scanner_bluetoothOffMessage": "Si prega di attivare il Bluetooth per effettuare la scansione dei dispositivi.",
+ "scanner_chromeRequired": "Browser Chrome richiesto",
+ "scanner_chromeRequiredMessage": "Questa applicazione web richiede Google Chrome o un browser basato su Chromium per il supporto Bluetooth.",
+ "scanner_enableBluetooth": "Abilita il Bluetooth",
+ "snrIndicator_nearByRepeaters": "Ripetitori vicini",
+ "snrIndicator_lastSeen": "Ultimo accesso",
+ "chat_ShowAllPaths": "Mostra tutti i percorsi",
+ "settings_clientRepeat": "Ripetizione \"fuori dalla rete\"",
+ "settings_clientRepeatFreqWarning": "Per la comunicazione fuori rete, è necessario utilizzare frequenze di 433, 869 o 918 MHz.",
+ "settings_clientRepeatSubtitle": "Permetti a questo dispositivo di ripetere i pacchetti di rete per gli altri.",
+ "settings_aboutOpenMeteoAttribution": "Dati di elevazione LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Unità",
+ "appSettings_unitsMetric": "Metrico (m/km)",
+ "appSettings_unitsImperial": "Imperiale (ft / mi)",
+ "map_lineOfSight": "Linea di vista",
+ "map_losScreenTitle": "Linea di vista",
+ "losSelectStartEnd": "Seleziona i nodi iniziali e finali per la LOS.",
+ "losRunFailed": "Controllo della linea di vista fallito: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Cancella tutti i punti",
+ "losRunToViewElevationProfile": "Eseguire LOS per visualizzare il profilo altimetrico",
+ "losMenuTitle": "Menù LOS",
+ "losMenuSubtitle": "Tocca i nodi o premi a lungo la mappa per punti personalizzati",
+ "losShowDisplayNodes": "Mostra i nodi di visualizzazione",
+ "losCustomPoints": "Punti personalizzati",
+ "losCustomPointLabel": "Personalizzato {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punto A",
+ "losPointB": "Punto B",
+ "losAntennaA": "Antenna A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenna B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Esegui LOS",
+ "losNoElevationData": "Nessun dato di elevazione",
+ "losProfileClear": "{distance} {distanceUnit}, libera LOS, distanza minima {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, bloccato da {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: controllo...",
+ "losStatusNoData": "LOS: nessun dato",
+ "losStatusSummary": "LOS: {clear}/{total} libera, {blocked} bloccato, {unknown} sconosciuto",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Dati di elevazione non disponibili per uno o più campioni.",
+ "losErrorInvalidInput": "Dati punti/elevazione non validi per il calcolo della LOS.",
+ "losRenameCustomPoint": "Rinomina punto personalizzato",
+ "losPointName": "Nome del punto",
+ "losShowPanelTooltip": "Mostra il pannello LOS",
+ "losHidePanelTooltip": "Nascondi il pannello LOS",
+ "losElevationAttribution": "Dati di elevazione: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Orizzonte radio",
+ "losLegendLosBeam": "Linea di vista",
+ "losLegendTerrain": "Terreno",
+ "losFrequencyLabel": "Frequenza",
+ "losFrequencyInfoTooltip": "Visualizza i dettagli del calcolo",
+ "losFrequencyDialogTitle": "Calcolo dell’orizzonte radio",
+ "losFrequencyDialogDescription": "Partendo da k={baselineK} a {baselineFreq} MHz, il calcolo regola il fattore k per l'attuale banda {frequencyMHz} MHz, che definisce il limite curvo dell'orizzonte radio.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_addToFavorites": "Aggiungi ai preferiti",
+ "listFilter_removeFromFavorites": "Rimuovi dai preferiti",
+ "listFilter_favorites": "Preferiti",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "Cerca {number}{str} Utenti...",
+ "contacts_searchContactsNoNumber": "Cerca Contatti...",
+ "contacts_searchFavorites": "Cerca {number}{str} Preferiti...",
+ "contacts_unread": "Non letti",
+ "contacts_searchRepeaters": "Cerca {number}{str} Ripetitori...",
+ "contacts_searchRoomServers": "Cerca {number}{str} server Room...",
+ "contactsSettings_title": "Impostazioni dei contatti",
+ "settings_contactSettings": "Impostazioni di contatto",
+ "contactsSettings_otherTitle": "Altre impostazioni relative ai contatti",
+ "contactsSettings_autoAddUsersSubtitle": "Consenti al compagno di aggiungere automaticamente gli utenti scoperti.",
+ "contactsSettings_autoAddRepeatersTitle": "Aggiungere ripetitori automaticamente",
+ "contactsSettings_autoAddRoomServersSubtitle": "Consenti al compagno di aggiungere automaticamente i server delle stanze scoperte.",
+ "contactsSettings_autoAddSensorsTitle": "Aggiungere automaticamente i sensori",
+ "settings_contactSettingsSubtitle": "Impostazioni per l'aggiunta dei contatti",
+ "contactsSettings_autoAddUsersTitle": "Aggiungere utenti automaticamente",
+ "contactsSettings_autoAddTitle": "Scoperta automatica",
+ "contactsSettings_autoAddSensorsSubtitle": "Consenti al compagno di aggiungere automaticamente i sensori scoperti",
+ "discoveredContacts_noMatching": "Nessun contatto corrispondente",
+ "contactsSettings_autoAddRepeatersSubtitle": "Consenti al compagno di aggiungere automaticamente i ripetitori scoperti.",
+ "discoveredContacts_searchHint": "Cerca contatti scoperti",
+ "contactsSettings_autoAddRoomServersTitle": "Aggiungere automaticamente i server delle stanze",
+ "discoveredContacts_addContact": "Aggiungi contatto",
+ "contactsSettings_overwriteOldestTitle": "Sostituisci il più vecchio",
+ "discoveredContacts_Title": "Contatti scoperti",
+ "discoveredContacts_contactAdded": "Contatto aggiunto",
+ "discoveredContacts_deleteContact": "Elimina Contatto",
+ "discoveredContacts_copyContact": "Copia contatto negli appunti",
+ "contactsSettings_overwriteOldestSubtitle": "Quando l'elenco dei contatti è pieno, il contatto più vecchio non tra i preferiti verrà sostituito.",
+ "common_deleteAll": "Elimina tutto",
+ "discoveredContacts_deleteContactAllContent": "Sei sicuro di voler eliminare tutti i contatti scoperti?",
+ "discoveredContacts_deleteContactAll": "Eliminare tutti i contatti scoperti",
+ "map_guessedLocation": "Località indovinata",
+ "map_showGuessedLocations": "Mostra le posizioni stimate dei nodi",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenSubtitle": "Seleziona il dispositivo seriale rilevato e connettilo direttamente al tuo nodo MeshCore.",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenTitle": "Connessione tramite USB",
+ "usbScreenStatus": "Seleziona un dispositivo USB",
+ "usbScreenNote": "La comunicazione seriale USB è attiva sui dispositivi Android supportati e sulle piattaforme desktop.",
+ "usbScreenEmptyState": "Nessun dispositivo USB rilevato. Collegare uno e aggiornare.",
+ "usbErrorPermissionDenied": "È stato negato l'accesso tramite USB.",
+ "usbErrorDeviceMissing": "Il dispositivo USB selezionato non è più disponibile.",
+ "usbErrorInvalidPort": "Seleziona un dispositivo USB valido.",
+ "usbErrorBusy": "Un'altra richiesta di connessione tramite USB è già in corso.",
+ "usbErrorNotConnected": "Non è collegato alcun dispositivo USB.",
+ "usbErrorOpenFailed": "Impossibile aprire il dispositivo USB selezionato.",
+ "usbErrorConnectFailed": "Impossibile connettersi al dispositivo USB selezionato.",
+ "usbErrorUnsupported": "La comunicazione seriale tramite USB non è supportata su questa piattaforma.",
+ "usbErrorAlreadyActive": "La connessione USB è già attiva.",
+ "usbErrorNoDeviceSelected": "Non è stato selezionato alcun dispositivo USB.",
+ "usbErrorPortClosed": "La connessione USB non è attiva.",
+ "usbFallbackDeviceName": "Dispositivo per comunicazione seriale su rete",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Ricerca di dispositivi USB...",
+ "usbConnectionFailed": "Errore nella connessione USB: {error}",
+ "usbStatus_notConnected": "Seleziona un dispositivo USB",
+ "usbStatus_connecting": "Connessione al dispositivo USB...",
+ "usbErrorConnectTimedOut": "La connessione è scaduta. Assicurarsi che il dispositivo abbia il firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostLabel": "Indirizzo IP",
+ "tcpHostHint": "192.168.40.10",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "Stabilire una connessione tramite TCP",
+ "tcpPortLabel": "Porta",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Inserisci l'endpoint e connettiti.",
+ "tcpStatus_connectingTo": "Connessione a {endpoint}...",
+ "tcpErrorHostRequired": "È necessario fornire un indirizzo IP.",
+ "tcpErrorPortInvalid": "La dimensione della porta deve essere compresa tra 1 e 65535.",
+ "tcpErrorUnsupported": "Il protocollo TCP non è supportato su questa piattaforma.",
+ "tcpErrorTimedOut": "La connessione TCP è scaduta.",
+ "tcpConnectionFailed": "Impossibile stabilire la connessione TCP: {error}",
+ "map_showDiscoveryContacts": "Mostra Contatti di Discovery",
+ "map_setAsMyLocation": "Imposta come la mia posizione",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacySettingsDescription": "Scegli le informazioni che il tuo dispositivo condivide con gli altri.",
+ "settings_allowByContact": "Consenti in base ai flag di contatto",
+ "settings_telemetryLocationMode": "Modalità di posizionamento telemetrico",
+ "settings_telemetryEnvironmentMode": "Modalità di ambiente di telemetria",
+ "settings_advertLocation": "Posizione dell'annuncio",
+ "settings_advertLocationSubtitle": "Includi la posizione nell'annuncio",
+ "settings_privacy": "Impostazioni sulla privacy",
+ "settings_denyAll": "Negare tutto",
+ "settings_privacySubtitle": "Controlla le informazioni che vengono condivise.",
+ "settings_allowAll": "Consenti tutto",
+ "contact_info": "Informazioni di Contatto",
+ "settings_telemetryBaseMode": "Modalità di base di telemetria",
+ "contact_teleBase": "Base di telemetria",
+ "contact_teleLoc": "Posizione telemetria",
+ "contact_teleLocSubtitle": "Consenti la condivisione dei dati di posizione",
+ "contact_clearChat": "Cancella chat",
+ "contact_telemetry": "Telemetria",
+ "contact_settings": "Impostazioni di contatto",
+ "contact_lastSeen": "Ultimo accesso",
+ "contact_teleBaseSubtitle": "Consenti la condivisione del livello della batteria e della telemetria di base",
+ "contact_teleEnvSubtitle": "Consenti la condivisione dei dati del sensore ambientale",
+ "contact_teleEnv": "Ambiente di telemetria",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeight": "Peso iniziale del percorso",
+ "appSettings_initialRouteWeightSubtitle": "Peso di partenza per nuovi percorsi",
+ "appSettings_maxRouteWeightSubtitle": "Il peso massimo che un percorso può accumulare grazie a consegne di successo.",
+ "appSettings_maxRouteWeight": "Massimo peso consentito per il percorso",
+ "appSettings_routeWeightSuccessIncrement": "Aumento del peso del successo",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Peso aggiunto a un percorso dopo una consegna riuscita.",
+ "appSettings_routeWeightFailureDecrement": "Riduzione del peso associato al fallimento",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Peso rimosso da un percorso dopo un tentativo di consegna fallito.",
+ "appSettings_maxMessageRetries": "Numero massimo di tentativi di invio del messaggio",
+ "appSettings_maxMessageRetriesSubtitle": "Numero di tentativi di riprova prima di considerare un messaggio come fallito.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Modalità telemetria aggiornata",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "map_showOverlaps": "Sovrapposizioni della chiave ripetitore",
+ "map_runTraceWithReturnPath": "Tornare indietro sullo stesso percorso",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_jumpToOldestUnreadSubtitle": "Quando si apre una chat con messaggi non letti, scorrete verso l'alto fino al primo messaggio non letto, invece che al più recente.",
+ "chat_sendCooldown": "Si prega di attendere un momento prima di inviare nuovamente.",
+ "appSettings_jumpToOldestUnread": "Vai al messaggio più vecchio non letto",
+ "appSettings_languageHu": "Ungherese",
+ "appSettings_languageJa": "Giapponese",
+ "appSettings_languageKo": "Coreano",
+ "radioStats_tooltip": "Statistiche per radio e reti",
+ "radioStats_screenTitle": "Statistiche radio",
+ "radioStats_notConnected": "Connettiti a un dispositivo per visualizzare le statistiche radio.",
+ "radioStats_firmwareTooOld": "Le statistiche radio richiedono il firmware versione 8 o successiva.",
+ "radioStats_noiseFloor": "Livello di rumore: {noiseDbm} dBm",
+ "radioStats_waiting": "In attesa dei dati…",
+ "radioStats_lastRssi": "Ultimo valore RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Ultimo SNR: {snr} dB",
+ "radioStats_txAir": "Tempo di trasmissione in diretta (totale): {seconds} s",
+ "radioStats_rxAir": "Tempo di trasmissione RX (totale): {seconds} s",
+ "radioStats_chartCaption": "Livello di rumore (dBm) misurato su campioni recenti.",
+ "radioStats_stripNoise": "Livello di rumore: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Recupero delle statistiche radio…",
+ "radioStats_settingsTile": "Statistiche radio",
+ "radioStats_settingsSubtitle": "Livello di rumore, RSSI, rapporto segnale/rumore (SNR) e tempo di trasmissione",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Tradurre prima di inviare",
+ "translation_enableSubtitle": "Tradurre i messaggi in arrivo e consentire la traduzione preventiva prima dell'invio.",
+ "translation_enableTitle": "Abilitare la traduzione",
+ "translation_title": "Traduzione",
+ "translation_composerSubtitle": "Controlla lo stato predefinito dell'icona di traduzione del compositore.",
+ "translation_targetLanguage": "Lingua di destinazione",
+ "translation_useAppLanguage": "Utilizza la lingua dell'app",
+ "translation_downloadedModelLabel": "Modello scaricato",
+ "translation_presetModelLabel": "Modello predefinito di Hugging Face",
+ "translation_manualUrlLabel": "URL del modello manuale",
+ "translation_downloadModel": "Scarica il modello",
+ "translation_downloading": "Inizio download...",
+ "translation_working": "Lavoro...",
+ "translation_stop": "Smetta",
+ "translation_downloadedModels": "Modelli scaricati",
+ "translation_mergingChunks": "Unione dei frammenti scaricati in un unico file...",
+ "translation_deleteModel": "Elimina modello",
+ "translation_modelDownloaded": "Modello di traduzione scaricato.",
+ "translation_downloadStopped": "Il download è stato interrotto.",
+ "translation_downloadFailed": "Download fallito: {error}",
+ "translation_enterUrlFirst": "Inserite innanzitutto l'URL del modello.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_messageTranslation": "Traduzione del messaggio",
+ "translation_translateBeforeSending": "Tradurre prima di inviare",
+ "translation_composerDisabledHint": "Invia messaggi utilizzando la lingua originale, scritta.",
+ "translation_composerEnabledHint": "I messaggi verranno tradotti prima di essere inviati.",
+ "translation_translateTo": "Tradurre in {language}",
+ "translation_translationOptions": "Opzioni di traduzione",
+ "translation_systemLanguage": "Lingua del sistema",
+ "scanner_linuxPairingPinPrompt": "Inserire il codice PIN per {deviceName} (lasciare vuoto se non presente).",
+ "scanner_linuxPairingShowPin": "Mostra PIN",
+ "scanner_linuxPairingPinTitle": "PIN per l'accoppiamento Bluetooth",
+ "scanner_linuxPairingHidePin": "Nascondi il PIN",
+ "repeater_cliQuickClockSync": "Sincronizzazione dell'orologio",
+ "repeater_cliQuickDiscovery": "Scopri i Vicini",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Invia automaticamente il comando \"sincronizzazione dell'orologio\" dopo un login riuscito.",
+ "repeater_clockSyncAfterLogin": "Sincronizzazione dell'orologio dopo il login",
+ "repeater_guest": "Informazioni sul ripetitore",
+ "repeater_guestTools": "Strumenti per gli ospiti",
+ "chat_sendMessage": "Invia messaggio",
+ "room_guest": "Informazioni sul server"
}
diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb
new file mode 100644
index 0000000..b63f146
--- /dev/null
+++ b/lib/l10n/app_ja.arb
@@ -0,0 +1,2115 @@
+{
+ "@@locale": "ja",
+ "appTitle": "MeshCore Open",
+ "nav_contacts": "連絡先",
+ "nav_channels": "チャンネル",
+ "nav_map": "地図",
+ "common_cancel": "キャンセル",
+ "common_ok": "了解",
+ "common_connect": "接続する",
+ "common_unknownDevice": "不明なデバイス",
+ "common_save": "保存",
+ "common_delete": "削除",
+ "common_deleteAll": "すべて削除",
+ "common_close": "閉じる",
+ "common_edit": "編集",
+ "common_add": "追加",
+ "common_settings": "設定",
+ "common_disconnect": "切断する",
+ "common_connected": "接続されている",
+ "common_disconnected": "切断",
+ "common_create": "作成する",
+ "common_continue": "続き",
+ "common_share": "共有する",
+ "common_copy": "コピー",
+ "common_retry": "再試",
+ "common_hide": "隠す",
+ "common_remove": "削除",
+ "common_enable": "有効化する",
+ "common_disable": "無効化する",
+ "common_reboot": "再起動",
+ "common_loading": "読み込み中...",
+ "common_notAvailable": "—",
+ "common_voltageValue": "{volts} V",
+ "@common_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "common_percentValue": "{percent}%",
+ "@common_percentValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ }
+ }
+ },
+ "scanner_title": "MeshCore オープン",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "ブルートゥース",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "TCP を使用して接続",
+ "tcpHostLabel": "IPアドレス",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "港",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "エンドポイントを入力し、接続する",
+ "tcpStatus_connectingTo": "{endpoint} への接続中...",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpErrorHostRequired": "IPアドレスが必要です。",
+ "tcpErrorPortInvalid": "ポート番号は1から65535の範囲で指定してください。",
+ "tcpErrorUnsupported": "このプラットフォームでは、TCP 転送はサポートされていません。",
+ "tcpErrorTimedOut": "TCP 接続がタイムアウトしました。",
+ "tcpConnectionFailed": "TCP接続に失敗しました:{error}",
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbScreenTitle": "USB経由で接続",
+ "usbScreenSubtitle": "検出されたシリアルデバイスを選択し、MeshCoreノードに直接接続してください。",
+ "usbScreenStatus": "USBデバイスを選択する",
+ "usbScreenNote": "USBシリアルポートは、サポートされているAndroidデバイスおよびデスクトッププラットフォームで利用可能です。",
+ "usbScreenEmptyState": "USBデバイスが見つかりませんでした。「別のUSBデバイスを接続して、再度確認してください。」",
+ "usbErrorPermissionDenied": "USBへのアクセス許可が拒否されました。",
+ "usbErrorDeviceMissing": "選択されたUSBデバイスは、もう利用できません。",
+ "usbErrorInvalidPort": "有効なUSBデバイスを選択してください。",
+ "usbErrorBusy": "別のUSB接続の要求がすでに処理中です。",
+ "usbErrorNotConnected": "USBデバイスは接続されていません。",
+ "usbErrorOpenFailed": "選択したUSBデバイスを開くことができません。",
+ "usbErrorConnectFailed": "選択したUSBデバイスへの接続に失敗しました。",
+ "usbErrorUnsupported": "このプラットフォームでは、USBシリアル通信はサポートされていません。",
+ "usbErrorAlreadyActive": "USB接続はすでに確立されています。",
+ "usbErrorNoDeviceSelected": "USBデバイスは選択されていません。",
+ "usbErrorPortClosed": "USB接続は確立されていません。",
+ "usbErrorConnectTimedOut": "接続がタイムアウトしました。デバイスにUSBコンパニオンファームウェアがインストールされていることを確認してください。",
+ "usbFallbackDeviceName": "ウェブシリアルデバイス",
+ "usbStatus_notConnected": "USBデバイスを選択する",
+ "usbStatus_connecting": "USBデバイスへの接続中...",
+ "usbStatus_searching": "USBデバイスを検索中...",
+ "usbConnectionFailed": "USB接続に失敗しました:{error}",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_scanning": "デバイスをスキャン中...",
+ "scanner_connecting": "接続中...",
+ "scanner_disconnecting": "切断...",
+ "scanner_notConnected": "接続されていない",
+ "scanner_connectedTo": "{deviceName} に接続",
+ "@scanner_connectedTo": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_searchingDevices": "MeshCoreデバイスの検索",
+ "scanner_tapToScan": "MeshCore デバイスを検索するには、「スキャン」ボタンをタップしてください。",
+ "scanner_connectionFailed": "接続に失敗しました:{error}",
+ "@scanner_connectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_stop": "停止",
+ "scanner_scan": "スキャン",
+ "scanner_bluetoothOff": "Bluetooth はオフになっています",
+ "scanner_bluetoothOffMessage": "Bluetoothを有効にして、デバイスを検索してください。",
+ "scanner_chromeRequired": "Chrome ブラウザが必須です",
+ "scanner_chromeRequiredMessage": "このWebアプリケーションは、Bluetooth機能を利用するために、Google ChromeまたはChromiumベースのブラウザが必要です。",
+ "scanner_enableBluetooth": "Bluetoothを有効にする",
+ "device_quickSwitch": "素早い切り替え",
+ "device_meshcore": "メッシュコア",
+ "settings_title": "設定",
+ "settings_deviceInfo": "デバイス情報",
+ "settings_appSettings": "アプリ設定",
+ "settings_appSettingsSubtitle": "通知、メッセージング、および地図の表示設定",
+ "settings_nodeSettings": "ノード設定",
+ "settings_nodeName": "ノード名",
+ "settings_nodeNameNotSet": "設定されていない",
+ "settings_nodeNameHint": "ノード名を入力してください",
+ "settings_nodeNameUpdated": "氏名変更",
+ "settings_radioSettings": "ラジオ設定",
+ "settings_radioSettingsSubtitle": "周波数、電力、スプレッドファクター",
+ "settings_radioSettingsUpdated": "ラジオの設定が更新されました",
+ "settings_location": "場所",
+ "settings_locationSubtitle": "GPS 座標",
+ "settings_locationUpdated": "場所とGPS設定が更新されました",
+ "settings_locationBothRequired": "緯度と経度をそれぞれ入力してください。",
+ "settings_locationInvalid": "無効な緯度または経度。",
+ "settings_locationGPSEnable": "GPS機能有効",
+ "settings_locationGPSEnableSubtitle": "GPSが自動的に位置情報を更新できるようにする。",
+ "settings_locationIntervalSec": "GPS データの取得間隔(秒)",
+ "settings_locationIntervalInvalid": "間隔は少なくとも60秒で、86400秒未満でなければなりません。",
+ "settings_latitude": "緯度",
+ "settings_longitude": "経度",
+ "settings_contactSettings": "連絡設定",
+ "settings_contactSettingsSubtitle": "連絡先を追加する設定",
+ "settings_privacyMode": "プライバシーモード",
+ "settings_privacyModeSubtitle": "広告に名前/場所を記載しない",
+ "settings_privacyModeToggle": "プライバシーモードをオンにして、広告に表示される名前や場所を非表示にします。",
+ "settings_privacyModeEnabled": "プライバシーモードが有効になっています",
+ "settings_privacyModeDisabled": "プライバシーモードは無効化されています",
+ "settings_actions": "行動",
+ "settings_sendAdvertisement": "広告を送信する",
+ "settings_sendAdvertisementSubtitle": "現在、放送での活動",
+ "settings_advertisementSent": "広告が送信されました",
+ "settings_syncTime": "同期時間",
+ "settings_syncTimeSubtitle": "デバイスの時刻を、携帯電話の時刻に合わせる",
+ "settings_timeSynchronized": "時間同期",
+ "settings_refreshContacts": "連絡先を更新する",
+ "settings_refreshContactsSubtitle": "デバイスから連絡先リストを再読み込みする",
+ "settings_rebootDevice": "デバイスを再起動する",
+ "settings_rebootDeviceSubtitle": "MeshCore デバイスを再起動する",
+ "settings_rebootDeviceConfirm": "本当にデバイスを再起動したいですか? その場合、接続が切断されます。",
+ "settings_debug": "デバッグ",
+ "settings_bleDebugLog": "BLE デバッグログ",
+ "settings_bleDebugLogSubtitle": "BLEコマンド、応答、および生のデータ",
+ "settings_appDebugLog": "アプリケーションのデバッグログ",
+ "settings_appDebugLogSubtitle": "アプリケーションのデバッグメッセージ",
+ "settings_about": "概要",
+ "settings_aboutVersion": "MeshCore Open {version}版",
+ "@settings_aboutVersion": {
+ "placeholders": {
+ "version": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_aboutLegalese": "2026年のMeshCoreオープンソースプロジェクト",
+ "settings_aboutDescription": "MeshCore LoRaメッシュネットワークデバイス用の、オープンソースのFlutterクライアント。",
+ "settings_aboutOpenMeteoAttribution": "LOS 標高データ:Open-Meteo (CC BY 4.0)",
+ "settings_infoName": "名前",
+ "settings_infoId": "ID",
+ "settings_infoStatus": "ステータス",
+ "settings_infoBattery": "バッテリー",
+ "settings_infoPublicKey": "公開鍵",
+ "settings_infoContactsCount": "連絡先数",
+ "settings_infoChannelCount": "チャンネル数",
+ "settings_presets": "プリセット",
+ "settings_frequency": "周波数 (MHz)",
+ "settings_frequencyHelper": "300.0 - 2500.0",
+ "settings_frequencyInvalid": "無効な周波数 (300-2500 MHz)",
+ "settings_bandwidth": "帯域幅",
+ "settings_spreadingFactor": "伝播係数",
+ "settings_codingRate": "コーディング速度",
+ "settings_txPower": "TX 信号電力 (dBm)",
+ "settings_txPowerHelper": "0 - 22",
+ "settings_txPowerInvalid": "無効な送信電力 (0-22 dBm)",
+ "settings_clientRepeat": "オフグリッド(電力網から孤立した状態)の繰り返し",
+ "settings_clientRepeatSubtitle": "このデバイスが、他のデバイスに対してメッシュパケットを繰り返し送信できるようにする。",
+ "settings_clientRepeatFreqWarning": "オフグリッドでの再送には、433MHz、869MHz、または918MHzの周波数が必要です。",
+ "settings_error": "エラー:{message}",
+ "@settings_error": {
+ "placeholders": {
+ "message": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_title": "アプリ設定",
+ "appSettings_appearance": "外観",
+ "appSettings_theme": "テーマ",
+ "appSettings_themeSystem": "システムデフォルト",
+ "appSettings_themeLight": "光",
+ "appSettings_themeDark": "暗い",
+ "appSettings_language": "言語",
+ "appSettings_languageSystem": "システムデフォルト",
+ "appSettings_languageEn": "英語",
+ "appSettings_languageFr": "フランス語",
+ "appSettings_languageEs": "スペイン語",
+ "appSettings_languageDe": "ドイツ語",
+ "appSettings_languagePl": "ポーランド語",
+ "appSettings_languageSl": "スロベニア語",
+ "appSettings_languagePt": "ポルトガル語",
+ "appSettings_languageIt": "イタリア語",
+ "appSettings_languageZh": "中国語",
+ "appSettings_languageSv": "スウェーデン語",
+ "appSettings_languageNl": "オランダ語",
+ "appSettings_languageSk": "スロベニア語",
+ "appSettings_languageBg": "ブルガリア語",
+ "appSettings_languageRu": "ロシア語",
+ "appSettings_languageUk": "ウクライナ語",
+ "appSettings_enableMessageTracing": "メッセージ追跡機能を有効にする",
+ "appSettings_enableMessageTracingSubtitle": "メッセージに関する詳細な経路およびタイミングに関するメタデータを表示する",
+ "appSettings_notifications": "通知",
+ "appSettings_enableNotifications": "通知を有効にする",
+ "appSettings_enableNotificationsSubtitle": "メッセージや広告に関する通知を受け取る",
+ "appSettings_notificationPermissionDenied": "通知の許可が拒否されました",
+ "appSettings_notificationsEnabled": "通知機能が有効になっています",
+ "appSettings_notificationsDisabled": "通知が無効化されています",
+ "appSettings_messageNotifications": "メッセージ通知",
+ "appSettings_messageNotificationsSubtitle": "新しいメッセージを受信した際に、通知を表示する",
+ "appSettings_channelMessageNotifications": "チャネルメッセージの通知",
+ "appSettings_channelMessageNotificationsSubtitle": "チャンネルからのメッセージを受信した際に、通知を表示する",
+ "appSettings_advertisementNotifications": "広告通知",
+ "appSettings_advertisementNotificationsSubtitle": "新しいノードが発見された場合に通知を表示する",
+ "appSettings_messaging": "メッセージング",
+ "appSettings_clearPathOnMaxRetry": "マックスリトライでの明確な手順",
+ "appSettings_clearPathOnMaxRetrySubtitle": "5回送信に失敗した場合、連絡経路をリセットする",
+ "appSettings_pathsWillBeCleared": "5回失敗した後、経路が再開されます。",
+ "appSettings_pathsWillNotBeCleared": "パスは自動で削除されません。",
+ "appSettings_autoRouteRotation": "自動ルートの切り替え",
+ "appSettings_autoRouteRotationSubtitle": "最適なルートと、洪水モードを切り替える",
+ "appSettings_autoRouteRotationEnabled": "自動ルートの切り替え機能が有効になっています",
+ "appSettings_autoRouteRotationDisabled": "自動ルートの変更機能が無効になっています。",
+ "appSettings_maxRouteWeight": "最大ルート重量",
+ "appSettings_maxRouteWeightSubtitle": "ある経路が、成功裏に配送された場合に、積み上げられる最大重量",
+ "appSettings_initialRouteWeight": "初期ルートの重み",
+ "appSettings_initialRouteWeightSubtitle": "新たに発見された経路の初期重量",
+ "appSettings_routeWeightSuccessIncrement": "成功時の重み増加",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "配送が成功した場合に、経路に追加される重量",
+ "appSettings_routeWeightFailureDecrement": "失敗時の重み減少",
+ "appSettings_routeWeightFailureDecrementSubtitle": "配送に失敗した際に、経路から取り除かれた重量",
+ "appSettings_maxMessageRetries": "最大メッセージ再試行回数",
+ "appSettings_maxMessageRetriesSubtitle": "メッセージを「失敗」とマークするまでの、再試行回数",
+ "path_routeWeight": "{weight}/{max}",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_battery": "バッテリー",
+ "appSettings_batteryChemistry": "電池の化学",
+ "appSettings_batteryChemistryPerDevice": "{deviceName} 単位",
+ "@appSettings_batteryChemistryPerDevice": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_batteryChemistryConnectFirst": "デバイスを選択するために接続する",
+ "appSettings_batteryNmc": "18650型 NMC (3.0-4.2V)",
+ "appSettings_batteryLifepo4": "LiFePO4 (2.6-3.65V)",
+ "appSettings_batteryLipo": "LiPo (3.0-4.2V)",
+ "appSettings_mapDisplay": "地図の表示",
+ "appSettings_showRepeaters": "繰り返し再生機能",
+ "appSettings_showRepeatersSubtitle": "地図上にリピーターノードを表示する",
+ "appSettings_showChatNodes": "チャットノードの表示",
+ "appSettings_showChatNodesSubtitle": "地図上にチャットノードを表示する",
+ "appSettings_showOtherNodes": "他のノードを表示する",
+ "appSettings_showOtherNodesSubtitle": "地図上に、他のノードの種類を表示する",
+ "appSettings_timeFilter": "時間フィルター",
+ "appSettings_timeFilterShowAll": "すべてのノードを表示する",
+ "appSettings_timeFilterShowLast": "過去 {hours} 時間のノードを表示する",
+ "@appSettings_timeFilterShowLast": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_mapTimeFilter": "地図の表示期間を絞り込む",
+ "appSettings_showNodesDiscoveredWithin": "以下の範囲内で発見されたノードを表示する:",
+ "appSettings_allTime": "すべての期間",
+ "appSettings_lastHour": "直前の",
+ "appSettings_last6Hours": "過去6時間",
+ "appSettings_last24Hours": "過去24時間",
+ "appSettings_lastWeek": "先週",
+ "appSettings_offlineMapCache": "オフライン用地図キャッシュ",
+ "appSettings_unitsTitle": "単位",
+ "appSettings_unitsMetric": "メートル (m) / キロメートル (km)",
+ "appSettings_unitsImperial": "帝国 (フィート / マイル)",
+ "appSettings_noAreaSelected": "選択されたエリアはありません",
+ "appSettings_areaSelectedZoom": "選択された範囲(ズームレベル:{minZoom}~{maxZoom})",
+ "@appSettings_areaSelectedZoom": {
+ "placeholders": {
+ "minZoom": {
+ "type": "int"
+ },
+ "maxZoom": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_debugCard": "デバッグ",
+ "appSettings_appDebugLogging": "アプリケーションのデバッグ用ログ",
+ "appSettings_appDebugLoggingSubtitle": "ログアプリのデバッグメッセージ(トラブルシューティング用)",
+ "appSettings_appDebugLoggingEnabled": "アプリケーションのデバッグ用ログ機能が有効になっています。",
+ "appSettings_appDebugLoggingDisabled": "アプリケーションのデバッグログが無効化されています。",
+ "contacts_title": "連絡先",
+ "contacts_noContacts": "現時点では、連絡先はまだありません。",
+ "contacts_contactsWillAppear": "デバイスが広告を行う際に、連絡先が表示されます。",
+ "contacts_unread": "未読",
+ "contacts_searchContactsNoNumber": "連絡先を検索...",
+ "contacts_searchContacts": "{number}件の{str}に関する連絡先を検索...",
+ "@contacts_searchContacts": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchFavorites": "{number}件の{str}を検索...",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "{number}件の{str}に関するユーザーを検索する...",
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "{number} {str} までの検索...",
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "{number} {str} 部屋のサーバーを検索する...",
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_noUnreadContacts": "未読の連絡先はありません",
+ "contacts_noContactsFound": "連絡先またはグループは見つかりませんでした。",
+ "contacts_deleteContact": "連絡先を削除",
+ "contacts_removeConfirm": "{contactName} を連絡先から削除しますか?",
+ "@contacts_removeConfirm": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_manageRepeater": "リピーターの管理",
+ "contacts_manageRoom": "ルームサーバーの管理",
+ "contacts_roomLogin": "ルームサーバーへのログイン",
+ "contacts_openChat": "自由な会話",
+ "contacts_editGroup": "編集グループ",
+ "contacts_deleteGroup": "グループを削除",
+ "contacts_deleteGroupConfirm": "{groupName} を削除しますか?",
+ "@contacts_deleteGroupConfirm": {
+ "placeholders": {
+ "groupName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_newGroup": "新しいグループ",
+ "contacts_groupName": "グループ名",
+ "contacts_groupNameRequired": "グループ名が必須です",
+ "contacts_groupNameReserved": "このグループ名はすでに使用されています。",
+ "contacts_groupAlreadyExists": "グループ「{name}」はすでに存在しています",
+ "@contacts_groupAlreadyExists": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_filterContacts": "連絡先をフィルタリングする…",
+ "contacts_noContactsMatchFilter": "指定された条件に合致する連絡先は見つかりませんでした。",
+ "contacts_noMembers": "メンバーはいない",
+ "contacts_lastSeenNow": "最近",
+ "contacts_lastSeenMinsAgo": "~{minutes} 分",
+ "@contacts_lastSeenMinsAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenHourAgo": "約1時間",
+ "contacts_lastSeenHoursAgo": "~ {hours} 時間",
+ "@contacts_lastSeenHoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenDayAgo": "~1日",
+ "contacts_lastSeenDaysAgo": "~{days}日間",
+ "@contacts_lastSeenDaysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_title": "チャンネル",
+ "channels_noChannelsConfigured": "設定されたチャンネルがありません",
+ "channels_addPublicChannel": "パブリックチャンネルを追加する",
+ "channels_searchChannels": "検索オプション...",
+ "channels_noChannelsFound": "チャンネルが見つかりませんでした",
+ "channels_channelIndex": "チャンネル {index}",
+ "@channels_channelIndex": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_hashtagChannel": "ハッシュタグチャンネル",
+ "channels_public": "一般の人々",
+ "channels_private": "個人の",
+ "channels_publicChannel": "一般チャンネル",
+ "channels_privateChannel": "プライベートチャンネル",
+ "channels_editChannel": "チャンネルを編集する",
+ "channels_muteChannel": "ミュート機能",
+ "channels_unmuteChannel": "ミュートを解除する",
+ "channels_deleteChannel": "チャンネルを削除する",
+ "channels_deleteChannelConfirm": "{name} を削除しますか? これは取り消すことができません。",
+ "@channels_deleteChannelConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleteFailed": "チャンネル「{name}」の削除に失敗しました。",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleted": "チャンネル「{name}」が削除されました",
+ "@channels_channelDeleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_addChannel": "チャンネルを追加",
+ "channels_channelIndexLabel": "チャンネルインデックス",
+ "channels_channelName": "チャンネル名",
+ "channels_usePublicChannel": "パブリックチャンネルを使用する",
+ "channels_standardPublicPsk": "標準的な公用 PSK",
+ "channels_pskHex": "PSK (ヘックス)",
+ "channels_generateRandomPsk": "ランダムなPSK(正交符号分割変調)を生成する",
+ "channels_enterChannelName": "チャンネル名を入力してください",
+ "channels_pskMustBe32Hex": "PSKは32桁の16進数で構成されている必要があります。",
+ "channels_channelAdded": "チャンネル「{name}」を追加",
+ "@channels_channelAdded": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_editChannelTitle": "チャンネル {index} の編集",
+ "@channels_editChannelTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_smazCompression": "SMAZ 圧縮",
+ "channels_channelUpdated": "チャンネル「{name}」が更新されました",
+ "@channels_channelUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_publicChannelAdded": "パブリックチャンネルが追加されました",
+ "channels_sortBy": "並び替え",
+ "channels_sortManual": "マニュアル",
+ "channels_sortAZ": "AからZ",
+ "channels_sortLatestMessages": "最新のメッセージ",
+ "channels_sortUnread": "未読",
+ "channels_createPrivateChannel": "プライベートチャンネルを作成する",
+ "channels_createPrivateChannelDesc": "秘密鍵を使用して保護されています。",
+ "channels_joinPrivateChannel": "プライベートチャンネルに参加する",
+ "channels_joinPrivateChannelDesc": "手動で秘密のキーを入力する。",
+ "channels_joinPublicChannel": "公開チャンネルに参加する",
+ "channels_joinPublicChannelDesc": "このチャンネルには、誰でも参加できます。",
+ "channels_joinHashtagChannel": "ハッシュタグチャンネルに参加する",
+ "channels_joinHashtagChannelDesc": "誰でもハッシュタグチャンネルに参加できます。",
+ "channels_scanQrCode": "QRコードをスキャンする",
+ "channels_scanQrCodeComingSoon": "近日公開",
+ "channels_enterHashtag": "ハッシュタグを入力してください",
+ "channels_hashtagHint": "例:#チーム",
+ "chat_noMessages": "まだメッセージは届いていません",
+ "chat_sendMessageToStart": "開始するためにメッセージを送信してください",
+ "chat_originalMessageNotFound": "元のメッセージが見つかりませんでした",
+ "chat_replyingTo": "{name} への返信",
+ "@chat_replyingTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_replyTo": "{name}への返信",
+ "@chat_replyTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_location": "場所",
+ "chat_sendMessageTo": "{contactName} へのメッセージを送信する",
+ "@chat_sendMessageTo": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_typeMessage": "メッセージを入力してください…",
+ "chat_messageTooLong": "メッセージが長すぎる({maxBytes} バイトを超える)。",
+ "@chat_messageTooLong": {
+ "placeholders": {
+ "maxBytes": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_messageCopied": "メッセージがコピーされました",
+ "chat_messageDeleted": "メッセージは削除されました",
+ "chat_retryingMessage": "再試行メッセージ",
+ "chat_retryCount": "{current} / {max} 回目",
+ "@chat_retryCount": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendGif": "GIFを送信する",
+ "chat_reply": "返信",
+ "chat_addReaction": "反応を追加",
+ "chat_me": "私",
+ "emojiCategorySmileys": "笑顔の絵文字",
+ "emojiCategoryGestures": "身振り、動作",
+ "emojiCategoryHearts": "心",
+ "emojiCategoryObjects": "対象物",
+ "gifPicker_title": "GIF を選択してください",
+ "gifPicker_searchHint": "GIFの検索...",
+ "gifPicker_poweredBy": "GIPHYによる提供",
+ "gifPicker_noGifsFound": "GIF形式のファイルは見つかりませんでした",
+ "gifPicker_failedLoad": "GIFファイルの読み込みに失敗しました",
+ "gifPicker_failedSearch": "GIFファイルの検索に失敗しました",
+ "gifPicker_noInternet": "インターネット接続なし",
+ "debugLog_appTitle": "アプリケーションのデバッグログ",
+ "debugLog_bleTitle": "BLE デバッグログ",
+ "debugLog_copyLog": "記録",
+ "debugLog_clearLog": "詳細なログ",
+ "debugLog_copied": "デバッグログをコピー",
+ "debugLog_bleCopied": "BLEログのコピー",
+ "debugLog_noEntries": "デバッグログはまだ生成されていません",
+ "debugLog_enableInSettings": "アプリのデバッグログを有効にするには、設定から操作してください。",
+ "debugLog_frames": "フレーム",
+ "debugLog_rawLogRx": "生のログ-RX",
+ "debugLog_noBleActivity": "現時点では、BLE関連の活動は行われていません。",
+ "debugFrame_length": "フレーム長: {count} バイト",
+ "@debugFrame_length": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_command": "コマンド: 0x{value}",
+ "@debugFrame_command": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textMessageHeader": "テキストメッセージ用フレーム:",
+ "debugFrame_destinationPubKey": "- 宛先公開鍵: {pubKey}",
+ "@debugFrame_destinationPubKey": {
+ "placeholders": {
+ "pubKey": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_timestamp": "- タイムスタンプ: {timestamp}",
+ "@debugFrame_timestamp": {
+ "placeholders": {
+ "timestamp": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_flags": "- フラグ: 0x{value}",
+ "@debugFrame_flags": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textType": "- テキストの種類: {type} ({label})",
+ "@debugFrame_textType": {
+ "placeholders": {
+ "type": {
+ "type": "int"
+ },
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textTypeCli": "CLI(コマンドラインインターフェース)",
+ "debugFrame_textTypePlain": "シンプルな",
+ "debugFrame_text": "- テキスト:「{text}」",
+ "@debugFrame_text": {
+ "placeholders": {
+ "text": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_hexDump": "ヘックスダンプ:",
+ "chat_pathManagement": "経路管理",
+ "chat_ShowAllPaths": "すべての経路を表示",
+ "chat_routingMode": "ルーティングモード",
+ "chat_autoUseSavedPath": "自動 (保存されたパスを使用)",
+ "chat_forceFloodMode": "強制的に洪水モードを起動",
+ "chat_recentAckPaths": "最近使用したACKパス(タップして使用):",
+ "chat_pathHistoryFull": "パスの履歴は完全です。エントリを削除して、新しいものを追加できます。",
+ "chat_hopSingular": "ジャンプ",
+ "chat_hopPlural": "ホップ",
+ "chat_hopsCount": "{count} {count, plural, =1{ホップ} other{ホップ}}",
+ "@chat_hopsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_successes": "成功事例",
+ "chat_removePath": "パスを削除する",
+ "chat_noPathHistoryYet": "まだ履歴はありません。\nパスを特定するためにメッセージを送信してください。",
+ "chat_pathActions": "パスの操作:",
+ "chat_setCustomPath": "カスタムパスを設定",
+ "chat_setCustomPathSubtitle": "手動で経路を指定する",
+ "chat_clearPath": "明確な道",
+ "chat_clearPathSubtitle": "次回送信時に、以前の情報を再取得する",
+ "chat_pathCleared": "経路が確保されました。次のメッセージでルートを再確認します。",
+ "chat_floodModeSubtitle": "アプリのバーにあるルーティング切り替え機能を使用する",
+ "chat_floodModeEnabled": "洪水モードが有効になっています。アプリのメニューバーにあるルートアイコンを使用して、モードを切り替えることができます。",
+ "chat_fullPath": "フルパス",
+ "chat_pathDetailsNotAvailable": "経路の詳細については、まだ情報がありません。「リフレッシュ」ボタンを押して、再度お試しください。",
+ "chat_pathSetHops": "Path set: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
+ "@chat_pathSetHops": {
+ "placeholders": {
+ "hopCount": {
+ "type": "int"
+ },
+ "status": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_pathSavedLocally": "ローカルで保存。同期のために接続する。",
+ "chat_pathDeviceConfirmed": "デバイスの確認済み。",
+ "chat_pathDeviceNotConfirmed": "デバイスの確認はまだできていません。",
+ "chat_type": "種類",
+ "chat_path": "道",
+ "chat_publicKey": "公開鍵",
+ "chat_compressOutgoingMessages": "送信されるメッセージを圧縮する",
+ "chat_floodForced": "洪水(強制的な)",
+ "chat_directForced": "直接的な(強制的な)",
+ "chat_hopsForced": "{count} 本のホップ(強制的に採取)",
+ "@chat_hopsForced": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_floodAuto": "洪水 (自動)",
+ "chat_direct": "直接",
+ "chat_poiShared": "共有されたPOI",
+ "chat_unread": "未読: {count}",
+ "@chat_unread": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_openLink": "リンクを開く?",
+ "chat_openLinkConfirmation": "このリンクをブラウザで開くことはご希望ですか?",
+ "chat_open": "開く",
+ "chat_couldNotOpenLink": "リンクを開けられませんでした: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "無効なリンク形式",
+ "map_title": "ノードマップ",
+ "map_lineOfSight": "視界",
+ "map_losScreenTitle": "視界",
+ "map_noNodesWithLocation": "位置情報データを持つノードは存在しません",
+ "map_nodesNeedGps": "ノードは、地図上に表示されるために、GPS座標を共有する必要があります。",
+ "map_nodesCount": "ノード:{count}",
+ "@map_nodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_pinsCount": "ピン:{count}個",
+ "@map_pinsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_chat": "チャット",
+ "map_repeater": "繰り返し送信装置",
+ "map_room": "部屋",
+ "map_sensor": "センサー",
+ "map_pinDm": "ピン(DM)",
+ "map_pinPrivate": "プライベート(非公開)",
+ "map_pinPublic": "公開 (一般公開)",
+ "map_lastSeen": "最後に確認された場所",
+ "map_disconnectConfirm": "本当にこのデバイスとの接続を解除したいですか?",
+ "map_from": "~から",
+ "map_source": "出典",
+ "map_flags": "旗",
+ "map_shareMarkerHere": "この場所でシェア",
+ "map_setAsMyLocation": "現在地として設定",
+ "map_pinLabel": "ピンラベル",
+ "map_label": "ラベル",
+ "map_pointOfInterest": "注目すべき点",
+ "map_sendToContact": "連絡先へ送信",
+ "map_sendToChannel": "特定のチャンネルに送信する",
+ "map_noChannelsAvailable": "利用可能なチャンネルはありません",
+ "map_publicLocationShare": "公共スペースの共有",
+ "map_publicLocationShareConfirm": "現在、{channelLabel} で位置情報を共有する準備をしています。このチャンネルは公開されており、PSK を持つ誰でも閲覧できます。",
+ "@map_publicLocationShareConfirm": {
+ "placeholders": {
+ "channelLabel": {
+ "type": "String"
+ }
+ }
+ },
+ "map_connectToShareMarkers": "他のデバイスと接続して、マーカーを共有する",
+ "map_filterNodes": "フィルタノード",
+ "map_nodeTypes": "ノードの種類",
+ "map_chatNodes": "チャットノード",
+ "map_repeaters": "繰り返し送信装置",
+ "map_otherNodes": "その他のノード",
+ "map_keyPrefix": "主要なプレフィックス",
+ "map_filterByKeyPrefix": "主要なプレフィックスでフィルタリングする",
+ "map_publicKeyPrefix": "公開鍵のプレフィックス",
+ "map_markers": "マーカー",
+ "map_showSharedMarkers": "共有のマーカーを表示する",
+ "map_showGuessedLocations": "推測されたノードの位置を表示する",
+ "map_showDiscoveryContacts": "Discovery社の連絡先を表示する",
+ "map_guessedLocation": "推測された場所",
+ "map_lastSeenTime": "最後に確認された時間",
+ "map_sharedPin": "共有パスワード",
+ "map_joinRoom": "部屋に参加する",
+ "map_manageRepeater": "リピーターの管理",
+ "map_tapToAdd": "ノードをクリックして、パスに追加します。",
+ "map_runTrace": "パスの追跡を実行",
+ "map_removeLast": "最後のものを削除",
+ "map_pathTraceCancelled": "パスの追跡は中止。",
+ "mapCache_title": "オフライン用地図キャッシュ",
+ "mapCache_selectAreaFirst": "最初にキャッシュする領域を選択してください",
+ "mapCache_noTilesToDownload": "この地域にはダウンロードできるタイルは存在しません。",
+ "mapCache_downloadTilesTitle": "タイルをダウンロードする",
+ "mapCache_downloadTilesPrompt": "オフラインでの使用のために、{count}個のタイルをダウンロードしますか?",
+ "@mapCache_downloadTilesPrompt": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadAction": "ダウンロード",
+ "mapCache_cachedTiles": "{count} 個のタイルをキャッシュ",
+ "@mapCache_cachedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_cachedTilesWithFailed": "Cached {downloaded} tiles ({failed} failed)",
+ "@mapCache_cachedTilesWithFailed": {
+ "placeholders": {
+ "downloaded": {
+ "type": "int"
+ },
+ "failed": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_clearOfflineCacheTitle": "オフラインキャッシュをクリアする",
+ "mapCache_clearOfflineCachePrompt": "キャッシュされた地図のタイルをすべて削除しますか?",
+ "mapCache_offlineCacheCleared": "オフラインキャッシュをクリア",
+ "mapCache_noAreaSelected": "選択されたエリアはありません",
+ "mapCache_cacheArea": "キャッシュエリア",
+ "mapCache_useCurrentView": "現在表示されている内容を保持する",
+ "mapCache_zoomRange": "ズーム範囲",
+ "mapCache_estimatedTiles": "推定されるタイル数: {count}",
+ "@mapCache_estimatedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadedTiles": "Downloaded {completed} / {total}",
+ "@mapCache_downloadedTiles": {
+ "placeholders": {
+ "completed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadTilesButton": "タイルをダウンロードする",
+ "mapCache_clearCacheButton": "キャッシュをクリアする",
+ "mapCache_failedDownloads": "失敗したダウンロード: {count}",
+ "@mapCache_failedDownloads": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
+ "@mapCache_boundsLabel": {
+ "placeholders": {
+ "north": {
+ "type": "String"
+ },
+ "south": {
+ "type": "String"
+ },
+ "east": {
+ "type": "String"
+ },
+ "west": {
+ "type": "String"
+ }
+ }
+ },
+ "time_justNow": "まさに今",
+ "time_minutesAgo": "{minutes}分前",
+ "@time_minutesAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hoursAgo": "{hours}時間前",
+ "@time_hoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "time_daysAgo": "{days}日前",
+ "@time_daysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hour": "1時間",
+ "time_hours": "時間",
+ "time_day": "一日",
+ "time_days": "日",
+ "time_week": "1週間",
+ "time_weeks": "週",
+ "time_month": "月",
+ "time_months": "月",
+ "time_minutes": "分",
+ "time_allTime": "全期間",
+ "dialog_disconnect": "切断する",
+ "dialog_disconnectConfirm": "本当にこのデバイスとの接続を解除したいですか?",
+ "login_repeaterLogin": "再ログイン",
+ "login_roomLogin": "ルームサーバーへのログイン",
+ "login_password": "パスワード",
+ "login_enterPassword": "パスワードを入力してください",
+ "login_savePassword": "パスワードを保存する",
+ "login_savePasswordSubtitle": "パスワードは、このデバイスに安全に保存されます。",
+ "login_repeaterDescription": "設定やステータスにアクセスするために、リピーターのパスワードを入力してください。",
+ "login_roomDescription": "設定やステータスへのアクセスには、部屋のパスワードを入力してください。",
+ "login_routing": "経路設定",
+ "login_routingMode": "ルーティングモード",
+ "login_autoUseSavedPath": "自動 (保存されたパスを使用)",
+ "login_forceFloodMode": "強制的に洪水モードを起動",
+ "login_managePaths": "パスの管理",
+ "login_login": "ログイン",
+ "login_attempt": "試行回数:{current}/{max}",
+ "@login_attempt": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "login_failed": "ログインに失敗しました:{error}",
+ "@login_failed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "login_failedMessage": "ログインに失敗しました。パスワードが間違っているか、または接続が確立されていません。",
+ "common_reload": "再読み込み",
+ "common_clear": "明確",
+ "path_currentPath": "現在のパス: {path}",
+ "@path_currentPath": {
+ "placeholders": {
+ "path": {
+ "type": "String"
+ }
+ }
+ },
+ "path_usingHopsPath": "{count} {count, plural, =1{ホップ} other{ホップ}}のパスを使用",
+ "@path_usingHopsPath": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "path_enterCustomPath": "カスタムパスを入力",
+ "path_currentPathLabel": "現在の経路",
+ "path_hexPrefixInstructions": "各ホップに対して、2文字の16進数プレフィックスをカンマで区切って入力してください。",
+ "path_hexPrefixExample": "例:A1, F2, 3C (各ノードは、自身の公開鍵の最初のバイトを使用)",
+ "path_labelHexPrefixes": "パス (ヘックスプレフィックス)",
+ "path_helperMaxHops": "最大64個のホップ。各プレフィックスは2つの16進数文字(1バイト)で構成されています。",
+ "path_selectFromContacts": "または、連絡先リストから選択してください:",
+ "path_noRepeatersFound": "繰り返し機能やルームサーバーは見つかりませんでした。",
+ "path_customPathsRequire": "カスタムパスには、メッセージを中継できる中間地点が必要です。",
+ "path_invalidHexPrefixes": "無効な16進数プレフィックス: {prefixes}",
+ "@path_invalidHexPrefixes": {
+ "placeholders": {
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "path_tooLong": "経路が長すぎる。最大64回のジャンプのみ許可。",
+ "path_setPath": "パスを設定",
+ "repeater_management": "リピーター管理",
+ "room_management": "ルームサーバーの管理",
+ "repeater_managementTools": "管理ツール",
+ "repeater_status": "ステータス",
+ "repeater_statusSubtitle": "リピーターの状態、統計情報、および隣接するネットワークの情報を表示する",
+ "repeater_telemetry": "テレメトリー",
+ "repeater_telemetrySubtitle": "センサーおよびシステムの状態に関するテレメトリの表示",
+ "repeater_cli": "CLI(コマンドラインインターフェース)",
+ "repeater_cliSubtitle": "リピーターへのコマンドを送信する",
+ "repeater_neighbors": "近隣住民",
+ "repeater_neighborsSubtitle": "ゼロホップの隣接ノードを表示する。",
+ "repeater_settings": "設定",
+ "repeater_settingsSubtitle": "リピーターのパラメータを設定する",
+ "repeater_statusTitle": "再送ステータス",
+ "repeater_routingMode": "ルーティングモード",
+ "repeater_autoUseSavedPath": "自動 (保存されたパスを使用)",
+ "repeater_forceFloodMode": "強制的に洪水モードを起動",
+ "repeater_pathManagement": "経路管理",
+ "repeater_refresh": "リフレッシュ",
+ "repeater_statusRequestTimeout": "ステータス情報の取得に失敗しました。",
+ "repeater_errorLoadingStatus": "ステータス読み込みエラー: {error}",
+ "@repeater_errorLoadingStatus": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_systemInformation": "システム情報",
+ "repeater_battery": "バッテリー",
+ "repeater_clockAtLogin": "ログイン時の時刻表示",
+ "repeater_uptime": "稼働率",
+ "repeater_queueLength": "待ち行列の長さ",
+ "repeater_debugFlags": "デバッグフラグ",
+ "repeater_radioStatistics": "ラジオに関する統計",
+ "repeater_lastRssi": "最後のRSSI",
+ "repeater_lastSnr": "最後のSNR",
+ "repeater_noiseFloor": "ノイズレベル",
+ "repeater_txAirtime": "TXの放送時間",
+ "repeater_rxAirtime": "RX 空き時間",
+ "repeater_packetStatistics": "パケット統計",
+ "repeater_sent": "送信",
+ "repeater_received": "受領",
+ "repeater_duplicates": "重複",
+ "repeater_daysHoursMinsSecs": "{days}日 {hours}時間 {minutes}分 {seconds}秒",
+ "@repeater_daysHoursMinsSecs": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_packetTxTotal": "合計: {total}, 洪水: {flood}, 直接: {direct}",
+ "@repeater_packetTxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_packetRxTotal": "合計: {total}, 洪水: {flood}, 直接: {direct}",
+ "@repeater_packetRxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesFloodDirect": "{flood}: {flood}, 直接: {direct}",
+ "@repeater_duplicatesFloodDirect": {
+ "placeholders": {
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesTotal": "合計: {total}",
+ "@repeater_duplicatesTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_settingsTitle": "リピーター設定",
+ "repeater_basicSettings": "基本設定",
+ "repeater_repeaterName": "送信装置名",
+ "repeater_repeaterNameHelper": "このリピーターの名前",
+ "repeater_adminPassword": "管理者パスワード",
+ "repeater_adminPasswordHelper": "完全アクセス権のパスワード",
+ "repeater_guestPassword": "ゲスト用のパスワード",
+ "repeater_guestPasswordHelper": "読み取り専用アクセス用のパスワード",
+ "repeater_radioSettings": "ラジオ設定",
+ "repeater_frequencyMhz": "周波数 (MHz)",
+ "repeater_frequencyHelper": "300~2500 MHz",
+ "repeater_txPower": "TXパワー",
+ "repeater_txPowerHelper": "-30~-10 dBm",
+ "repeater_bandwidth": "帯域幅",
+ "repeater_spreadingFactor": "伝播係数",
+ "repeater_codingRate": "コーディング速度",
+ "repeater_locationSettings": "場所設定",
+ "repeater_latitude": "緯度",
+ "repeater_latitudeHelper": "度分表記(例:37.7749)",
+ "repeater_longitude": "経度",
+ "repeater_longitudeHelper": "度分表記(例:-122.4194)",
+ "repeater_features": "特徴",
+ "repeater_packetForwarding": "パケット転送",
+ "repeater_packetForwardingSubtitle": "リピーターがパケットを転送できるように設定する",
+ "repeater_guestAccess": "ゲストへのアクセス",
+ "repeater_guestAccessSubtitle": "ゲストへの読み取り専用アクセスを許可する",
+ "repeater_privacyMode": "プライバシーモード",
+ "repeater_privacyModeSubtitle": "広告に名前/場所を記載しない",
+ "repeater_advertisementSettings": "広告設定",
+ "repeater_localAdvertInterval": "地域広告掲載期間",
+ "repeater_localAdvertIntervalMinutes": "{minutes} 分",
+ "@repeater_localAdvertIntervalMinutes": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_floodAdvertInterval": "洪水に関する広告の表示間隔",
+ "repeater_floodAdvertIntervalHours": "{hours} 時間",
+ "@repeater_floodAdvertIntervalHours": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_encryptedAdvertInterval": "暗号化された広告表示間",
+ "repeater_dangerZone": "危険区域",
+ "repeater_rebootRepeater": "リピーターを再起動する",
+ "repeater_rebootRepeaterSubtitle": "リピーターデバイスを再起動する",
+ "repeater_rebootRepeaterConfirm": "本当にこのリピーターを再起動したいですか?",
+ "repeater_regenerateIdentityKey": "IDキーの再生成",
+ "repeater_regenerateIdentityKeySubtitle": "新しい公開鍵/秘密鍵のペアを生成する",
+ "repeater_regenerateIdentityKeyConfirm": "これにより、リピーターには新しい識別情報が割り当てられます。続行しますか?",
+ "repeater_eraseFileSystem": "ファイルシステムを削除する",
+ "repeater_eraseFileSystemSubtitle": "リピーターファイルシステムをフォーマットする",
+ "repeater_eraseFileSystemConfirm": "警告:この操作により、リピーター内のすべてのデータが消去されます。この操作は元に戻すことができません!",
+ "repeater_eraseSerialOnly": "Erase機能は、シリアルコンソール経由でのみ利用可能です。",
+ "repeater_commandSent": "送信されたコマンド: {command}",
+ "@repeater_commandSent": {
+ "placeholders": {
+ "command": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorSendingCommand": "コマンド送信エラー:{error}",
+ "@repeater_errorSendingCommand": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_confirm": "確認",
+ "repeater_settingsSaved": "設定が正常に保存されました",
+ "repeater_errorSavingSettings": "設定の保存に失敗しました:{error}",
+ "@repeater_errorSavingSettings": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_refreshBasicSettings": "基本設定をリセットする",
+ "repeater_refreshRadioSettings": "ラジオ設定をリセットする",
+ "repeater_refreshTxPower": "TX の電力レベルをリセットする",
+ "repeater_refreshLocationSettings": "場所設定をリセットする",
+ "repeater_refreshPacketForwarding": "パケット転送の刷新",
+ "repeater_refreshGuestAccess": "ゲストへのアクセスをリフレッシュする",
+ "repeater_refreshPrivacyMode": "プライバシーモードをリセットする",
+ "repeater_refreshAdvertisementSettings": "広告設定のリセット",
+ "repeater_refreshed": "{label} が更新されました",
+ "@repeater_refreshed": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorRefreshing": "{label} の更新に失敗しました",
+ "@repeater_errorRefreshing": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliTitle": "リピーターのコマンドラインインターフェース",
+ "repeater_debugNextCommand": "次のコマンドのデバッグ",
+ "repeater_commandHelp": "コマンドヘルプ",
+ "repeater_clearHistory": "明確な歴史",
+ "repeater_noCommandsSent": "まだコマンドは送信されていません",
+ "repeater_typeCommandOrUseQuick": "以下のコマンドを入力するか、クイックコマンドを使用してください。",
+ "repeater_enterCommandHint": "コマンドを入力してください...",
+ "repeater_previousCommand": "直前の指示",
+ "repeater_nextCommand": "次の指示",
+ "repeater_enterCommandFirst": "まず、コマンドを入力してください。",
+ "repeater_cliCommandFrameTitle": "CLI コマンドフレーム",
+ "repeater_cliCommandError": "エラー:{error}",
+ "@repeater_cliCommandError": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliQuickGetName": "名前を取得する",
+ "repeater_cliQuickGetRadio": "ラジオを聴く",
+ "repeater_cliQuickGetTx": "TXを入手する",
+ "repeater_cliQuickNeighbors": "近隣住民",
+ "repeater_cliQuickVersion": "バージョン",
+ "repeater_cliQuickAdvertise": "広告",
+ "repeater_cliQuickClock": "時計",
+ "repeater_cliHelpAdvert": "広告用資料を送る",
+ "repeater_cliHelpReboot": "デバイスを再起動します。(注:通常は「タイムアウト」が表示されますが、これは正常です)",
+ "repeater_cliHelpClock": "各デバイスの時計で現在の時刻を表示します。",
+ "repeater_cliHelpPassword": "デバイス用の新しい管理者パスワードを設定します。",
+ "repeater_cliHelpVersion": "デバイスのバージョンとファームウェアのビルド日を表示します。",
+ "repeater_cliHelpClearStats": "さまざまな統計カウンターをゼロにリセットする。",
+ "repeater_cliHelpSetAf": "空き時間係数を設定します。",
+ "repeater_cliHelpSetTx": "LoRaの送信電力をdBmで設定します。(設定変更後、再起動が必要です)",
+ "repeater_cliHelpSetRepeat": "このノードに対するリピーターの役割を有効化または無効化します。",
+ "repeater_cliHelpSetAllowReadOnly": "(ルームサーバー設定)「オン」に設定した場合、空白のパスワードでのログインは可能ですが、ルームへの投稿はできません。(閲覧のみ)",
+ "repeater_cliHelpSetFloodMax": "インバウンドフラッパケットの最大ホップ数を設定します(最大値を超えた場合、パケットは転送されません)。",
+ "repeater_cliHelpSetIntThresh": "干渉閾値を設定します(dB単位)。デフォルト値は14です。0に設定すると、チャンネル間の干渉を検出する機能を無効にします。",
+ "repeater_cliHelpSetAgcResetInterval": "オートゲインコントローラーのリセット間隔を設定します。 0 に設定すると無効化されます。",
+ "repeater_cliHelpSetMultiAcks": "「ダブルACK」機能の有効化または無効化を可能にします。",
+ "repeater_cliHelpSetAdvertInterval": "ローカル(ホップなし)の広告パケットを送信する間隔を分単位で設定します。 0 に設定すると、機能を無効にします。",
+ "repeater_cliHelpSetFloodAdvertInterval": "洪水広告の送信間隔を時間単位で設定します。0に設定すると、送信を停止します。",
+ "repeater_cliHelpSetGuestPassword": "ゲストのパスワードを設定/更新します。(繰り返し利用の場合、ゲストのログインは「統計情報を取得」のリクエストを送信できます)",
+ "repeater_cliHelpSetName": "広告の名前を設定します。",
+ "repeater_cliHelpSetLat": "広告表示の地図の緯度を設定します。(度分秒表記)",
+ "repeater_cliHelpSetLon": "広告表示の地図の経度を設定します。(度数、分)",
+ "repeater_cliHelpSetRadio": "完全に新しいラジオパラメータを設定し、設定として保存します。適用するには、「再起動」コマンドが必要です。",
+ "repeater_cliHelpSetRxDelay": "(実験用)遅延時間を設定するためのベース(1以上の値に設定する必要)\n受信パケットに対して、信号強度/スコアに基づいてわずかな遅延を適用します。 0に設定すると無効化されます。",
+ "repeater_cliHelpSetTxDelay": "時間経過に応じた「フラッシュモード」パケットの送信遅延を設定します。この遅延は、ランダムなスロットシステムと組み合わせて使用され、パケットの衝突を減らすことを目的としています。",
+ "repeater_cliHelpSetDirectTxDelay": "txdelayと同様ですが、ダイレクトモードのパケット転送にランダムな遅延を適用する場合に使用します。",
+ "repeater_cliHelpSetBridgeEnabled": "ブリッジを有効化/無効化",
+ "repeater_cliHelpSetBridgeDelay": "パケットを再送信する前に、遅延を設定する。",
+ "repeater_cliHelpSetBridgeSource": "橋が受信したパケットを再送信するか、送信したパケットを再送信するかどうかを選択してください。",
+ "repeater_cliHelpSetBridgeBaud": "RS232 橋渡しに使用するシリアルリンクのボーレートを設定する。",
+ "repeater_cliHelpSetBridgeSecret": "ESPNow 橋の秘密設定",
+ "repeater_cliHelpSetAdcMultiplier": "特定のボードでのみサポートされている、報告されるバッテリー電圧を調整するためのカスタムファクタを設定できます。",
+ "repeater_cliHelpTempRadio": "指定された時間(分単位)に対して、一時的にラジオパラメータを設定し、その後元のラジオパラメータに戻します。(設定を保存しません)。",
+ "repeater_cliHelpSetPerm": "ACL を変更します。「permissions」が 0 の場合、対応するエントリ(pubkey のプレフィックスで識別)を削除します。pubkey-hex が有効な長さで、かつ ACL に現在存在しない場合に、新しいエントリを追加します。pubkey のプレフィックスと一致するエントリを更新します。権限ビットはファームウェアの役割によって異なり、下位 2 ビットは以下のとおりです:0 (ゲスト)、1 (読み取り専用)、2 (読み書き)、3 (管理者)",
+ "repeater_cliHelpGetBridgeType": "ブリッジ機能なし、RS232、ESPNow",
+ "repeater_cliHelpLogStart": "パケットのログ記録を開始し、ファイルシステムに保存する。",
+ "repeater_cliHelpLogStop": "ファイルシステムへのパケットログの記録を停止する。",
+ "repeater_cliHelpLogErase": "ファイルシステムからパケットログを削除する。",
+ "repeater_cliHelpNeighbors": "ゼロホップ広告を通じて受信した他のリピーターノードの一覧を表示します。各行は、IDプレフィックス(16進数)、タイムスタンプ、SNR(シグナル強度)の情報を4つ含みます。",
+ "repeater_cliHelpNeighborRemove": "隣接リストから、最初に一致するエントリ(pubkeyプレフィックス(16進数)で特定)を削除します。",
+ "repeater_cliHelpRegion": "(特定のシリーズのみ)定義されたすべての地域と、現在の洪水許可状況を一覧表示します。",
+ "repeater_cliHelpRegionLoad": "注:これは特殊な複数コマンドの呼び出しです。その後の各コマンドは、地域名であり(スペースを使用して親階層を示し、少なくとも1つのスペースが必要です)、空行/コマンドで終了します。",
+ "repeater_cliHelpRegionGet": "指定された名前のプレフィックスを持つ地域を検索します(または、グローバルな範囲の場合は「*」)。結果として、「region-name (parent-name) 'F'」と返答します。",
+ "repeater_cliHelpRegionPut": "指定された名前で、領域の定義を追加または更新します。",
+ "repeater_cliHelpRegionRemove": "指定された名前を持つ領域の定義を削除します。(正確に一致している必要があり、子領域は存在してはなりません)",
+ "repeater_cliHelpRegionAllowf": "指定された領域に対して、「洪水」アクセス許可を設定します。 (グローバル/従来のスコープには「*」を使用)",
+ "repeater_cliHelpRegionDenyf": "指定された領域における「FLOOD」権限を削除します。(注:現時点では、グローバル/従来の範囲での使用は推奨されません!)",
+ "repeater_cliHelpRegionHome": "現在の「ホーム」地域に返信します。(まだ適用されていない、将来利用を予定)",
+ "repeater_cliHelpRegionHomeSet": "「ホーム」地域を設定します。",
+ "repeater_cliHelpRegionSave": "領域リスト/マップをストレージに保存する。",
+ "repeater_cliHelpGps": "GPSの状態を表示します。GPSがオフの場合、「オフ」と表示します。オンの場合、「オン」、「ステータス」、「位置情報」、「衛星数」と表示します。",
+ "repeater_cliHelpGpsOnOff": "GPS の電源状態を切り替えます。",
+ "repeater_cliHelpGpsSync": "ノードの時刻をGPSクロックと同期する。",
+ "repeater_cliHelpGpsSetLoc": "ノードの位置をGPS座標に設定し、設定を保存する。",
+ "repeater_cliHelpGpsAdvert": "ノードの広告設定における場所情報の指定:\n- none: 広告に場所情報を含まない\n- share: GPS位置情報を共有 (SensorManagerから取得)\n- prefs: プリファレンスに保存された場所情報を広告",
+ "repeater_cliHelpGpsAdvertSet": "場所に関する広告設定を行います。",
+ "repeater_commandsListTitle": "コマンド一覧",
+ "repeater_commandsListNote": "注:さまざまな「set ...」コマンドには、「get ...」コマンドも存在します。",
+ "repeater_general": "一般的な",
+ "repeater_settingsCategory": "設定",
+ "repeater_bridge": "橋",
+ "repeater_logging": "ログ記録",
+ "repeater_neighborsRepeaterOnly": "近隣住民(リピーターのみ)",
+ "repeater_regionManagementRepeaterOnly": "地域管理(ブロードキャスト用のみ)",
+ "repeater_regionNote": "地域レベルでの管理のため、地域定義と権限の管理を行うための機能が導入されました。",
+ "repeater_gpsManagement": "GPS管理",
+ "repeater_gpsNote": "GPSコマンドは、位置情報に関連するタスクを管理するために導入されました。",
+ "telemetry_receivedData": "受信したテレメトリーデータ",
+ "telemetry_requestTimeout": "テレメトリの要求タイムアウトしました。",
+ "telemetry_errorLoading": "テレメトリの読み込みに失敗しました: {error}",
+ "@telemetry_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_noData": "テレメトリデータは利用できません。",
+ "telemetry_channelTitle": "チャンネル {channel}",
+ "@telemetry_channelTitle": {
+ "placeholders": {
+ "channel": {
+ "type": "int"
+ }
+ }
+ },
+ "telemetry_batteryLabel": "バッテリー",
+ "telemetry_voltageLabel": "電圧",
+ "telemetry_mcuTemperatureLabel": "MCU の温度",
+ "telemetry_temperatureLabel": "温度",
+ "telemetry_currentLabel": "現在",
+ "telemetry_batteryValue": "{percent}% / {volts}V",
+ "@telemetry_batteryValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ },
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_voltageValue": "{volts}V",
+ "@telemetry_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_currentValue": "{amps}A",
+ "@telemetry_currentValue": {
+ "placeholders": {
+ "amps": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_temperatureValue": "{celsius}℃ / {fahrenheit}°F",
+ "@telemetry_temperatureValue": {
+ "placeholders": {
+ "celsius": {
+ "type": "String"
+ },
+ "fahrenheit": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_receivedData": "近隣住民のデータを受信",
+ "neighbors_requestTimedOut": "近隣住民からの要望:時間制限を設けてください。",
+ "neighbors_errorLoading": "近隣情報の読み込みに失敗: {error}",
+ "@neighbors_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_repeatersNeighbors": "繰り返し送信する、近隣",
+ "neighbors_noData": "近隣のデータは利用できません。",
+ "neighbors_unknownContact": "不明な {pubkey}",
+ "@neighbors_unknownContact": {
+ "placeholders": {
+ "pubkey": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_heardAgo": "聞いたのは、{time} くらい前です",
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_title": "パケットパス",
+ "channelPath_viewMap": "地図を表示する",
+ "channelPath_otherObservedPaths": "観察されたその他の経路",
+ "channelPath_repeaterHops": "ホップの繰り返し",
+ "channelPath_noHopDetails": "このパッケージに関する詳細な情報は提供されていません。",
+ "channelPath_messageDetails": "メッセージの詳細",
+ "channelPath_senderLabel": "送信者",
+ "channelPath_timeLabel": "時間",
+ "channelPath_repeatsLabel": "繰り返し",
+ "channelPath_pathLabel": "{index} 番目の経路",
+ "channelPath_observedLabel": "観察",
+ "channelPath_observedPathTitle": "観察された経路 {index} • {hops}",
+ "@channelPath_observedPathTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ },
+ "hops": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noLocationData": "場所に関するデータはありません",
+ "channelPath_timeWithDate": "{day}/{month} {time}",
+ "@channelPath_timeWithDate": {
+ "placeholders": {
+ "day": {
+ "type": "int"
+ },
+ "month": {
+ "type": "int"
+ },
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_timeOnly": "{time}",
+ "@channelPath_timeOnly": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_unknownPath": "不明",
+ "channelPath_floodPath": "洪水",
+ "channelPath_directPath": "直接",
+ "channelPath_observedZeroOf": "{total}個のホップ",
+ "@channelPath_observedZeroOf": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_observedSomeOf": "{observed} of {total} hops",
+ "@channelPath_observedSomeOf": {
+ "placeholders": {
+ "observed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_mapTitle": "経路図",
+ "channelPath_noRepeaterLocations": "この経路には、中継装置の設置場所がありません。",
+ "channelPath_primaryPath": "{index}番目の経路(主要経路)",
+ "@channelPath_primaryPath": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "@channelPath_pathLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_pathLabelTitle": "道",
+ "channelPath_observedPathHeader": "観察された経路",
+ "channelPath_selectedPathLabel": "{label} • {prefixes}",
+ "@channelPath_selectedPathLabel": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ },
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noHopDetailsAvailable": "このパッケージに関する詳細な配送情報は利用できません。",
+ "channelPath_unknownRepeater": "不明な増幅機",
+ "community_title": "地域",
+ "community_create": "コミュニティを構築する",
+ "community_createDesc": "新しいコミュニティを作成し、QRコードで共有する。",
+ "community_join": "参加する",
+ "community_joinTitle": "コミュニティに参加する",
+ "community_joinConfirmation": "{name}さんのようなコミュニティに参加したいですか?",
+ "@community_joinConfirmation": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanQr": "コミュニティのQRコードをスキャン",
+ "community_scanInstructions": "カメラを、地域のQRコードを向けて",
+ "community_showQr": "QRコードを表示する",
+ "community_publicChannel": "地域住民向け",
+ "community_hashtagChannel": "コミュニティ用ハッシュタグ",
+ "community_name": "コミュニティ名",
+ "community_enterName": "コミュニティ名を入力してください",
+ "community_created": "コミュニティ「{name}」が作成されました",
+ "@community_created": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_joined": "{name} のコミュニティに参加",
+ "@community_joined": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_qrTitle": "コミュニティ共有",
+ "community_qrInstructions": "このQRコードをスキャンして、{name}に参加してください。",
+ "@community_qrInstructions": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_hashtagPrivacyHint": "コミュニティハッシュタグのチャンネルは、コミュニティのメンバーのみが参加できます。",
+ "community_invalidQrCode": "無効なコミュニティQRコード",
+ "community_alreadyMember": "すでに会員である",
+ "community_alreadyMemberMessage": "あなたはすでに {name} の会員です。",
+ "@community_alreadyMemberMessage": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addPublicChannel": "コミュニティ用の公開チャンネルを追加",
+ "community_addPublicChannelHint": "このコミュニティの公開チャンネルを自動的に追加する",
+ "community_noCommunities": "まだコミュニティは形成されていません。",
+ "community_scanOrCreate": "QRコードをスキャンするか、コミュニティを作成して開始してください。",
+ "community_manageCommunities": "コミュニティの管理",
+ "community_delete": "コミュニティからの離脱",
+ "community_deleteConfirm": "{name}を辞める?",
+ "@community_deleteConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_deleteChannelsWarning": "これにより、{count} のチャンネルとそのメッセージも削除されます。",
+ "@community_deleteChannelsWarning": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "community_deleted": "コミュニティ「{name}」を離れる",
+ "@community_deleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "秘密の復元",
+ "community_regenerateSecretConfirm": "{name} の秘密鍵を再生成しますか? 継続的に通信するため、すべてのメンバーは新しいQRコードをスキャンする必要があります。",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "再生",
+ "community_secretRegenerated": "{name} への秘密が再設定されました",
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_updateSecret": "秘密情報の更新",
+ "community_secretUpdated": "{name} 向けの秘密設定を更新",
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanToUpdateSecret": "新しいQRコードをスキャンして、{name}の秘密情報を更新してください。",
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addHashtagChannel": "コミュニティのハッシュタグを追加",
+ "community_addHashtagChannelDesc": "このコミュニティ用のハッシュタグチャンネルを追加する",
+ "community_selectCommunity": "コミュニティを選択",
+ "community_regularHashtag": "定期的なハッシュタグ",
+ "community_regularHashtagDesc": "一般のハッシュタグ(誰でも参加可能)",
+ "community_communityHashtag": "コミュニティ用ハッシュタグ",
+ "community_communityHashtagDesc": "コミュニティメンバーのみへの限定",
+ "community_forCommunity": "{name} 様",
+ "@community_forCommunity": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "listFilter_tooltip": "フィルタリングと並べ替え",
+ "listFilter_sortBy": "並び替え",
+ "listFilter_latestMessages": "最新のメッセージ",
+ "listFilter_heardRecently": "最近、聞いた",
+ "listFilter_az": "AからZ",
+ "listFilter_filters": "フィルター",
+ "listFilter_all": "すべて",
+ "listFilter_favorites": "お気に入り",
+ "listFilter_addToFavorites": "お気に入りに追加",
+ "listFilter_removeFromFavorites": "お気に入りから削除",
+ "listFilter_users": "利用者",
+ "listFilter_repeaters": "繰り返し送信装置",
+ "listFilter_roomServers": "ルーム用サーバー",
+ "listFilter_unreadOnly": "未読のみ",
+ "listFilter_newGroup": "新しいグループ",
+ "pathTrace_you": "あなた",
+ "pathTrace_failed": "パスの追跡に失敗しました。",
+ "pathTrace_notAvailable": "パスの追跡機能は利用できません。",
+ "pathTrace_refreshTooltip": "パスの追跡をリフレッシュする。",
+ "pathTrace_someHopsNoLocation": "ホップの1つまたは複数について、場所が特定されていません。",
+ "pathTrace_clearTooltip": "明確な道筋。",
+ "losSelectStartEnd": "LOS の開始ノードと終了ノードを選択してください。",
+ "losRunFailed": "視界確認に失敗: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "すべての項目をクリア",
+ "losRunToViewElevationProfile": "LOS(レーザー測距)を使用して、標高プロファイルを表示する",
+ "losMenuTitle": "LOS メニュー",
+ "losMenuSubtitle": "特定の場所をタップするか、地図を長押ししてカスタムポイントを作成する。",
+ "losShowDisplayNodes": "表示ノードを表示する",
+ "losCustomPoints": "カスタマイズ可能なポイント",
+ "losCustomPointLabel": "カスタマイズ {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "ポイントA",
+ "losPointB": "ポイントB",
+ "losAntennaA": "アンテナ A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "アンテナ B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "LOS(レーティングシステム)を使用する",
+ "losNoElevationData": "標高データは含まれていません",
+ "losProfileClear": "{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS:確認中…",
+ "losStatusNoData": "LOS: データの欠如",
+ "losStatusSummary": "LOS: {clear}/{total} clear, {blocked} blocked, {unknown} unknown",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "あるサンプルまたは複数のサンプルについて、標高データが利用できません。",
+ "losErrorInvalidInput": "LOS(レーダー)計算に必要な、無効な点/標高データ。",
+ "losRenameCustomPoint": "カスタムポイントの名前を変更する",
+ "losPointName": "項目名",
+ "losShowPanelTooltip": "LOSパネルを表示する",
+ "losHidePanelTooltip": "LOSパネルを隠す",
+ "losElevationAttribution": "標高データ:Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "ラジオ・ホライゾン",
+ "losLegendLosBeam": "LOS ビーミング",
+ "losLegendTerrain": "地形",
+ "losFrequencyLabel": "周波数",
+ "losFrequencyInfoTooltip": "計算の詳細を見る",
+ "losFrequencyDialogTitle": "ラジオによる水平線計算",
+ "losFrequencyDialogDescription": "k={baselineK} ( {baselineFreq} MHz) から開始し、現在の {frequencyMHz} MHz の帯域に対して k の値を調整します。これにより、曲面状の無線通信範囲の限界が定義されます。",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "contacts_pathTrace": "経路追跡",
+ "contacts_ping": "パング",
+ "contacts_repeaterPathTrace": "リピーターまでの経路を追跡する",
+ "contacts_repeaterPing": "PING 繰り返し",
+ "contacts_roomPathTrace": "部屋のサーバーへの経路を追跡する",
+ "contacts_roomPing": "ピンルーム用サーバー",
+ "contacts_chatTraceRoute": "経路の追跡ルート",
+ "contacts_pathTraceTo": "{name} への経路を追跡する",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_clipboardEmpty": "クリップボードは空です。",
+ "contacts_invalidAdvertFormat": "無効な連絡先情報",
+ "contacts_contactImported": "連絡先が登録されました。",
+ "contacts_contactImportFailed": "連絡先のインポートに失敗しました。",
+ "contacts_zeroHopAdvert": "ゼロホップ広告",
+ "contacts_floodAdvert": "洪水に関する広告",
+ "contacts_copyAdvertToClipboard": "広告をクリップボードにコピー",
+ "contacts_addContactFromClipboard": "クリップボードから連絡先を追加する",
+ "contacts_ShareContact": "連絡先をクリップボードにコピー",
+ "contacts_ShareContactZeroHop": "広告を通じて連絡先を共有する",
+ "contacts_zeroHopContactAdvertSent": "広告を通じて連絡先を得た。",
+ "contacts_zeroHopContactAdvertFailed": "連絡を送信できませんでした。",
+ "contacts_contactAdvertCopied": "広告がクリップボードにコピーされました。",
+ "contacts_contactAdvertCopyFailed": "広告のコピーがクリップボードにコピーできませんでした。",
+ "notification_activityTitle": "メッシュコアの活動",
+ "notification_messagesCount": "{count} {count, plural, =1{message} other{messages}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{チャンネルメッセージ} other{チャンネルメッセージ}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{新しいノード} other{新しいノード}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "新たに {contactType} が発見されました",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "新しいメッセージを受信",
+ "settings_gpxExportRepeaters": "GPX へのエクスポート用リピーター/ルームサーバー",
+ "settings_gpxExportRepeatersSubtitle": "GPXファイルに場所情報を付加した、レピーター/ルームサーバーのエクスポート",
+ "settings_gpxExportContacts": "GPX 形式へのエクスポート",
+ "settings_gpxExportContactsSubtitle": "GPXファイルに位置情報を保存して、他の人と共有する。",
+ "settings_gpxExportAll": "すべての連絡先をGPX形式でエクスポートする",
+ "settings_gpxExportAllSubtitle": "すべての連絡先を、場所情報付きのGPXファイルにエクスポートする。",
+ "settings_gpxExportSuccess": "GPXファイルの正常なエクスポートが完了しました。",
+ "settings_gpxExportNoContacts": "エクスポートする連絡先は存在しません。",
+ "settings_gpxExportNotAvailable": "このデバイス/OSではサポートされていません",
+ "settings_gpxExportError": "エクスポート時にエラーが発生しました。",
+ "settings_gpxExportRepeatersRoom": "中継装置およびルームサーバーの設置場所",
+ "settings_gpxExportChat": "関連施設",
+ "settings_gpxExportAllContacts": "すべての連絡先場所",
+ "settings_gpxExportShareText": "meshcore-openからエクスポートされた地図データ",
+ "settings_gpxExportShareSubject": "meshcore-open GPX形式の地図データのエクスポート",
+ "snrIndicator_nearByRepeaters": "近くの電波中継局",
+ "snrIndicator_lastSeen": "最後に確認された場所",
+ "contactsSettings_title": "連絡先設定",
+ "contactsSettings_autoAddTitle": "自動検出",
+ "contactsSettings_otherTitle": "その他の連絡に関する設定",
+ "contactsSettings_autoAddUsersTitle": "自動でユーザーを追加する",
+ "contactsSettings_autoAddUsersSubtitle": "利用者が自動的に発見したユーザーを追加できるようにする。",
+ "contactsSettings_autoAddRepeatersTitle": "自動で繰り返し設定",
+ "contactsSettings_autoAddRepeatersSubtitle": "発見した中継局を、自動的に追加できるようにする。",
+ "contactsSettings_autoAddRoomServersTitle": "自動でルームサーバーを追加",
+ "contactsSettings_autoAddRoomServersSubtitle": "利用者が、発見した部屋のサーバーを自動的に追加できるようにする。",
+ "contactsSettings_autoAddSensorsTitle": "自動でセンサーを追加",
+ "contactsSettings_autoAddSensorsSubtitle": "利用者が、発見したセンサーを自動的に追加できるようにする。",
+ "contactsSettings_overwriteOldestTitle": "最も古いものを上書きする",
+ "contactsSettings_overwriteOldestSubtitle": "連絡先リストが満杯になった場合、最も古いかつ「お気に入り」ではない連絡先が削除されます。",
+ "discoveredContacts_Title": "連絡先が見つかった",
+ "discoveredContacts_noMatching": "一致する連絡先が見つかりませんでした",
+ "discoveredContacts_searchHint": "発見された連絡先を検索する",
+ "discoveredContacts_contactAdded": "連絡先を追加",
+ "discoveredContacts_addContact": "連絡先を追加",
+ "discoveredContacts_copyContact": "連絡先をクリップボードにコピー",
+ "discoveredContacts_deleteContact": "発見された連絡先を削除",
+ "discoveredContacts_deleteContactAll": "発見されたすべての連絡先を削除",
+ "discoveredContacts_deleteContactAllContent": "本当に、見つけたすべての連絡先を削除してもよろしいですか?",
+ "chat_sendCooldown": "再度送信する前に、しばらくお待ちください。",
+ "appSettings_jumpToOldestUnread": "最も古い未読のメッセージへ移動",
+ "appSettings_jumpToOldestUnreadSubtitle": "未読メッセージがあるチャットを開く際、「最新のメッセージ」ではなく、最初に未読のメッセージまでスクロールしてください。",
+ "appSettings_languageHu": "ハンガリー語",
+ "appSettings_languageJa": "日本語",
+ "appSettings_languageKo": "韓国語",
+ "radioStats_tooltip": "ラジオおよびメッシュに関する統計",
+ "radioStats_screenTitle": "ラジオの統計",
+ "radioStats_notConnected": "ラジオの統計情報を表示するために、デバイスに接続してください。",
+ "radioStats_firmwareTooOld": "ラジオの統計機能を使用するには、v8またはそれ以降のファームウェアが必要です。",
+ "radioStats_waiting": "データ待ち…",
+ "radioStats_noiseFloor": "ノイズレベル: {noiseDbm} dBm",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastRssi": "最後のRSSI: {rssiDbm} dBm",
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastSnr": "最終SNR: {snr} dB",
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "radioStats_txAir": "TX 放送時間(合計):{seconds} 秒",
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_rxAir": "RX 放送時間(合計):{seconds} 秒",
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_chartCaption": "最近のサンプルのノイズレベル(dBm)。",
+ "radioStats_stripNoise": "ノイズレベル: {noiseDbm} dBm",
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_stripWaiting": "ラジオの統計情報を取得中…",
+ "radioStats_settingsTile": "ラジオの統計",
+ "radioStats_settingsSubtitle": "ノイズレベル、RSSI、SNR、および通信時間",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "プライバシー設定",
+ "settings_privacySubtitle": "共有する情報の内容を管理する。",
+ "settings_denyAll": "すべてを否定",
+ "settings_privacySettingsDescription": "自分のデバイスが他の人に共有する情報を選択してください。",
+ "settings_allowByContact": "連絡先を明示するオプション",
+ "settings_allowAll": "すべて許可",
+ "settings_telemetryBaseMode": "テレメトリ基本モード",
+ "settings_telemetryLocationMode": "テレメトリ位置特定モード",
+ "settings_telemetryEnvironmentMode": "テレメトリ環境モード",
+ "settings_advertLocation": "広告掲載場所",
+ "settings_advertLocationSubtitle": "広告に場所を記載してください。",
+ "settings_multiAck": "複数のACK:{value}",
+ "settings_telemetryModeUpdated": "テレメトリモードが更新されました",
+ "contact_info": "連絡先",
+ "contact_settings": "連絡設定",
+ "contact_telemetry": "テレメトリー",
+ "contact_lastSeen": "最後に確認された場所",
+ "contact_clearChat": "チャットのクリア",
+ "contact_teleBase": "テレメトリ基地",
+ "contact_teleBaseSubtitle": "バッテリー残量と基本的なテレメトリーの共有を許可する",
+ "contact_teleLoc": "テレメトリの場所",
+ "contact_teleLocSubtitle": "位置情報共有を許可する",
+ "contact_teleEnv": "テレメトリ環境",
+ "contact_teleEnvSubtitle": "環境センサーのデータを共有することを許可する",
+ "map_showOverlaps": "リピーターキーの重複",
+ "map_runTraceWithReturnPath": "元の経路に戻る。",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "受信メッセージを翻訳し、送信前に翻訳を適用できるようにする。",
+ "translation_title": "翻訳",
+ "translation_composerTitle": "送信する前に翻訳する",
+ "translation_enableTitle": "翻訳機能を有効にする",
+ "translation_composerSubtitle": "作曲家翻訳アイコンのデフォルト状態を制御する。",
+ "translation_targetLanguage": "翻訳対象言語",
+ "translation_useAppLanguage": "アプリの言語設定",
+ "translation_downloadedModelLabel": "ダウンロードしたモデル",
+ "translation_presetModelLabel": "あらかじめ設定されたHugging Faceモデル",
+ "translation_manualUrlLabel": "マニュアルモデルのURL",
+ "translation_downloadModel": "モデルのダウンロード",
+ "translation_downloading": "ダウンロード中...",
+ "translation_working": "業務中…",
+ "translation_stop": "停止",
+ "translation_mergingChunks": "ダウンロードしたファイルを最終ファイルに結合中...",
+ "translation_downloadedModels": "ダウンロードされたモデル",
+ "translation_deleteModel": "モデルを削除",
+ "translation_modelDownloaded": "翻訳モデルのダウンロードが完了しました。",
+ "translation_downloadStopped": "ダウンロードが中断されました。",
+ "translation_downloadFailed": "ダウンロードに失敗しました:{error}",
+ "translation_enterUrlFirst": "まず、モデルのURLを入力してください。",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_translateBeforeSending": "送信する前に翻訳する",
+ "translation_composerEnabledHint": "メッセージは送信前に翻訳されます。",
+ "translation_messageTranslation": "メッセージの翻訳",
+ "translation_composerDisabledHint": "元のタイプされた言語でメッセージを送信してください。",
+ "translation_translateTo": "{language} への翻訳",
+ "translation_translationOptions": "翻訳の選択肢",
+ "translation_systemLanguage": "システム言語",
+ "scanner_linuxPairingShowPin": "PINを表示",
+ "scanner_linuxPairingHidePin": "PINを非表示",
+ "scanner_linuxPairingPinTitle": "Bluetooth ペアリング PIN",
+ "scanner_linuxPairingPinPrompt": "{deviceName}のPINを入力してください(なしの場合は空欄のまま)。",
+ "repeater_cliQuickClockSync": "クロック同期",
+ "repeater_cliQuickDiscovery": "近隣を発見する",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "ログイン後、時計の時刻を同期する",
+ "repeater_clockSyncAfterLoginSubtitle": "ログインが成功した場合、自動的に「時刻同期」を送信する。",
+ "room_guest": "ルームサーバーに関する情報",
+ "chat_sendMessage": "メッセージを送信する",
+ "repeater_guest": "繰り返し送信に関する情報",
+ "repeater_guestTools": "ゲスト向けツール"
+}
diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb
new file mode 100644
index 0000000..40721fe
--- /dev/null
+++ b/lib/l10n/app_ko.arb
@@ -0,0 +1,2115 @@
+{
+ "@@locale": "ko",
+ "appTitle": "MeshCore Open",
+ "nav_contacts": "연락처",
+ "nav_channels": "채널",
+ "nav_map": "지도",
+ "common_cancel": "취소",
+ "common_ok": "알겠습니다",
+ "common_connect": "연결",
+ "common_unknownDevice": "알 수 없는 장치",
+ "common_save": "저장",
+ "common_delete": "삭제",
+ "common_deleteAll": "모두 삭제",
+ "common_close": "닫기",
+ "common_edit": "수정",
+ "common_add": "추가",
+ "common_settings": "설정",
+ "common_disconnect": "연결 해제",
+ "common_connected": "연결된",
+ "common_disconnected": "단절",
+ "common_create": "만들다",
+ "common_continue": "계속",
+ "common_share": "공유",
+ "common_copy": "복사",
+ "common_retry": "다시 시도",
+ "common_hide": "숨기다",
+ "common_remove": "제거",
+ "common_enable": "활성화",
+ "common_disable": "비활성화",
+ "common_reboot": "재부팅",
+ "common_loading": "로딩 중...",
+ "common_notAvailable": "—",
+ "common_voltageValue": "{volts} V",
+ "@common_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "common_percentValue": "{percent}%",
+ "@common_percentValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ }
+ }
+ },
+ "scanner_title": "MeshCore 공개",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "블루투스",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "TCP를 통해 연결",
+ "tcpHostLabel": "IP 주소",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "항",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "목적지 주소 입력 후 연결",
+ "tcpStatus_connectingTo": "{endpoint}에 연결 중...",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpErrorHostRequired": "IP 주소가 필요합니다.",
+ "tcpErrorPortInvalid": "포트 번호는 1에서 65535 사이여야 합니다.",
+ "tcpErrorUnsupported": "이 플랫폼에서는 TCP 트랜스포트를 지원하지 않습니다.",
+ "tcpErrorTimedOut": "TCP 연결이 시간 초과되었습니다.",
+ "tcpConnectionFailed": "TCP 연결 실패: {error}",
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbScreenTitle": "USB를 통해 연결",
+ "usbScreenSubtitle": "감지된 시리얼 장치를 선택하고 MeshCore 노드에 직접 연결하십시오.",
+ "usbScreenStatus": "USB 장치를 선택합니다.",
+ "usbScreenNote": "USB 직렬 통신은 지원되는 안드로이드 장치 및 데스크톱 플랫폼에서 활성화됩니다.",
+ "usbScreenEmptyState": "USB 장치가 탐지되지 않았습니다. USB 장치를 연결하고 다시 시도해 보세요.",
+ "usbErrorPermissionDenied": "USB 접근 권한이 거부되었습니다.",
+ "usbErrorDeviceMissing": "선택한 USB 장치는 더 이상 사용 불가능합니다.",
+ "usbErrorInvalidPort": "유효한 USB 장치를 선택하세요.",
+ "usbErrorBusy": "또 다른 USB 연결 요청이 이미 진행 중입니다.",
+ "usbErrorNotConnected": "USB 장치가 연결되지 않았습니다.",
+ "usbErrorOpenFailed": "선택한 USB 장치를 열 수 없습니다.",
+ "usbErrorConnectFailed": "선택한 USB 장치에 연결에 실패했습니다.",
+ "usbErrorUnsupported": "이 플랫폼에서는 USB 직렬 통신을 지원하지 않습니다.",
+ "usbErrorAlreadyActive": "USB 연결이 이미 활성화되어 있습니다.",
+ "usbErrorNoDeviceSelected": "USB 장치가 선택되지 않았습니다.",
+ "usbErrorPortClosed": "USB 연결이 활성화되지 않았습니다.",
+ "usbErrorConnectTimedOut": "연결이 시간 초과되었습니다. 장치가 USB Companion 펌웨어를 가지고 있는지 확인해 주세요.",
+ "usbFallbackDeviceName": "웹 시리얼 장치",
+ "usbStatus_notConnected": "USB 장치를 선택합니다.",
+ "usbStatus_connecting": "USB 장치에 연결 중...",
+ "usbStatus_searching": "USB 장치 검색 중...",
+ "usbConnectionFailed": "USB 연결 실패: {error}",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_scanning": "장치 검색 중...",
+ "scanner_connecting": "연결 중...",
+ "scanner_disconnecting": "연결 해제 중...",
+ "scanner_notConnected": "연결되지 않음",
+ "scanner_connectedTo": "{deviceName}에 연결됨",
+ "@scanner_connectedTo": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_searchingDevices": "MeshCore 장치를 검색 중...",
+ "scanner_tapToScan": "MeshCore 장치를 찾기 위해 스캔 버튼을 누르세요.",
+ "scanner_connectionFailed": "연결 실패: {error}",
+ "@scanner_connectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_stop": "멈춰",
+ "scanner_scan": "스캔",
+ "scanner_bluetoothOff": "블루투스는 꺼져 있습니다.",
+ "scanner_bluetoothOffMessage": "블루투스를 켜서 장치를 검색해주세요.",
+ "scanner_chromeRequired": "크롬 브라우저 필요",
+ "scanner_chromeRequiredMessage": "이 웹 애플리케이션은 블루투드 지원을 위해 Google Chrome 또는 Chromium 기반 브라우저가 필요합니다.",
+ "scanner_enableBluetooth": "블루투스 활성화",
+ "device_quickSwitch": "빠른 전환",
+ "device_meshcore": "메쉬코어",
+ "settings_title": "설정",
+ "settings_deviceInfo": "장치 정보",
+ "settings_appSettings": "앱 설정",
+ "settings_appSettingsSubtitle": "알림, 메시징, 지도 설정",
+ "settings_nodeSettings": "노드 설정",
+ "settings_nodeName": "노드 이름",
+ "settings_nodeNameNotSet": "설정되지 않음",
+ "settings_nodeNameHint": "노드 이름을 입력하세요",
+ "settings_nodeNameUpdated": "이름 변경",
+ "settings_radioSettings": "라디오 설정",
+ "settings_radioSettingsSubtitle": "주파수, 전력, 스펙트럼",
+ "settings_radioSettingsUpdated": "라디오 설정이 업데이트되었습니다.",
+ "settings_location": "위치",
+ "settings_locationSubtitle": "GPS 좌표",
+ "settings_locationUpdated": "위치 및 GPS 설정이 업데이트되었습니다.",
+ "settings_locationBothRequired": "위도와 경도를 모두 입력하세요.",
+ "settings_locationInvalid": "유효하지 않은 위도 또는 경도.",
+ "settings_locationGPSEnable": "GPS 활성화",
+ "settings_locationGPSEnableSubtitle": "GPS를 사용하여 위치 정보를 자동으로 업데이트할 수 있도록 합니다.",
+ "settings_locationIntervalSec": "GPS 간격 (초)",
+ "settings_locationIntervalInvalid": "간격은 최소 60초 이상, 86400초 미만이어야 합니다.",
+ "settings_latitude": "위도",
+ "settings_longitude": "경도",
+ "settings_contactSettings": "연락처 설정",
+ "settings_contactSettingsSubtitle": "연락처 추가 방식 설정",
+ "settings_privacyMode": "개인 정보 보호 모드",
+ "settings_privacyModeSubtitle": "광고에 이름/위치 정보 숨기기",
+ "settings_privacyModeToggle": "광고에 자신의 이름과 위치를 숨기기 위해 개인 정보 보호 모드를 켜거나 끄십시오.",
+ "settings_privacyModeEnabled": "개인 정보 보호 모드 활성화",
+ "settings_privacyModeDisabled": "개인 정보 보호 모드 비활성화",
+ "settings_actions": "행동",
+ "settings_sendAdvertisement": "광고 전송",
+ "settings_sendAdvertisementSubtitle": "방송 활동",
+ "settings_advertisementSent": "광고 전송",
+ "settings_syncTime": "동기화 시간",
+ "settings_syncTimeSubtitle": "장치 시계를 휴대폰 시간으로 설정",
+ "settings_timeSynchronized": "시간 동기화",
+ "settings_refreshContacts": "연락처 갱신",
+ "settings_refreshContactsSubtitle": "장치에서 연락처 목록을 다시 불러오기",
+ "settings_rebootDevice": "장치 재부팅",
+ "settings_rebootDeviceSubtitle": "MeshCore 장치를 재부팅하세요.",
+ "settings_rebootDeviceConfirm": "정말로 장치를 재부팅하시겠습니까? 이 경우 연결이 끊어집니다.",
+ "settings_debug": "디버깅",
+ "settings_bleDebugLog": "BLE 디버그 로그",
+ "settings_bleDebugLogSubtitle": "BLE 명령어, 응답 및 원시 데이터",
+ "settings_appDebugLog": "앱 디버깅 로그",
+ "settings_appDebugLogSubtitle": "애플리케이션 디버깅 메시지",
+ "settings_about": "소개",
+ "settings_aboutVersion": "MeshCore Open {version} 버전",
+ "@settings_aboutVersion": {
+ "placeholders": {
+ "version": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_aboutLegalese": "2026년 MeshCore 오픈 소스 프로젝트",
+ "settings_aboutDescription": "MeshCore LoRa 메시 네트워크 장치를 위한 오픈 소스 Flutter 클라이언트.",
+ "settings_aboutOpenMeteoAttribution": "LOS 고도 데이터: Open-Meteo (CC BY 4.0)",
+ "settings_infoName": "이름",
+ "settings_infoId": "ID",
+ "settings_infoStatus": "상태",
+ "settings_infoBattery": "배터리",
+ "settings_infoPublicKey": "공개 키",
+ "settings_infoContactsCount": "연락처 수",
+ "settings_infoChannelCount": "채널 수",
+ "settings_presets": "기본 설정",
+ "settings_frequency": "주파수 (MHz)",
+ "settings_frequencyHelper": "300.0 - 2500.0",
+ "settings_frequencyInvalid": "유효하지 않은 주파수 (300-2500 MHz)",
+ "settings_bandwidth": "대역폭",
+ "settings_spreadingFactor": "분산 계수",
+ "settings_codingRate": "코딩 속도",
+ "settings_txPower": "TX 전력 (dBm)",
+ "settings_txPowerHelper": "0 - 22",
+ "settings_txPowerInvalid": "유효하지 않은 TX 전력 (0-22 dBm)",
+ "settings_clientRepeat": "오프그리드 반복",
+ "settings_clientRepeatSubtitle": "이 장치가 다른 사람들을 위해 메시 패킷을 반복하도록 허용합니다.",
+ "settings_clientRepeatFreqWarning": "오프그리드(무전력) 시스템 재연결에는 433MHz, 869MHz, 또는 918MHz 주파수가 필요합니다.",
+ "settings_error": "오류: {message}",
+ "@settings_error": {
+ "placeholders": {
+ "message": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_title": "앱 설정",
+ "appSettings_appearance": "외관",
+ "appSettings_theme": "주제",
+ "appSettings_themeSystem": "기본 설정",
+ "appSettings_themeLight": "빛",
+ "appSettings_themeDark": "어둡다",
+ "appSettings_language": "언어",
+ "appSettings_languageSystem": "기본 설정",
+ "appSettings_languageEn": "영어",
+ "appSettings_languageFr": "프랑스어",
+ "appSettings_languageEs": "스페인어",
+ "appSettings_languageDe": "독일어",
+ "appSettings_languagePl": "폴란드",
+ "appSettings_languageSl": "슬로베니아어",
+ "appSettings_languagePt": "포르투갈어",
+ "appSettings_languageIt": "이탈리아어",
+ "appSettings_languageZh": "중국어",
+ "appSettings_languageSv": "스웨덴어",
+ "appSettings_languageNl": "네덜란드어",
+ "appSettings_languageSk": "슬로베니아어",
+ "appSettings_languageBg": "불가리",
+ "appSettings_languageRu": "러시아어",
+ "appSettings_languageUk": "우크라이나",
+ "appSettings_enableMessageTracing": "메시지 추적 기능 활성화",
+ "appSettings_enableMessageTracingSubtitle": "메시지에 대한 상세한 경로 및 시간 정보를 표시",
+ "appSettings_notifications": "알림",
+ "appSettings_enableNotifications": "알림 활성화",
+ "appSettings_enableNotificationsSubtitle": "메시지와 광고에 대한 알림을 받으세요.",
+ "appSettings_notificationPermissionDenied": "알림 권한 거부",
+ "appSettings_notificationsEnabled": "알림 기능 활성화",
+ "appSettings_notificationsDisabled": "알림 기능 끄기",
+ "appSettings_messageNotifications": "메시지 알림",
+ "appSettings_messageNotificationsSubtitle": "새로운 메시지를 받을 때 알림 표시",
+ "appSettings_channelMessageNotifications": "채널 메시지 알림",
+ "appSettings_channelMessageNotificationsSubtitle": "채널 메시지를 수신할 때 알림 표시",
+ "appSettings_advertisementNotifications": "광고 알림",
+ "appSettings_advertisementNotificationsSubtitle": "새 노드가 발견되었을 때 알림 표시",
+ "appSettings_messaging": "메시징",
+ "appSettings_clearPathOnMaxRetry": "Max 재시도 시 경로 명확하게 설정",
+ "appSettings_clearPathOnMaxRetrySubtitle": "5번의 전송 시도가 실패하면 연락 경로를 재설정",
+ "appSettings_pathsWillBeCleared": "5번의 시도 실패 후, 해당 경로가 확보될 것입니다.",
+ "appSettings_pathsWillNotBeCleared": "경로는 자동으로 정리되지 않습니다.",
+ "appSettings_autoRouteRotation": "자동 경로 순환",
+ "appSettings_autoRouteRotationSubtitle": "최적 경로와 방수 모드 사이를 전환",
+ "appSettings_autoRouteRotationEnabled": "자동 경로 순환 기능 활성화",
+ "appSettings_autoRouteRotationDisabled": "자동 경로 순환 기능 비활성화",
+ "appSettings_maxRouteWeight": "최대 경로 무게",
+ "appSettings_maxRouteWeightSubtitle": "한 경로가 성공적인 배송을 통해 누적할 수 있는 최대 무게",
+ "appSettings_initialRouteWeight": "초기 경로 가중치",
+ "appSettings_initialRouteWeightSubtitle": "새롭게 발견된 경로의 초기 무게",
+ "appSettings_routeWeightSuccessIncrement": "성공 횟수 증가",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "성공적으로 배송된 경로에 추가된 무게",
+ "appSettings_routeWeightFailureDecrement": "오류 가중치 감소",
+ "appSettings_routeWeightFailureDecrementSubtitle": "배송 실패 후 경로에서 제거된 무게",
+ "appSettings_maxMessageRetries": "최대 메시지 재시도 횟수",
+ "appSettings_maxMessageRetriesSubtitle": "메시지를 실패로 처리하기 전 시도 횟수",
+ "path_routeWeight": "{weight}/{max}",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_battery": "배터리",
+ "appSettings_batteryChemistry": "배터리 화학",
+ "appSettings_batteryChemistryPerDevice": "{deviceName} 당분간",
+ "@appSettings_batteryChemistryPerDevice": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_batteryChemistryConnectFirst": "장치를 선택하기 위해 연결",
+ "appSettings_batteryNmc": "18650 NMC (3.0-4.2V)",
+ "appSettings_batteryLifepo4": "LiFePO4 (2.6-3.65V)",
+ "appSettings_batteryLipo": "리튬 폴리머 (3.0-4.2V)",
+ "appSettings_mapDisplay": "지도 표시",
+ "appSettings_showRepeaters": "반복 기능 표시",
+ "appSettings_showRepeatersSubtitle": "지도에 반복자 노드를 표시",
+ "appSettings_showChatNodes": "채팅 노드 표시",
+ "appSettings_showChatNodesSubtitle": "지도에 채팅 노드를 표시",
+ "appSettings_showOtherNodes": "다른 노드 표시",
+ "appSettings_showOtherNodesSubtitle": "지도에서 다른 노드 유형을 표시",
+ "appSettings_timeFilter": "시간 필터",
+ "appSettings_timeFilterShowAll": "모든 노드 표시",
+ "appSettings_timeFilterShowLast": "지난 {hours} 시간 동안의 노드 표시",
+ "@appSettings_timeFilterShowLast": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_mapTimeFilter": "지도 필터",
+ "appSettings_showNodesDiscoveredWithin": "다음 내역에서 발견된 노드 표시:",
+ "appSettings_allTime": "모든 시간",
+ "appSettings_lastHour": "지난 시간",
+ "appSettings_last6Hours": "지난 6시간",
+ "appSettings_last24Hours": "지난 24시간",
+ "appSettings_lastWeek": "지난 주",
+ "appSettings_offlineMapCache": "오프라인 지도 캐시",
+ "appSettings_unitsTitle": "단위",
+ "appSettings_unitsMetric": "단위 (m / km)",
+ "appSettings_unitsImperial": "제국 (피트/마일)",
+ "appSettings_noAreaSelected": "선택된 영역 없음",
+ "appSettings_areaSelectedZoom": "선택된 영역 (줌 레벨: {minZoom} - {maxZoom})",
+ "@appSettings_areaSelectedZoom": {
+ "placeholders": {
+ "minZoom": {
+ "type": "int"
+ },
+ "maxZoom": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_debugCard": "디버깅",
+ "appSettings_appDebugLogging": "앱 디버깅 로깅",
+ "appSettings_appDebugLoggingSubtitle": "로그 앱 디버깅 메시지 (문제 해결을 위한)",
+ "appSettings_appDebugLoggingEnabled": "앱 디버깅 로깅 활성화",
+ "appSettings_appDebugLoggingDisabled": "앱 디버깅 로깅 비활성화",
+ "contacts_title": "연락처",
+ "contacts_noContacts": "아직 연락처는 없습니다.",
+ "contacts_contactsWillAppear": "장치가 광고를 할 때, 연락처 정보가 표시됩니다.",
+ "contacts_unread": "읽지 않음",
+ "contacts_searchContactsNoNumber": "연락처 검색...",
+ "contacts_searchContacts": "{number} {str} 연락처 검색...",
+ "@contacts_searchContacts": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchFavorites": "{number} {str} 검색 결과 보기...",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "{number} {str} 사용자 검색...",
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "{number} {str} 검색 결과 반복기 검색",
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "{number} {str} 방 서버 검색",
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_noUnreadContacts": "읽지 않은 연락처가 없습니다.",
+ "contacts_noContactsFound": "연락처 또는 그룹이 검색되지 않았습니다.",
+ "contacts_deleteContact": "연락처 삭제",
+ "contacts_removeConfirm": "{contactName}를 연락처 목록에서 제거하시겠습니까?",
+ "@contacts_removeConfirm": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_manageRepeater": "리피터 관리",
+ "contacts_manageRoom": "방 서버 관리",
+ "contacts_roomLogin": "방 서버 로그인",
+ "contacts_openChat": "자유로운 대화",
+ "contacts_editGroup": "편집 그룹",
+ "contacts_deleteGroup": "그룹 삭제",
+ "contacts_deleteGroupConfirm": "{groupName} 삭제?",
+ "@contacts_deleteGroupConfirm": {
+ "placeholders": {
+ "groupName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_newGroup": "새로운 그룹",
+ "contacts_groupName": "그룹 이름",
+ "contacts_groupNameRequired": "그룹 이름이 필요합니다",
+ "contacts_groupNameReserved": "이 그룹 이름은 이미 사용 중입니다.",
+ "contacts_groupAlreadyExists": "그룹 \"{name}\"은 이미 존재합니다.",
+ "@contacts_groupAlreadyExists": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_filterContacts": "연락처 필터링...",
+ "contacts_noContactsMatchFilter": "입력하신 검색 조건과 일치하는 연락처가 없습니다.",
+ "contacts_noMembers": "회원 없음",
+ "contacts_lastSeenNow": "최근",
+ "contacts_lastSeenMinsAgo": "~ {minutes} min.",
+ "@contacts_lastSeenMinsAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenHourAgo": "약 1시간",
+ "contacts_lastSeenHoursAgo": "~ {hours} hours",
+ "@contacts_lastSeenHoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenDayAgo": "~ 1일",
+ "contacts_lastSeenDaysAgo": "~ {days}일",
+ "@contacts_lastSeenDaysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_title": "채널",
+ "channels_noChannelsConfigured": "구성된 채널이 없습니다.",
+ "channels_addPublicChannel": "공개 채널 추가",
+ "channels_searchChannels": "검색 채널...",
+ "channels_noChannelsFound": "채널을 찾을 수 없습니다.",
+ "channels_channelIndex": "채널 {index}",
+ "@channels_channelIndex": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_hashtagChannel": "해시태그 채널",
+ "channels_public": "대중의",
+ "channels_private": "사립",
+ "channels_publicChannel": "공개 채널",
+ "channels_privateChannel": "개인 채널",
+ "channels_editChannel": "채널 편집",
+ "channels_muteChannel": "음소거 채널",
+ "channels_unmuteChannel": "채널 음소거 해제",
+ "channels_deleteChannel": "채널 삭제",
+ "channels_deleteChannelConfirm": "{name} 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
+ "@channels_deleteChannelConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleteFailed": "채널 \"{name}\" 삭제에 실패했습니다.",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleted": "채널 \"{name}\" 삭제",
+ "@channels_channelDeleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_addChannel": "채널 추가",
+ "channels_channelIndexLabel": "채널 인덱스",
+ "channels_channelName": "채널 이름",
+ "channels_usePublicChannel": "공개 채널 사용",
+ "channels_standardPublicPsk": "표준 공공 PSK",
+ "channels_pskHex": "PSK (헥스)",
+ "channels_generateRandomPsk": "임의의 PSK 생성",
+ "channels_enterChannelName": "채널 이름을 입력해 주세요.",
+ "channels_pskMustBe32Hex": "PSK(개인식별키)는 32자리 16진수 문자여야 합니다.",
+ "channels_channelAdded": "채널 \"{name}\" 추가",
+ "@channels_channelAdded": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_editChannelTitle": "채널 {index} 편집",
+ "@channels_editChannelTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_smazCompression": "SMAZ 압축",
+ "channels_channelUpdated": "채널 \"{name}\"이 업데이트되었습니다.",
+ "@channels_channelUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_publicChannelAdded": "공개 채널 추가",
+ "channels_sortBy": "정렬 기준 선택",
+ "channels_sortManual": "사용 설명서",
+ "channels_sortAZ": "A부터 Z까지",
+ "channels_sortLatestMessages": "최신 메시지",
+ "channels_sortUnread": "읽지 않음",
+ "channels_createPrivateChannel": "개인 채널 만들기",
+ "channels_createPrivateChannelDesc": "비밀 키로 암호화되어 있습니다.",
+ "channels_joinPrivateChannel": "개인 채널에 참여하기",
+ "channels_joinPrivateChannelDesc": "비밀 키를 수동으로 입력합니다.",
+ "channels_joinPublicChannel": "공개 채널에 참여하세요",
+ "channels_joinPublicChannelDesc": "누구나 이 채널에 참여할 수 있습니다.",
+ "channels_joinHashtagChannel": "해시태그 채널에 참여하세요",
+ "channels_joinHashtagChannelDesc": "누구나 해시태그 채널에 참여할 수 있습니다.",
+ "channels_scanQrCode": "QR 코드를 스캔",
+ "channels_scanQrCodeComingSoon": "곧 출시",
+ "channels_enterHashtag": "해시태그 입력",
+ "channels_hashtagHint": "예: #팀",
+ "chat_noMessages": "아직 메시지가 없습니다.",
+ "chat_sendMessageToStart": "시작하려면 메시지를 보내세요.",
+ "chat_originalMessageNotFound": "원래 메시지를 찾을 수 없음",
+ "chat_replyingTo": "{name}에게 답변",
+ "@chat_replyingTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_replyTo": "{name}님께 회신",
+ "@chat_replyTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_location": "위치",
+ "chat_sendMessageTo": "{contactName}에게 메시지를 보내",
+ "@chat_sendMessageTo": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_typeMessage": "메시지를 입력하세요...",
+ "chat_messageTooLong": "메시지가 너무 길어서 (최대 {maxBytes} 바이트).",
+ "@chat_messageTooLong": {
+ "placeholders": {
+ "maxBytes": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_messageCopied": "메시지가 복사되었습니다",
+ "chat_messageDeleted": "메시지가 삭제되었습니다.",
+ "chat_retryingMessage": "재시도 메시지",
+ "chat_retryCount": "{current}/{max} 시도",
+ "@chat_retryCount": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendGif": "GIF 보내기",
+ "chat_reply": "답변",
+ "chat_addReaction": "댓글 추가",
+ "chat_me": "나",
+ "emojiCategorySmileys": "이모티콘",
+ "emojiCategoryGestures": "제스처",
+ "emojiCategoryHearts": "심장",
+ "emojiCategoryObjects": "대상",
+ "gifPicker_title": "GIF 선택",
+ "gifPicker_searchHint": "GIF 검색...",
+ "gifPicker_poweredBy": "GIPHY에서 제공",
+ "gifPicker_noGifsFound": "GIF 파일이 없습니다.",
+ "gifPicker_failedLoad": "GIF 파일 로딩 실패",
+ "gifPicker_failedSearch": "GIF 검색에 실패했습니다.",
+ "gifPicker_noInternet": "인터넷 연결 없음",
+ "debugLog_appTitle": "앱 디버깅 로그",
+ "debugLog_bleTitle": "BLE 디버그 로그",
+ "debugLog_copyLog": "로그 기록",
+ "debugLog_clearLog": "명확한 로그",
+ "debugLog_copied": "디버깅 로그 복사",
+ "debugLog_bleCopied": "BLE 로그 복사",
+ "debugLog_noEntries": "현재 디버깅 로그는 생성되지 않았습니다.",
+ "debugLog_enableInSettings": "설정에서 앱 디버깅 로깅을 활성화합니다.",
+ "debugLog_frames": "프레임",
+ "debugLog_rawLogRx": "원시 로그-RX",
+ "debugLog_noBleActivity": "현재 BLE 관련 활동은 없습니다.",
+ "debugFrame_length": "프레임 길이: {count} 바이트",
+ "@debugFrame_length": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_command": "명령: 0x{value}",
+ "@debugFrame_command": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textMessageHeader": "텍스트 메시지 프레임:",
+ "debugFrame_destinationPubKey": "- 목적지 공개 키: {pubKey}",
+ "@debugFrame_destinationPubKey": {
+ "placeholders": {
+ "pubKey": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_timestamp": "- 시간: {timestamp}",
+ "@debugFrame_timestamp": {
+ "placeholders": {
+ "timestamp": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_flags": "- 플래그: 0x{value}",
+ "@debugFrame_flags": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textType": "- 텍스트 유형: {type} ({label})",
+ "@debugFrame_textType": {
+ "placeholders": {
+ "type": {
+ "type": "int"
+ },
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textTypeCli": "명령줄 인터페이스 (CLI)",
+ "debugFrame_textTypePlain": "단순한",
+ "debugFrame_text": "- 텍스트: \"{text}\"",
+ "@debugFrame_text": {
+ "placeholders": {
+ "text": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_hexDump": "헥스 덤프:",
+ "chat_pathManagement": "경로 관리",
+ "chat_ShowAllPaths": "모든 경로 표시",
+ "chat_routingMode": "라우팅 방식",
+ "chat_autoUseSavedPath": "자동 (저장된 경로 사용)",
+ "chat_forceFloodMode": "강수 모드 활성화",
+ "chat_recentAckPaths": "최근 사용한 ACK 경로 (사용하려면 탭):",
+ "chat_pathHistoryFull": "이력 기록은 이미 가득 차 있습니다. 항목을 삭제하여 새로운 항목을 추가할 수 있습니다.",
+ "chat_hopSingular": "점프",
+ "chat_hopPlural": "홉",
+ "chat_hopsCount": "{count} {count, plural, =1{홉} other{홉}}",
+ "@chat_hopsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_successes": "성공 사례",
+ "chat_removePath": "경로 제거",
+ "chat_noPathHistoryYet": "아직 경로 기록이 없습니다.\n경로를 찾기 위해 메시지를 보내세요.",
+ "chat_pathActions": "경로 작업:",
+ "chat_setCustomPath": "사용자 지정 경로 설정",
+ "chat_setCustomPathSubtitle": "수동으로 경로를 지정",
+ "chat_clearPath": "명확한 길",
+ "chat_clearPathSubtitle": "다음 전송 시, 강제 재전송 설정",
+ "chat_pathCleared": "경로가 확보되었습니다. 다음 메시지는 경로를 다시 찾을 것입니다.",
+ "chat_floodModeSubtitle": "앱 바에서 라우팅 스위치를 사용",
+ "chat_floodModeEnabled": "홍수 모드 활성화됨. 앱 바의 경로 아이콘을 사용하여 다시 전환할 수 있습니다.",
+ "chat_fullPath": "전체 경로",
+ "chat_pathDetailsNotAvailable": "경로 정보는 아직 제공되지 않습니다. 메시지를 보내어 다시 시도해 보세요.",
+ "chat_pathSetHops": "Path set: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
+ "@chat_pathSetHops": {
+ "placeholders": {
+ "hopCount": {
+ "type": "int"
+ },
+ "status": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_pathSavedLocally": "로컬에 저장. 동기화 연결",
+ "chat_pathDeviceConfirmed": "장치 확인 완료.",
+ "chat_pathDeviceNotConfirmed": "기기가 아직 확인되지 않았습니다.",
+ "chat_type": "종류",
+ "chat_path": "경로",
+ "chat_publicKey": "공개 키",
+ "chat_compressOutgoingMessages": "전송되는 메시지 압축",
+ "chat_floodForced": "홍수 (강제)",
+ "chat_directForced": "직접적인 (강제적인)",
+ "chat_hopsForced": "{count}번 띄우기 (강제)",
+ "@chat_hopsForced": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_floodAuto": "홍수 (자동)",
+ "chat_direct": "직접",
+ "chat_poiShared": "공유된 POI",
+ "chat_unread": "읽지 않음: {count}",
+ "@chat_unread": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_openLink": "링크를 열기?",
+ "chat_openLinkConfirmation": "이 링크를 브라우저에서 열고 싶으신가요?",
+ "chat_open": "열기",
+ "chat_couldNotOpenLink": "링크를 열 수 없습니다: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "유효하지 않은 링크 형식",
+ "map_title": "노드 매핑",
+ "map_lineOfSight": "시야",
+ "map_losScreenTitle": "시야",
+ "map_noNodesWithLocation": "위치 정보가 있는 노드가 없습니다.",
+ "map_nodesNeedGps": "노드는 지도에 표시되려면 GPS 좌표를 공유해야 합니다.",
+ "map_nodesCount": "노드: {count}",
+ "@map_nodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_pinsCount": "핀: {count}",
+ "@map_pinsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_chat": "채팅",
+ "map_repeater": "반복기",
+ "map_room": "방",
+ "map_sensor": "센서",
+ "map_pinDm": "핀 (DM)",
+ "map_pinPrivate": "개인 계정",
+ "map_pinPublic": "공개 (일반 공개)",
+ "map_lastSeen": "마지막으로 목격",
+ "map_disconnectConfirm": "이 장치와의 연결을 해제하시겠습니까?",
+ "map_from": "~부터",
+ "map_source": "출처",
+ "map_flags": "깃발",
+ "map_shareMarkerHere": "여기에서 마커 공유",
+ "map_setAsMyLocation": "내 위치로 설정",
+ "map_pinLabel": "핀 라벨",
+ "map_label": "레이블",
+ "map_pointOfInterest": "관심 지점",
+ "map_sendToContact": "연락처로 보내기",
+ "map_sendToChannel": "채널로 전송",
+ "map_noChannelsAvailable": "사용 가능한 채널이 없습니다.",
+ "map_publicLocationShare": "공개 장소 공유",
+ "map_publicLocationShareConfirm": "현재 {channelLabel} 채널에서 위치 정보를 공유하려고 합니다. 이 채널은 공개되어 있으며, PSK를 가진 모든 사용자가 이 위치 정보를 볼 수 있습니다.",
+ "@map_publicLocationShareConfirm": {
+ "placeholders": {
+ "channelLabel": {
+ "type": "String"
+ }
+ }
+ },
+ "map_connectToShareMarkers": "장치를 연결하여 마커를 공유",
+ "map_filterNodes": "필터 노드",
+ "map_nodeTypes": "노드 유형",
+ "map_chatNodes": "채팅 노드",
+ "map_repeaters": "다시 보내는 장치",
+ "map_otherNodes": "다른 노드",
+ "map_keyPrefix": "핵심 접두사",
+ "map_filterByKeyPrefix": "주요 접두사 기준으로 필터링",
+ "map_publicKeyPrefix": "공개 키 접두사",
+ "map_markers": "마커",
+ "map_showSharedMarkers": "공통 마커 표시",
+ "map_showGuessedLocations": "추정된 노드 위치 표시",
+ "map_showDiscoveryContacts": "디스커버리 담당자 연락처 보기",
+ "map_guessedLocation": "추측된 위치",
+ "map_lastSeenTime": "마지막으로 확인된 시간",
+ "map_sharedPin": "공유 비밀번호",
+ "map_joinRoom": "방에 참여",
+ "map_manageRepeater": "리피터 관리",
+ "map_tapToAdd": "노드에 클릭하여 경로에 추가합니다.",
+ "map_runTrace": "경로 추적",
+ "map_removeLast": "마지막 항목 삭제",
+ "map_pathTraceCancelled": "경로 추적 기능이 취소되었습니다.",
+ "mapCache_title": "오프라인 지도 캐시",
+ "mapCache_selectAreaFirst": "캐시할 영역을 먼저 선택하세요",
+ "mapCache_noTilesToDownload": "이 지역에 다운로드할 타일이 없습니다.",
+ "mapCache_downloadTilesTitle": "타일 다운로드",
+ "mapCache_downloadTilesPrompt": "{count}개의 타일을 오프라인 사용을 위해 다운로드하시겠습니까?",
+ "@mapCache_downloadTilesPrompt": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadAction": "다운로드",
+ "mapCache_cachedTiles": "{count} 개의 타일 캐시",
+ "@mapCache_cachedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_cachedTilesWithFailed": "Cached {downloaded} tiles ({failed} failed)",
+ "@mapCache_cachedTilesWithFailed": {
+ "placeholders": {
+ "downloaded": {
+ "type": "int"
+ },
+ "failed": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_clearOfflineCacheTitle": "오프라인 캐시 삭제",
+ "mapCache_clearOfflineCachePrompt": "모든 캐시된 지도 템플릿을 삭제하시겠습니까?",
+ "mapCache_offlineCacheCleared": "오프라인 캐시 삭제",
+ "mapCache_noAreaSelected": "선택된 영역 없음",
+ "mapCache_cacheArea": "캐시 영역",
+ "mapCache_useCurrentView": "현재 보기 유지",
+ "mapCache_zoomRange": "줌 기능 범위",
+ "mapCache_estimatedTiles": "예상되는 타일 개수: {count}",
+ "@mapCache_estimatedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadedTiles": "Downloaded {completed} / {total}",
+ "@mapCache_downloadedTiles": {
+ "placeholders": {
+ "completed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadTilesButton": "타일 다운로드",
+ "mapCache_clearCacheButton": "캐시 삭제",
+ "mapCache_failedDownloads": "실패한 다운로드: {count}",
+ "@mapCache_failedDownloads": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_boundsLabel": "N {north}, S {south}, E {east}, W {west}",
+ "@mapCache_boundsLabel": {
+ "placeholders": {
+ "north": {
+ "type": "String"
+ },
+ "south": {
+ "type": "String"
+ },
+ "east": {
+ "type": "String"
+ },
+ "west": {
+ "type": "String"
+ }
+ }
+ },
+ "time_justNow": "방금",
+ "time_minutesAgo": "{minutes}분 전",
+ "@time_minutesAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hoursAgo": "{hours}h ago",
+ "@time_hoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "time_daysAgo": "{days}일 전",
+ "@time_daysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hour": "시간",
+ "time_hours": "시간",
+ "time_day": "하루",
+ "time_days": "일",
+ "time_week": "주",
+ "time_weeks": "몇 주",
+ "time_month": "달",
+ "time_months": "개월",
+ "time_minutes": "분",
+ "time_allTime": "모든 시간",
+ "dialog_disconnect": "연결 해제",
+ "dialog_disconnectConfirm": "이 장치와의 연결을 해제하시겠습니까?",
+ "login_repeaterLogin": "다시 로그인",
+ "login_roomLogin": "방 서버 로그인",
+ "login_password": "비밀번호",
+ "login_enterPassword": "비밀번호를 입력하세요",
+ "login_savePassword": "비밀번호 저장",
+ "login_savePasswordSubtitle": "비밀번호는 이 장치에 안전하게 저장됩니다.",
+ "login_repeaterDescription": "반복기 비밀번호를 입력하여 설정 및 상태를 확인하십시오.",
+ "login_roomDescription": "설정 및 상태에 액세스하려면 방 비밀번호를 입력하세요.",
+ "login_routing": "라우팅",
+ "login_routingMode": "라우팅 모드",
+ "login_autoUseSavedPath": "자동 (저장된 경로 사용)",
+ "login_forceFloodMode": "강수 모드 활성화",
+ "login_managePaths": "경로 관리",
+ "login_login": "로그인",
+ "login_attempt": "시도 {current}/{max}",
+ "@login_attempt": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "login_failed": "로그인 실패: {error}",
+ "@login_failed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "login_failedMessage": "로그인에 실패했습니다. 비밀번호가 잘못되었거나, 연결이 되지 않는 것 같습니다.",
+ "common_reload": "다시 로드",
+ "common_clear": "명확하게",
+ "path_currentPath": "현재 경로: {path}",
+ "@path_currentPath": {
+ "placeholders": {
+ "path": {
+ "type": "String"
+ }
+ }
+ },
+ "path_usingHopsPath": "Using {count} {count, plural, =1{hop} other{hops}} path",
+ "@path_usingHopsPath": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "path_enterCustomPath": "사용자 지정 경로 입력",
+ "path_currentPathLabel": "현재 경로",
+ "path_hexPrefixInstructions": "각 단계에 대한 2자리 헥사데진 접두사를 쉼표로 구분하여 입력하세요.",
+ "path_hexPrefixExample": "예시: A1, F2, 3C (각 노드는 자신의 공개 키의 첫 번째 바이트를 사용)",
+ "path_labelHexPrefixes": "경로 (헥스 접두사)",
+ "path_helperMaxHops": "최대 64개의 홉. 각 접두사는 2개의 16진수 문자(1바이트)로 구성됩니다.",
+ "path_selectFromContacts": "또 연락처 목록에서 선택:",
+ "path_noRepeatersFound": "반복 장치 또는 서버는 찾을 수 없습니다.",
+ "path_customPathsRequire": "사용자 정의 경로에는 메시지를 전달할 수 있는 중간 경로가 필요합니다.",
+ "path_invalidHexPrefixes": "유효하지 않은 16진수 접두사: {prefixes}",
+ "@path_invalidHexPrefixes": {
+ "placeholders": {
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "path_tooLong": "경로가 너무 길어. 최대 64개의 연결만 허용됩니다.",
+ "path_setPath": "경로 설정",
+ "repeater_management": "리피터 관리",
+ "room_management": "방 서버 관리",
+ "repeater_managementTools": "관리 도구",
+ "repeater_status": "상태",
+ "repeater_statusSubtitle": "반복 장비의 상태, 통계, 및 이웃 장비 목록 보기",
+ "repeater_telemetry": "텔레메트리",
+ "repeater_telemetrySubtitle": "센서 및 시스템 상태에 대한 통신 데이터를 확인",
+ "repeater_cli": "명령줄 인터페이스 (CLI)",
+ "repeater_cliSubtitle": "리피터에 명령을 전송",
+ "repeater_neighbors": "이웃",
+ "repeater_neighborsSubtitle": "0홉 이웃 노드를 확인합니다.",
+ "repeater_settings": "설정",
+ "repeater_settingsSubtitle": "리피터 파라미터 설정",
+ "repeater_statusTitle": "반복 장치 상태",
+ "repeater_routingMode": "라우팅 방식",
+ "repeater_autoUseSavedPath": "자동 (저장된 경로 사용)",
+ "repeater_forceFloodMode": "강수 모드 활성화",
+ "repeater_pathManagement": "경로 관리",
+ "repeater_refresh": "새롭게",
+ "repeater_statusRequestTimeout": "상태 확인 요청이 시간 초과되었습니다.",
+ "repeater_errorLoadingStatus": "상태 로딩 오류: {error}",
+ "@repeater_errorLoadingStatus": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_systemInformation": "시스템 정보",
+ "repeater_battery": "배터리",
+ "repeater_clockAtLogin": "로그인 시 시간 표시",
+ "repeater_uptime": "가동 시간",
+ "repeater_queueLength": "대기 줄의 길이",
+ "repeater_debugFlags": "디버깅 플래그",
+ "repeater_radioStatistics": "라디오 통계",
+ "repeater_lastRssi": "마지막 RSSI 값",
+ "repeater_lastSnr": "마지막 SNR",
+ "repeater_noiseFloor": "잡음 수준",
+ "repeater_txAirtime": "TX 에어타임",
+ "repeater_rxAirtime": "RX 에어타임",
+ "repeater_packetStatistics": "패킷 통계",
+ "repeater_sent": "발송",
+ "repeater_received": "수신",
+ "repeater_duplicates": "중복",
+ "repeater_daysHoursMinsSecs": "{days}일 {hours}시간 {minutes}분 {seconds}초",
+ "@repeater_daysHoursMinsSecs": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_packetTxTotal": "총: {total}, 홍수: {flood}, 직접: {direct}",
+ "@repeater_packetTxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_packetRxTotal": "총: {total}, 홍수: {flood}, 직접: {direct}",
+ "@repeater_packetRxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesFloodDirect": "홍수: {flood}, 직접: {direct}",
+ "@repeater_duplicatesFloodDirect": {
+ "placeholders": {
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesTotal": "총: {total}",
+ "@repeater_duplicatesTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_settingsTitle": "리피터 설정",
+ "repeater_basicSettings": "기본 설정",
+ "repeater_repeaterName": "반복 장비 이름",
+ "repeater_repeaterNameHelper": "이 반복기용 표시 이름",
+ "repeater_adminPassword": "관리자 비밀번호",
+ "repeater_adminPasswordHelper": "전체 접근 권한 비밀번호",
+ "repeater_guestPassword": "게스트 비밀번호",
+ "repeater_guestPasswordHelper": "읽기 전용 접근 비밀번호",
+ "repeater_radioSettings": "라디오 설정",
+ "repeater_frequencyMhz": "주파수 (MHz)",
+ "repeater_frequencyHelper": "300-2500 MHz",
+ "repeater_txPower": "TX 파워",
+ "repeater_txPowerHelper": "1~30 dBm",
+ "repeater_bandwidth": "대역폭",
+ "repeater_spreadingFactor": "분산 계수",
+ "repeater_codingRate": "코딩 속도",
+ "repeater_locationSettings": "위치 설정",
+ "repeater_latitude": "위도",
+ "repeater_latitudeHelper": "십진법 위도 (예: 37.7749)",
+ "repeater_longitude": "경도",
+ "repeater_longitudeHelper": "십진법 위도 (예: -122.4194)",
+ "repeater_features": "특징",
+ "repeater_packetForwarding": "패킷 전송",
+ "repeater_packetForwardingSubtitle": "리피터가 패킷을 전달하도록 설정",
+ "repeater_guestAccess": "게스트 접근",
+ "repeater_guestAccessSubtitle": "게스트의 읽기 전용 접근 권한 허용",
+ "repeater_privacyMode": "개인 정보 보호 모드",
+ "repeater_privacyModeSubtitle": "광고에 이름/위치 정보 숨기기",
+ "repeater_advertisementSettings": "광고 설정",
+ "repeater_localAdvertInterval": "지역 광고 시간 간격",
+ "repeater_localAdvertIntervalMinutes": "{minutes} 분",
+ "@repeater_localAdvertIntervalMinutes": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_floodAdvertInterval": "홍수 광고 간격",
+ "repeater_floodAdvertIntervalHours": "{hours} 시간",
+ "@repeater_floodAdvertIntervalHours": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_encryptedAdvertInterval": "암호화된 광고 간격",
+ "repeater_dangerZone": "위험 구역",
+ "repeater_rebootRepeater": "리부트 반복",
+ "repeater_rebootRepeaterSubtitle": "리피터 장치를 재시작하세요.",
+ "repeater_rebootRepeaterConfirm": "반복기를 재부팅하시려는 것이 맞으신가요?",
+ "repeater_regenerateIdentityKey": "아이디 키 재 생성",
+ "repeater_regenerateIdentityKeySubtitle": "새로운 공개/개인 키 쌍 생성",
+ "repeater_regenerateIdentityKeyConfirm": "이를 통해 리피터에 새로운 식별자를 할당합니다. 계속 진행하시겠습니까?",
+ "repeater_eraseFileSystem": "파일 시스템 삭제",
+ "repeater_eraseFileSystemSubtitle": "리피터 파일 시스템을 포맷합니다.",
+ "repeater_eraseFileSystemConfirm": "경고: 이 작업은 리피터에 있는 모든 데이터를 삭제합니다. 이 작업을 되돌릴 수 없습니다!",
+ "repeater_eraseSerialOnly": "'Erase' 기능은 시리얼 콘솔을 통해서만 사용할 수 있습니다.",
+ "repeater_commandSent": "명령 전송: {command}",
+ "@repeater_commandSent": {
+ "placeholders": {
+ "command": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorSendingCommand": "명령 전송 오류: {error}",
+ "@repeater_errorSendingCommand": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_confirm": "확인",
+ "repeater_settingsSaved": "설정이 성공적으로 저장되었습니다.",
+ "repeater_errorSavingSettings": "설정 저장 오류: {error}",
+ "@repeater_errorSavingSettings": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_refreshBasicSettings": "기본 설정 초기화",
+ "repeater_refreshRadioSettings": "라디오 설정 초기화",
+ "repeater_refreshTxPower": "TX 전원 재설정",
+ "repeater_refreshLocationSettings": "위치 설정 초기화",
+ "repeater_refreshPacketForwarding": "패킷 전송 재시작",
+ "repeater_refreshGuestAccess": "게스트 접근 권한 갱신",
+ "repeater_refreshPrivacyMode": "개인 정보 보호 모드 재설정",
+ "repeater_refreshAdvertisementSettings": "광고 설정 재설정",
+ "repeater_refreshed": "{label}가 갱신됨",
+ "@repeater_refreshed": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorRefreshing": "{label}를 새로 고침 중 오류 발생",
+ "@repeater_errorRefreshing": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliTitle": "리피터 CLI",
+ "repeater_debugNextCommand": "다음 명령 디버깅",
+ "repeater_commandHelp": "명령 도움",
+ "repeater_clearHistory": "명확한 역사",
+ "repeater_noCommandsSent": "아직 명령이 전송되지 않았습니다.",
+ "repeater_typeCommandOrUseQuick": "아래에 명령어를 입력하거나, 빠른 명령어를 사용하세요.",
+ "repeater_enterCommandHint": "명령어를 입력하세요...",
+ "repeater_previousCommand": "이전 명령어",
+ "repeater_nextCommand": "다음 명령어",
+ "repeater_enterCommandFirst": "먼저 명령어를 입력하세요",
+ "repeater_cliCommandFrameTitle": "CLI 명령어 프레임",
+ "repeater_cliCommandError": "오류: {error}",
+ "@repeater_cliCommandError": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliQuickGetName": "이름을 알려주세요",
+ "repeater_cliQuickGetRadio": "라디오 듣기",
+ "repeater_cliQuickGetTx": "TX 획득",
+ "repeater_cliQuickNeighbors": "이웃",
+ "repeater_cliQuickVersion": "버전",
+ "repeater_cliQuickAdvertise": "광고",
+ "repeater_cliQuickClock": "시계",
+ "repeater_cliHelpAdvert": "광고 패킷을 발송",
+ "repeater_cliHelpReboot": "장치를 재부팅합니다. (참고: '시간 초과' 오류가 발생할 수 있으며, 이는 정상적인 현상입니다)",
+ "repeater_cliHelpClock": "각 기기의 시계에 표시되는 현재 시간",
+ "repeater_cliHelpPassword": "장치에 새로운 관리자 비밀번호를 설정합니다.",
+ "repeater_cliHelpVersion": "장치 버전 및 펌웨어 빌드 날짜를 표시합니다.",
+ "repeater_cliHelpClearStats": "다양한 통계 지표를 0으로 초기화합니다.",
+ "repeater_cliHelpSetAf": "에어 타임 요소를 설정합니다.",
+ "repeater_cliHelpSetTx": "LoRa 전송 전력을 dBm 단위로 설정합니다. (설정을 적용하려면 재부팅 필요)",
+ "repeater_cliHelpSetRepeat": "이 노드에 대한 리피터 역할을 활성화하거나 비활성화합니다.",
+ "repeater_cliHelpSetAllowReadOnly": "(방 서버) '켜짐' 상태인 경우, 빈 비밀번호로 로그인할 수 있지만, 방에 게시할 수는 없습니다 (단, 읽기만 가능).",
+ "repeater_cliHelpSetFloodMax": "들어오는 플러드 패킷의 최대 홉 수를 설정합니다 (최대 홉 수보다 크거나 같으면 패킷은 전달되지 않습니다).",
+ "repeater_cliHelpSetIntThresh": "간섭 임계값을 설정합니다 (dB 단위). 기본값은 14입니다. 0으로 설정하면 채널 간섭 감지 기능을 비활성화합니다.",
+ "repeater_cliHelpSetAgcResetInterval": "자동 게인 제어기를 재설정하는 간격을 설정합니다. 0으로 설정하면 비활성화됩니다.",
+ "repeater_cliHelpSetMultiAcks": "'더블 ACK' 기능을 활성화하거나 비활성화할 수 있습니다.",
+ "repeater_cliHelpSetAdvertInterval": "로컬 (제로 홉) 광고 패킷을 전송하는 간격 (분 단위)을 설정합니다. 0으로 설정하면 비활성화됩니다.",
+ "repeater_cliHelpSetFloodAdvertInterval": "시간 단위로 광고 패킷을 전송하는 간격을 설정합니다. 0으로 설정하면 비활성화됩니다.",
+ "repeater_cliHelpSetGuestPassword": "게스트 비밀번호를 설정하거나 업데이트합니다. (반복 사용자, 게스트 로그인 시 \"통계 가져오기\" 요청을 보낼 수 있음)",
+ "repeater_cliHelpSetName": "광고 이름을 설정합니다.",
+ "repeater_cliHelpSetLat": "광고 지도의 위도를 설정합니다. (십진법 단위)",
+ "repeater_cliHelpSetLon": "광고 지도의 경도를 설정합니다. (십진도)",
+ "repeater_cliHelpSetRadio": "완전히 새로운 라디오 파라미터를 설정하고, 선호 사항에 저장합니다. 적용하려면 \"재부팅\" 명령이 필요합니다.",
+ "repeater_cliHelpSetRxDelay": "(실험용) 기본 설정 (최소 1이어야 함)으로, 수신된 패킷에 약간의 지연을 적용하며, 신호 강도/점수를 기준으로 설정합니다. 0으로 설정하면 비활성화됩니다.",
+ "repeater_cliHelpSetTxDelay": "공통 패킷의 전송 지연 시간을 설정하며, 시간-공기 시간과 무작위 슬롯 시스템을 곱하여 충돌 가능성을 줄입니다.",
+ "repeater_cliHelpSetDirectTxDelay": "txdelay와 동일하게, 하지만 직접 모드 패킷 전송 시 무작위 지연을 적용하는 경우",
+ "repeater_cliHelpSetBridgeEnabled": "브리지 활성화/비활성화",
+ "repeater_cliHelpSetBridgeDelay": "패킷 재전송 전에 지연 시간을 설정합니다.",
+ "repeater_cliHelpSetBridgeSource": "브리지가 수신된 패킷을 다시 전송할지, 아니면 전송된 패킷을 다시 전송할지 선택하십시오.",
+ "repeater_cliHelpSetBridgeBaud": "rs232 브리지에 대한 직렬 통신 속도(baud rate)를 설정합니다.",
+ "repeater_cliHelpSetBridgeSecret": "ESPNow 브리지에 대한 비밀 설정",
+ "repeater_cliHelpSetAdcMultiplier": "특정 보드에서만 지원되는 방식으로, 보고되는 배터리 전압을 조정하기 위한 사용자 정의 요소를 설정할 수 있습니다.",
+ "repeater_cliHelpTempRadio": "주어진 시간(분) 동안 임시 라디오 파라미터를 설정하고, 이후 원래 라디오 파라미터로 되돌립니다. (설정을 저장하지 않습니다).",
+ "repeater_cliHelpSetPerm": "ACL을 수정합니다. \"permissions\" 값이 0인 경우, 일치하는 항목(pubkey 접두사)을 제거합니다. pubkey-hex 길이가 완전하고 현재 ACL에 없는 경우 새로운 항목을 추가합니다. pubkey 접두사를 기준으로 항목을 업데이트합니다. 권한 비트는 펌웨어 역할에 따라 다르지만, 하위 2비트는 다음과 같습니다: 0 (게스트), 1 (읽기 전용), 2 (읽기/쓰기), 3 (관리자)",
+ "repeater_cliHelpGetBridgeType": "브리지형, RS232, ESPNOW 지원",
+ "repeater_cliHelpLogStart": "패킷 로깅을 파일 시스템으로 시작합니다.",
+ "repeater_cliHelpLogStop": "패킷 로깅을 파일 시스템으로 저장하는 것을 중단합니다.",
+ "repeater_cliHelpLogErase": "파일 시스템에서 패킷 로그를 삭제합니다.",
+ "repeater_cliHelpNeighbors": "제로 홉 광고를 통해 수신된 다른 리피터 노드 목록을 보여줍니다. 각 줄은 ID-프리픽스-16진수:타임스탬프:SNR-횟수-4 형식입니다.",
+ "repeater_cliHelpNeighborRemove": "이 함수는 지정된 pubkey 접두사(16진수)와 일치하는 첫 번째 항목을 이웃 목록에서 제거합니다.",
+ "repeater_cliHelpRegion": "(단일 시리즈) 정의된 모든 지역과 현재 홍수 허가 정보를 나열합니다.",
+ "repeater_cliHelpRegionLoad": "참고: 이는 여러 명령을 한 번에 실행하는 특별한 방식입니다. 각 후속 명령은 영역 이름이며 (부모 계층 구조를 나타내기 위해 공백으로 들여쓰기하며, 최소 1개의 공백을 사용) 공백으로 끝나는 줄 또는 명령을 보내어 종료합니다.",
+ "repeater_cliHelpRegionGet": "주어진 이름 접두사(또는 전역 검색을 위한 \"\\*\" 사용)를 사용하여 특정 지역을 검색합니다. 결과를 \"-> 지역 이름 (상위 지역 이름) 'F'\" 형태로 반환합니다.",
+ "repeater_cliHelpRegionPut": "주어진 이름으로 지역 정의를 추가하거나 업데이트합니다.",
+ "repeater_cliHelpRegionRemove": "지정된 이름으로 특정 영역 정의를 제거합니다. (정확히 일치해야 하며, 하위 영역은 존재하지 않아야 합니다)",
+ "repeater_cliHelpRegionAllowf": "지정된 영역에 대한 '물' 접근 권한을 설정합니다. ('*'는 전역/기존 범위에 해당)",
+ "repeater_cliHelpRegionDenyf": "지정된 영역에 대해 'Flood' 권한을 제거합니다. (참고: 현재 단계에서는 전역/기존 범위에서 이 기능을 사용하지 않는 것이 좋습니다!!)",
+ "repeater_cliHelpRegionHome": "현재 '홈' 지역으로 응답합니다. (아직 적용되지 않았으며, 향후 사용을 위해 예약됨)",
+ "repeater_cliHelpRegionHomeSet": "'홈' 지역을 설정합니다.",
+ "repeater_cliHelpRegionSave": "지역 목록/지도를 저장에 유지합니다.",
+ "repeater_cliHelpGps": "GPS 상태를 표시합니다. GPS가 꺼져 있으면 \"꺼짐\"이라고 표시하고, 켜져 있으면 \"켜짐\", 상태, 위치 정보, 위성 수 등을 표시합니다.",
+ "repeater_cliHelpGpsOnOff": "GPS 전원 상태를 켜고 끄는 기능.",
+ "repeater_cliHelpGpsSync": "노드 시간을 GPS 시계와 동기화합니다.",
+ "repeater_cliHelpGpsSetLoc": "노드의 위치를 GPS 좌표로 설정하고, 설정을 저장합니다.",
+ "repeater_cliHelpGpsAdvert": "노드의 위치 광고 설정:\n- none: 광고에 위치 정보를 포함하지 않음\n- share: GPS 위치 정보를 공유 (SensorManager에서 가져옴)\n- prefs: 설정에 저장된 위치를 광고",
+ "repeater_cliHelpGpsAdvertSet": "위치 기반 광고 설정 구성",
+ "repeater_commandsListTitle": "명령 목록",
+ "repeater_commandsListNote": "참고: 다양한 \"set...\" 명령과 함께 \"get...\" 명령도 존재합니다.",
+ "repeater_general": "일반",
+ "repeater_settingsCategory": "설정",
+ "repeater_bridge": "다리",
+ "repeater_logging": "로깅",
+ "repeater_neighborsRepeaterOnly": "이웃 (단방향 통신만 지원)",
+ "repeater_regionManagementRepeaterOnly": "지역 관리 (단, 중계 기능만 사용)",
+ "repeater_regionNote": "지역별 관리 기능을 도입하여 지역 정의 및 권한 관리를 수행할 수 있습니다.",
+ "repeater_gpsManagement": "GPS 관리",
+ "repeater_gpsNote": "GPS 명령이 위치 관련 주제를 관리하기 위해 도입되었습니다.",
+ "telemetry_receivedData": "수신된 통신 데이터",
+ "telemetry_requestTimeout": "원격 모니터링 요청이 시간 초과되었습니다.",
+ "telemetry_errorLoading": "{error} 오류로 인해 통신 데이터를 로드하지 못했습니다.",
+ "@telemetry_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_noData": "텔레메트리 데이터는 제공되지 않습니다.",
+ "telemetry_channelTitle": "채널 {channel}",
+ "@telemetry_channelTitle": {
+ "placeholders": {
+ "channel": {
+ "type": "int"
+ }
+ }
+ },
+ "telemetry_batteryLabel": "배터리",
+ "telemetry_voltageLabel": "전압",
+ "telemetry_mcuTemperatureLabel": "MCU의 온도",
+ "telemetry_temperatureLabel": "온도",
+ "telemetry_currentLabel": "현재",
+ "telemetry_batteryValue": "{percent}% / {volts}V",
+ "@telemetry_batteryValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ },
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_voltageValue": "{volts}V",
+ "@telemetry_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_currentValue": "{amps}A",
+ "@telemetry_currentValue": {
+ "placeholders": {
+ "amps": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_temperatureValue": "{celsius}°C / {fahrenheit}°F",
+ "@telemetry_temperatureValue": {
+ "placeholders": {
+ "celsius": {
+ "type": "String"
+ },
+ "fahrenheit": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_receivedData": "이웃 정보 수집",
+ "neighbors_requestTimedOut": "이웃들이 시간 제한을 요청하고 있습니다.",
+ "neighbors_errorLoading": "이웃 정보 로딩 중 오류: {error}",
+ "@neighbors_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_repeatersNeighbors": "반복기, 이웃",
+ "neighbors_noData": "이웃 정보는 없습니다.",
+ "neighbors_unknownContact": "알 수 없는 {pubkey}",
+ "@neighbors_unknownContact": {
+ "placeholders": {
+ "pubkey": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_heardAgo": "Heard: {time} ago",
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_title": "패킷 경로",
+ "channelPath_viewMap": "지도 보기",
+ "channelPath_otherObservedPaths": "관찰된 다른 경로",
+ "channelPath_repeaterHops": "반복 홉",
+ "channelPath_noHopDetails": "이 패키지에 대한 자세한 정보는 제공되지 않습니다.",
+ "channelPath_messageDetails": "메시지 세부 정보",
+ "channelPath_senderLabel": "발신자",
+ "channelPath_timeLabel": "시간",
+ "channelPath_repeatsLabel": "반복",
+ "channelPath_pathLabel": "경로 {index}",
+ "channelPath_observedLabel": "관찰",
+ "channelPath_observedPathTitle": "관찰된 경로 {index} • {hops}",
+ "@channelPath_observedPathTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ },
+ "hops": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noLocationData": "위치 정보 없음",
+ "channelPath_timeWithDate": "{day}/{month} {time}",
+ "@channelPath_timeWithDate": {
+ "placeholders": {
+ "day": {
+ "type": "int"
+ },
+ "month": {
+ "type": "int"
+ },
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_timeOnly": "{time}",
+ "@channelPath_timeOnly": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_unknownPath": "알 수 없음",
+ "channelPath_floodPath": "홍수",
+ "channelPath_directPath": "직접",
+ "channelPath_observedZeroOf": "{total} 중 0개",
+ "@channelPath_observedZeroOf": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_observedSomeOf": "{observed} of {total} hops",
+ "@channelPath_observedSomeOf": {
+ "placeholders": {
+ "observed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_mapTitle": "경로 지도",
+ "channelPath_noRepeaterLocations": "이 경로에 대한 중계기 설치 위치는 없습니다.",
+ "channelPath_primaryPath": "경로 {index} (주 경로)",
+ "@channelPath_primaryPath": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "@channelPath_pathLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_pathLabelTitle": "경로",
+ "channelPath_observedPathHeader": "관찰된 경로",
+ "channelPath_selectedPathLabel": "{label} • {prefixes}",
+ "@channelPath_selectedPathLabel": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ },
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noHopDetailsAvailable": "이 패킷에 대한 이동 정보는 제공되지 않습니다.",
+ "channelPath_unknownRepeater": "알 수 없는 중계기",
+ "community_title": "지역 사회",
+ "community_create": "커뮤니티 만들기",
+ "community_createDesc": "새로운 커뮤니티를 만들고 QR 코드를 통해 공유하세요.",
+ "community_join": "참여하기",
+ "community_joinTitle": "커뮤니티에 참여하기",
+ "community_joinConfirmation": "{name}님, 커뮤니티에 참여하고 싶으신가요?",
+ "@community_joinConfirmation": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanQr": "커뮤니티 QR 스캔",
+ "community_scanInstructions": "카메라를 커뮤니티 QR 코드 방향으로 향하게 하세요.",
+ "community_showQr": "QR 코드 표시",
+ "community_publicChannel": "지역 사회 대상",
+ "community_hashtagChannel": "커뮤니티 해시태그",
+ "community_name": "지역 이름",
+ "community_enterName": "커뮤니티 이름을 입력하세요",
+ "community_created": "커뮤니티 \"{name}\"이 생성되었습니다.",
+ "@community_created": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_joined": "\"{name}\" 커뮤니티에 가입",
+ "@community_joined": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_qrTitle": "커뮤니티 공유",
+ "community_qrInstructions": "이 QR 코드를 스캔하여 \"{name}\"에 가입하세요.",
+ "@community_qrInstructions": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_hashtagPrivacyHint": "커뮤니티 해시태그 채널은 커뮤니티 구성원만 가입할 수 있습니다.",
+ "community_invalidQrCode": "유효하지 않은 커뮤니티 QR 코드",
+ "community_alreadyMember": "이미 회원인 경우",
+ "community_alreadyMemberMessage": "이미 {name}의 회원입니다.",
+ "@community_alreadyMemberMessage": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addPublicChannel": "커뮤니티 공개 채널 추가",
+ "community_addPublicChannelHint": "이 커뮤니티에 공개 채널을 자동으로 추가합니다.",
+ "community_noCommunities": "아직 어느 커뮤니티도 가입하지 않았습니다.",
+ "community_scanOrCreate": "QR 코드를 스캔하거나 커뮤니티를 만들어 시작하세요.",
+ "community_manageCommunities": "커뮤니티 관리",
+ "community_delete": "커뮤니티 떠나기",
+ "community_deleteConfirm": "{name}을 묻어두나요?",
+ "@community_deleteConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_deleteChannelsWarning": "또한, 이 기능은 {count}개의 채널과 그에 해당하는 메시지를 삭제합니다.",
+ "@community_deleteChannelsWarning": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "community_deleted": "지역 커뮤니티 \"{name}\"",
+ "@community_deleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "비밀 복원",
+ "community_regenerateSecretConfirm": "{name}의 비밀 키를 재생성하시겠습니까? 모든 회원은 계속 통신을 위해 새로운 QR 코드를 스캔해야 합니다.",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "재생",
+ "community_secretRegenerated": "{name}을 위한 비밀 정보가 복원되었습니다.",
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_updateSecret": "비밀 업데이트",
+ "community_secretUpdated": "{name}을 위한 비밀 정보 업데이트",
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanToUpdateSecret": "새로운 QR 코드를 스캔하여 {name}의 비밀번호를 업데이트하세요.",
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addHashtagChannel": "커뮤니티 해시태그 추가",
+ "community_addHashtagChannelDesc": "이 커뮤니티를 위한 해시태그 채널을 추가하세요.",
+ "community_selectCommunity": "커뮤니티 선택",
+ "community_regularHashtag": "일반 해시태그",
+ "community_regularHashtagDesc": "공개 해시태그 (누구나 참여 가능)",
+ "community_communityHashtag": "커뮤니티 해시태그",
+ "community_communityHashtagDesc": "지역 주민을 위한",
+ "community_forCommunity": "{name} 님께",
+ "@community_forCommunity": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "listFilter_tooltip": "필터링 및 정렬",
+ "listFilter_sortBy": "정렬 기준 선택",
+ "listFilter_latestMessages": "최신 메시지",
+ "listFilter_heardRecently": "최근에 들었습니다",
+ "listFilter_az": "A부터 Z까지",
+ "listFilter_filters": "필터",
+ "listFilter_all": "모든",
+ "listFilter_favorites": "관심 목록",
+ "listFilter_addToFavorites": "즐겨찾으로 추가",
+ "listFilter_removeFromFavorites": "즐겨찾에서 제거",
+ "listFilter_users": "사용자",
+ "listFilter_repeaters": "다시 보내는 장치",
+ "listFilter_roomServers": "방 내 서버",
+ "listFilter_unreadOnly": "읽지 않은 항목만",
+ "listFilter_newGroup": "새로운 그룹",
+ "pathTrace_you": "당신",
+ "pathTrace_failed": "경로 추적 실패.",
+ "pathTrace_notAvailable": "경로 추적 기능은 제공되지 않습니다.",
+ "pathTrace_refreshTooltip": "경로 추적 재시작",
+ "pathTrace_someHopsNoLocation": "홉 중 하나 또는 여러 개에 위치 정보가 누락되었습니다!",
+ "pathTrace_clearTooltip": "명확한 경로.",
+ "losSelectStartEnd": "LOS(최소 거리 경로)의 시작 및 종료 노드를 선택합니다.",
+ "losRunFailed": "시야 확인 실패: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "모든 사항을 명확히 합니다.",
+ "losRunToViewElevationProfile": "LOS(Line of Sight)를 사용하여 고도 프로필을 확인합니다.",
+ "losMenuTitle": "LOS 메뉴",
+ "losMenuSubtitle": "사용자 지정 지점을 추가하려면, 노드를 탭하거나 맵을 길게 눌러 주세요.",
+ "losShowDisplayNodes": "노드 표시",
+ "losCustomPoints": "사용자 지정 포인트",
+ "losCustomPointLabel": "맞춤형 {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "A 지점",
+ "losPointB": "점 B",
+ "losAntennaA": "안테나 A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenna B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "LOS (Loss of Signal) 상태로 전환",
+ "losNoElevationData": "고도 정보 없음",
+ "losProfileClear": "{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: 확인 중...",
+ "losStatusNoData": "LOS: 데이터 없음",
+ "losStatusSummary": "LOS: {clear}/{total} 개, {blocked} 개, {unknown} 개",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "샘플 중 하나 이상에 대한 고도 데이터가 없습니다.",
+ "losErrorInvalidInput": "LOS 계산에 사용되는 부정확한 지점/고도 데이터.",
+ "losRenameCustomPoint": "사용자 지정된 지점의 이름을 변경",
+ "losPointName": "항목 이름",
+ "losShowPanelTooltip": "LOS 패널 표시",
+ "losHidePanelTooltip": "LOS 패널 숨기기",
+ "losElevationAttribution": "고도 데이터: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "라디오 호라이즌",
+ "losLegendLosBeam": "LOS 빔",
+ "losLegendTerrain": "지형",
+ "losFrequencyLabel": "빈도",
+ "losFrequencyInfoTooltip": "계산 내역 보기",
+ "losFrequencyDialogTitle": "라디오 수신 가능 범위 계산",
+ "losFrequencyDialogDescription": "{baselineK}에서 시작하여 {baselineFreq} MHz의 주파수에서 계산을 시작하면, 현재 {frequencyMHz} MHz 대역에 대한 k-값을 조정하여, 이는 곡선형 라디오 지평선 상한선을 정의합니다.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "contacts_pathTrace": "경로 추적",
+ "contacts_ping": "핑",
+ "contacts_repeaterPathTrace": "리피터로 가는 경로",
+ "contacts_repeaterPing": "핑 반복",
+ "contacts_roomPathTrace": "방 서버로의 경로 추적",
+ "contacts_roomPing": "피нг 룸 서버",
+ "contacts_chatTraceRoute": "경로 추적 경로",
+ "contacts_pathTraceTo": "{name}까지의 경로 추적",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_clipboardEmpty": "클립보드가 비어 있습니다.",
+ "contacts_invalidAdvertFormat": "유효하지 않은 연락 정보",
+ "contacts_contactImported": "연락이 수신되었습니다.",
+ "contacts_contactImportFailed": "연락처를 가져오지 못했습니다.",
+ "contacts_zeroHopAdvert": "제로 홉 광고",
+ "contacts_floodAdvert": "홍수 광고",
+ "contacts_copyAdvertToClipboard": "광고 텍스트를 클립보드에 복사",
+ "contacts_addContactFromClipboard": "복사본에서 연락처 추가",
+ "contacts_ShareContact": "연락처를 복사",
+ "contacts_ShareContactZeroHop": "광고를 통해 연락처 공유",
+ "contacts_zeroHopContactAdvertSent": "광고를 통해 연락처를 받았습니다.",
+ "contacts_zeroHopContactAdvertFailed": "연락처 전송에 실패했습니다.",
+ "contacts_contactAdvertCopied": "광고 내용이 복사되었습니다.",
+ "contacts_contactAdvertCopyFailed": "광고를 클립보드에 복사하는 데 실패했습니다.",
+ "notification_activityTitle": "메쉬코어 활동",
+ "notification_messagesCount": "{count} {count, plural, =1{메시지} other{메시지들}}",
+ "@notification_messagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_channelMessagesCount": "{count} {count, plural, =1{채널 메시지} other{채널 메시지}}",
+ "@notification_channelMessagesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newNodesCount": "{count} {count, plural, =1{새 노드} other{새 노드들}}",
+ "@notification_newNodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "notification_newTypeDiscovered": "새로운 {contactType} 발견",
+ "@notification_newTypeDiscovered": {
+ "placeholders": {
+ "contactType": {
+ "type": "String"
+ }
+ }
+ },
+ "notification_receivedNewMessage": "새로운 메시지를 받았습니다",
+ "settings_gpxExportRepeaters": "GPX로 전송/방 관리 서버",
+ "settings_gpxExportRepeatersSubtitle": "GPX 파일에 위치 정보를 포함하여 반복자/룸 서버를 내보냅니다.",
+ "settings_gpxExportContacts": "GPX 형식으로 내보내기",
+ "settings_gpxExportContactsSubtitle": "GPX 파일에 위치 정보를 포함하여 동행하는 기능을 내보냅니다.",
+ "settings_gpxExportAll": "모든 연락처를 GPX 형식으로 내보내기",
+ "settings_gpxExportAllSubtitle": "위치 정보가 있는 모든 연락처를 GPX 파일로 내보냅니다.",
+ "settings_gpxExportSuccess": "GPX 파일이 성공적으로 내보내졌습니다.",
+ "settings_gpxExportNoContacts": "수출할 연락처가 없습니다.",
+ "settings_gpxExportNotAvailable": "귀하의 장치/운영체제에서는 지원되지 않습니다.",
+ "settings_gpxExportError": "데이터 내보내기 과정에서 오류가 발생했습니다.",
+ "settings_gpxExportRepeatersRoom": "중계 장치 및 서버 위치",
+ "settings_gpxExportChat": "함께 방문할 장소",
+ "settings_gpxExportAllContacts": "모든 연락처 위치",
+ "settings_gpxExportShareText": "meshcore-open에서 추출한 지도 데이터",
+ "settings_gpxExportShareSubject": "meshcore-open GPX 지도 데이터 내보내기",
+ "snrIndicator_nearByRepeaters": "주변의 중계기",
+ "snrIndicator_lastSeen": "마지막으로 목격",
+ "contactsSettings_title": "연락처 설정",
+ "contactsSettings_autoAddTitle": "자동 검색",
+ "contactsSettings_otherTitle": "다른 연락 관련 설정",
+ "contactsSettings_autoAddUsersTitle": "자동으로 사용자 추가",
+ "contactsSettings_autoAddUsersSubtitle": "동반자가 자동으로 발견한 사용자를 추가할 수 있도록 합니다.",
+ "contactsSettings_autoAddRepeatersTitle": "자동으로 중계기 추가",
+ "contactsSettings_autoAddRepeatersSubtitle": "애완동물이 발견한 무선 라디오를 자동으로 추가할 수 있도록 설정합니다.",
+ "contactsSettings_autoAddRoomServersTitle": "자동으로 방 서버 추가",
+ "contactsSettings_autoAddRoomServersSubtitle": "애완동물이 발견한 방 서버를 자동으로 추가할 수 있도록 설정합니다.",
+ "contactsSettings_autoAddSensorsTitle": "자동으로 센서 추가",
+ "contactsSettings_autoAddSensorsSubtitle": "애완동물이 발견한 센서를 자동으로 추가할 수 있도록 설정합니다.",
+ "contactsSettings_overwriteOldestTitle": "가장 오래된 것을 덮어쓰기",
+ "contactsSettings_overwriteOldestSubtitle": "연락처 목록이 가득 차면, 가장 오래된 (선호하지 않은) 연락처가 대체됩니다.",
+ "discoveredContacts_Title": "연락처 찾기",
+ "discoveredContacts_noMatching": "일치하는 연락처가 없습니다.",
+ "discoveredContacts_searchHint": "발견된 연락처 검색",
+ "discoveredContacts_contactAdded": "연락처 추가",
+ "discoveredContacts_addContact": "연락처 추가",
+ "discoveredContacts_copyContact": "복사",
+ "discoveredContacts_deleteContact": "발견된 연락처 삭제",
+ "discoveredContacts_deleteContactAll": "발견된 모든 연락처 삭제",
+ "discoveredContacts_deleteContactAllContent": "정말로 모든 검색된 연락처를 삭제하시겠습니까?",
+ "chat_sendCooldown": "다시 보내기 전에 잠시 기다려 주시기 바랍니다.",
+ "appSettings_jumpToOldestUnread": "가장 오래된, 아직 읽지 않은 항목으로 이동",
+ "appSettings_jumpToOldestUnreadSubtitle": "새로운 메시지가 없는 채팅을 열 때, 최신 메시지가 아닌 첫 번째 읽지 않은 메시지로 스크롤하세요.",
+ "appSettings_languageHu": "헝가리",
+ "appSettings_languageJa": "일본어",
+ "appSettings_languageKo": "한국어",
+ "radioStats_tooltip": "라디오 및 메시 통계",
+ "radioStats_screenTitle": "라디오 통계",
+ "radioStats_notConnected": "라디오 통계를 확인하기 위해 장치에 연결합니다.",
+ "radioStats_firmwareTooOld": "무선 통계 기능을 사용하려면 v8 또는 그 이상의 호환 펌웨어가 필요합니다.",
+ "radioStats_waiting": "데이터를 기다리는 중…",
+ "radioStats_noiseFloor": "잡음 수준: {noiseDbm} dBm",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastRssi": "마지막 RSSI: {rssiDbm} dBm",
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_lastSnr": "마지막 SNR: {snr} dB",
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "radioStats_txAir": "TX 방송 시간 (총): {seconds} 초",
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_rxAir": "RX 사용 시간 (총): {seconds} 초",
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_chartCaption": "최근 샘플의 잡음 수준 (dBm)",
+ "radioStats_stripNoise": "잡음 수준: {noiseDbm} dBm",
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "radioStats_stripWaiting": "라디오 통계 가져오기…",
+ "radioStats_settingsTile": "라디오 통계",
+ "radioStats_settingsSubtitle": "잡음 수준, RSSI, 신호 대 잡음비, 통신 시간",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "개인 정보 설정",
+ "settings_privacySubtitle": "어떤 정보를 공유할지 통제하세요.",
+ "settings_privacySettingsDescription": "어떤 정보를 기기가 다른 사람들과 공유할지 선택하세요.",
+ "settings_denyAll": "모든 것을 부정",
+ "settings_allowByContact": "연락처 표시 기능 활성화",
+ "settings_allowAll": "모든 것을 허용",
+ "settings_telemetryBaseMode": "원격 모니터링 기본 설정",
+ "settings_telemetryLocationMode": "텔레메트리 위치 모드",
+ "settings_telemetryEnvironmentMode": "텔레메트리 환경 모드",
+ "settings_advertLocation": "광고 위치",
+ "settings_advertLocationSubtitle": "광고에 위치 정보를 포함하세요.",
+ "settings_multiAck": "다중 ACK: {value}",
+ "settings_telemetryModeUpdated": "텔레메트리 모드 업데이트 완료",
+ "contact_info": "연락처",
+ "contact_settings": "연락처 설정",
+ "contact_telemetry": "텔레메트리",
+ "contact_lastSeen": "마지막으로 목격",
+ "contact_clearChat": "명확한 대화",
+ "contact_teleBase": "텔레메트리 기반",
+ "contact_teleBaseSubtitle": "배터리 잔량 및 기본적인 통신 데이터를 공유할 수 있도록 허용",
+ "contact_teleLoc": "텔레메트리 위치",
+ "contact_teleLocSubtitle": "위치 정보 공유 허용",
+ "contact_teleEnv": "텔레메트리 환경",
+ "contact_teleEnvSubtitle": "환경 센서 데이터를 공유하도록 허용",
+ "map_showOverlaps": "반복 키 중복",
+ "map_runTraceWithReturnPath": "원래 경로로 돌아가세요.",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "입력 메시지를 번역하고, 미리 번역 기능을 제공합니다.",
+ "translation_title": "번역",
+ "translation_enableTitle": "번역 기능 활성화",
+ "translation_composerTitle": "보내기 전에 번역",
+ "translation_composerSubtitle": "컴포저 번역 아이콘의 기본 상태를 제어합니다.",
+ "translation_targetLanguage": "목표 언어",
+ "translation_useAppLanguage": "앱 언어 사용",
+ "translation_downloadedModelLabel": "다운로드한 모델",
+ "translation_presetModelLabel": "사전에 설정된 Hugging Face 모델",
+ "translation_manualUrlLabel": "수동 모델 URL",
+ "translation_downloadModel": "모델 다운로드",
+ "translation_downloading": "다운로드 중...",
+ "translation_working": "업무 중...",
+ "translation_stop": "멈춰",
+ "translation_mergingChunks": "다운로드한 파일 조각들을 최종 파일로 병합 중...",
+ "translation_downloadedModels": "다운로드한 모델",
+ "translation_deleteModel": "모델 삭제",
+ "translation_modelDownloaded": "번역 모델이 다운로드되었습니다.",
+ "translation_downloadStopped": "다운로드 중단됨.",
+ "translation_downloadFailed": "다운로드 실패: {error}",
+ "translation_enterUrlFirst": "먼저 모델 URL을 입력하세요.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_linuxPairingPinTitle": "블루투스 페어링 PIN",
+ "scanner_linuxPairingHidePin": "PIN 숨기기",
+ "scanner_linuxPairingShowPin": "PIN 보기",
+ "scanner_linuxPairingPinPrompt": "{deviceName}의 PIN을 입력하세요 (해당하는 경우에만 입력).",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerEnabledHint": "메시지는 전송하기 전에 번역될 것입니다.",
+ "translation_translateBeforeSending": "보내기 전에 번역",
+ "translation_messageTranslation": "메시지 번역",
+ "translation_composerDisabledHint": "원래 작성된 언어로 메시지를 보내세요.",
+ "translation_translateTo": "{language} 번역",
+ "translation_translationOptions": "번역 옵션",
+ "translation_systemLanguage": "시스템 언어",
+ "repeater_cliQuickClockSync": "시계 동기화",
+ "repeater_cliQuickDiscovery": "이웃 발견하기",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "로그인 후 시계 동기화",
+ "repeater_clockSyncAfterLoginSubtitle": "성공적인 로그인 후, 자동으로 \"시간 동기화\"를 전송합니다.",
+ "repeater_guestTools": "손님용 도구",
+ "chat_sendMessage": "메시지를 보내기",
+ "repeater_guest": "반복 장비 정보",
+ "room_guest": "서버 정보"
+}
diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart
index fe4fc01..2c1342d 100644
--- a/lib/l10n/app_localizations.dart
+++ b/lib/l10n/app_localizations.dart
@@ -10,13 +10,18 @@ import 'app_localizations_de.dart';
import 'app_localizations_en.dart';
import 'app_localizations_es.dart';
import 'app_localizations_fr.dart';
+import 'app_localizations_hu.dart';
import 'app_localizations_it.dart';
+import 'app_localizations_ja.dart';
+import 'app_localizations_ko.dart';
import 'app_localizations_nl.dart';
import 'app_localizations_pl.dart';
import 'app_localizations_pt.dart';
+import 'app_localizations_ru.dart';
import 'app_localizations_sk.dart';
import 'app_localizations_sl.dart';
import 'app_localizations_sv.dart';
+import 'app_localizations_uk.dart';
import 'app_localizations_zh.dart';
// ignore_for_file: type=lint
@@ -110,13 +115,18 @@ abstract class AppLocalizations {
Locale('en'),
Locale('es'),
Locale('fr'),
+ Locale('hu'),
Locale('it'),
+ Locale('ja'),
+ Locale('ko'),
Locale('nl'),
Locale('pl'),
Locale('pt'),
+ Locale('ru'),
Locale('sk'),
Locale('sl'),
Locale('sv'),
+ Locale('uk'),
Locale('zh'),
];
@@ -180,6 +190,12 @@ abstract class AppLocalizations {
/// **'Delete'**
String get common_delete;
+ /// No description provided for @common_deleteAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete All'**
+ String get common_deleteAll;
+
/// No description provided for @common_close.
///
/// In en, this message translates to:
@@ -312,6 +328,228 @@ abstract class AppLocalizations {
/// **'MeshCore Open'**
String get scanner_title;
+ /// No description provided for @connectionChoiceUsbLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'USB'**
+ String get connectionChoiceUsbLabel;
+
+ /// No description provided for @connectionChoiceBluetoothLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Bluetooth'**
+ String get connectionChoiceBluetoothLabel;
+
+ /// No description provided for @connectionChoiceTcpLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'TCP'**
+ String get connectionChoiceTcpLabel;
+
+ /// No description provided for @tcpScreenTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Connect over TCP'**
+ String get tcpScreenTitle;
+
+ /// No description provided for @tcpHostLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'IP Address'**
+ String get tcpHostLabel;
+
+ /// No description provided for @tcpHostHint.
+ ///
+ /// In en, this message translates to:
+ /// **'192.168.40.10'**
+ String get tcpHostHint;
+
+ /// No description provided for @tcpPortLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Port'**
+ String get tcpPortLabel;
+
+ /// No description provided for @tcpPortHint.
+ ///
+ /// In en, this message translates to:
+ /// **'5000'**
+ String get tcpPortHint;
+
+ /// No description provided for @tcpStatus_notConnected.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter endpoint and connect'**
+ String get tcpStatus_notConnected;
+
+ /// No description provided for @tcpStatus_connectingTo.
+ ///
+ /// In en, this message translates to:
+ /// **'Connecting to {endpoint}...'**
+ String tcpStatus_connectingTo(String endpoint);
+
+ /// No description provided for @tcpErrorHostRequired.
+ ///
+ /// In en, this message translates to:
+ /// **'IP address is required.'**
+ String get tcpErrorHostRequired;
+
+ /// No description provided for @tcpErrorPortInvalid.
+ ///
+ /// In en, this message translates to:
+ /// **'Port must be between 1 and 65535.'**
+ String get tcpErrorPortInvalid;
+
+ /// No description provided for @tcpErrorUnsupported.
+ ///
+ /// In en, this message translates to:
+ /// **'TCP transport is not supported on this platform.'**
+ String get tcpErrorUnsupported;
+
+ /// No description provided for @tcpErrorTimedOut.
+ ///
+ /// In en, this message translates to:
+ /// **'TCP connection timed out.'**
+ String get tcpErrorTimedOut;
+
+ /// No description provided for @tcpConnectionFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'TCP connection failed: {error}'**
+ String tcpConnectionFailed(String error);
+
+ /// No description provided for @usbScreenTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Connect over USB'**
+ String get usbScreenTitle;
+
+ /// No description provided for @usbScreenSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Choose a detected serial device and connect directly to your MeshCore node.'**
+ String get usbScreenSubtitle;
+
+ /// No description provided for @usbScreenStatus.
+ ///
+ /// In en, this message translates to:
+ /// **'Select a USB device'**
+ String get usbScreenStatus;
+
+ /// No description provided for @usbScreenNote.
+ ///
+ /// In en, this message translates to:
+ /// **'USB serial is active on supported Android devices and desktop platforms.'**
+ String get usbScreenNote;
+
+ /// No description provided for @usbScreenEmptyState.
+ ///
+ /// In en, this message translates to:
+ /// **'No USB devices found. Plug one in and refresh.'**
+ String get usbScreenEmptyState;
+
+ /// No description provided for @usbErrorPermissionDenied.
+ ///
+ /// In en, this message translates to:
+ /// **'USB permission was denied.'**
+ String get usbErrorPermissionDenied;
+
+ /// No description provided for @usbErrorDeviceMissing.
+ ///
+ /// In en, this message translates to:
+ /// **'The selected USB device is no longer available.'**
+ String get usbErrorDeviceMissing;
+
+ /// No description provided for @usbErrorInvalidPort.
+ ///
+ /// In en, this message translates to:
+ /// **'Select a valid USB device.'**
+ String get usbErrorInvalidPort;
+
+ /// No description provided for @usbErrorBusy.
+ ///
+ /// In en, this message translates to:
+ /// **'Another USB connection request is already in progress.'**
+ String get usbErrorBusy;
+
+ /// No description provided for @usbErrorNotConnected.
+ ///
+ /// In en, this message translates to:
+ /// **'No USB device is connected.'**
+ String get usbErrorNotConnected;
+
+ /// No description provided for @usbErrorOpenFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed to open the selected USB device.'**
+ String get usbErrorOpenFailed;
+
+ /// No description provided for @usbErrorConnectFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed to connect to the selected USB device.'**
+ String get usbErrorConnectFailed;
+
+ /// No description provided for @usbErrorUnsupported.
+ ///
+ /// In en, this message translates to:
+ /// **'USB serial is not supported on this platform.'**
+ String get usbErrorUnsupported;
+
+ /// No description provided for @usbErrorAlreadyActive.
+ ///
+ /// In en, this message translates to:
+ /// **'A USB connection is already active.'**
+ String get usbErrorAlreadyActive;
+
+ /// No description provided for @usbErrorNoDeviceSelected.
+ ///
+ /// In en, this message translates to:
+ /// **'No USB device was selected.'**
+ String get usbErrorNoDeviceSelected;
+
+ /// No description provided for @usbErrorPortClosed.
+ ///
+ /// In en, this message translates to:
+ /// **'The USB connection is not open.'**
+ String get usbErrorPortClosed;
+
+ /// No description provided for @usbErrorConnectTimedOut.
+ ///
+ /// In en, this message translates to:
+ /// **'Connection timed out. Make sure the device has USB Companion firmware.'**
+ String get usbErrorConnectTimedOut;
+
+ /// No description provided for @usbFallbackDeviceName.
+ ///
+ /// In en, this message translates to:
+ /// **'Web Serial Device'**
+ String get usbFallbackDeviceName;
+
+ /// No description provided for @usbStatus_notConnected.
+ ///
+ /// In en, this message translates to:
+ /// **'Select a USB device'**
+ String get usbStatus_notConnected;
+
+ /// No description provided for @usbStatus_connecting.
+ ///
+ /// In en, this message translates to:
+ /// **'Connecting to USB device...'**
+ String get usbStatus_connecting;
+
+ /// No description provided for @usbStatus_searching.
+ ///
+ /// In en, this message translates to:
+ /// **'Searching for USB devices...'**
+ String get usbStatus_searching;
+
+ /// No description provided for @usbConnectionFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'USB connection failed: {error}'**
+ String usbConnectionFailed(String error);
+
/// No description provided for @scanner_scanning.
///
/// In en, this message translates to:
@@ -372,6 +610,36 @@ abstract class AppLocalizations {
/// **'Scan'**
String get scanner_scan;
+ /// No description provided for @scanner_bluetoothOff.
+ ///
+ /// In en, this message translates to:
+ /// **'Bluetooth is off'**
+ String get scanner_bluetoothOff;
+
+ /// No description provided for @scanner_bluetoothOffMessage.
+ ///
+ /// In en, this message translates to:
+ /// **'Please turn on Bluetooth to scan for devices'**
+ String get scanner_bluetoothOffMessage;
+
+ /// No description provided for @scanner_chromeRequired.
+ ///
+ /// In en, this message translates to:
+ /// **'Chrome Browser Required'**
+ String get scanner_chromeRequired;
+
+ /// No description provided for @scanner_chromeRequiredMessage.
+ ///
+ /// In en, this message translates to:
+ /// **'This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.'**
+ String get scanner_chromeRequiredMessage;
+
+ /// No description provided for @scanner_enableBluetooth.
+ ///
+ /// In en, this message translates to:
+ /// **'Enable Bluetooth'**
+ String get scanner_enableBluetooth;
+
/// No description provided for @device_quickSwitch.
///
/// In en, this message translates to:
@@ -522,6 +790,18 @@ abstract class AppLocalizations {
/// **'Longitude'**
String get settings_longitude;
+ /// No description provided for @settings_contactSettings.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact Settings'**
+ String get settings_contactSettings;
+
+ /// No description provided for @settings_contactSettingsSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Settings for how contacts are added.'**
+ String get settings_contactSettingsSubtitle;
+
/// No description provided for @settings_privacyMode.
///
/// In en, this message translates to:
@@ -552,6 +832,84 @@ abstract class AppLocalizations {
/// **'Privacy mode disabled'**
String get settings_privacyModeDisabled;
+ /// No description provided for @settings_privacy.
+ ///
+ /// In en, this message translates to:
+ /// **'Privacy Settings'**
+ String get settings_privacy;
+
+ /// No description provided for @settings_privacySubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Control what information is shared.'**
+ String get settings_privacySubtitle;
+
+ /// No description provided for @settings_privacySettingsDescription.
+ ///
+ /// In en, this message translates to:
+ /// **'Choose what information your device shares with others.'**
+ String get settings_privacySettingsDescription;
+
+ /// No description provided for @settings_denyAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Deny all'**
+ String get settings_denyAll;
+
+ /// No description provided for @settings_allowByContact.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow by contact flags'**
+ String get settings_allowByContact;
+
+ /// No description provided for @settings_allowAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow all'**
+ String get settings_allowAll;
+
+ /// No description provided for @settings_telemetryBaseMode.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Base Mode'**
+ String get settings_telemetryBaseMode;
+
+ /// No description provided for @settings_telemetryLocationMode.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Location Mode'**
+ String get settings_telemetryLocationMode;
+
+ /// No description provided for @settings_telemetryEnvironmentMode.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Environment Mode'**
+ String get settings_telemetryEnvironmentMode;
+
+ /// No description provided for @settings_advertLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'Advert Location'**
+ String get settings_advertLocation;
+
+ /// No description provided for @settings_advertLocationSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Include location in advert.'**
+ String get settings_advertLocationSubtitle;
+
+ /// No description provided for @settings_multiAck.
+ ///
+ /// In en, this message translates to:
+ /// **'Multi-ACKs: {value}'**
+ String settings_multiAck(String value);
+
+ /// No description provided for @settings_telemetryModeUpdated.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry mode updated'**
+ String get settings_telemetryModeUpdated;
+
/// No description provided for @settings_actions.
///
/// In en, this message translates to:
@@ -678,6 +1036,12 @@ abstract class AppLocalizations {
/// **'An open-source Flutter client for MeshCore LoRa mesh networking devices.'**
String get settings_aboutDescription;
+ /// No description provided for @settings_aboutOpenMeteoAttribution.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS elevation data: Open-Meteo (CC BY 4.0)'**
+ String get settings_aboutOpenMeteoAttribution;
+
/// No description provided for @settings_infoName.
///
/// In en, this message translates to:
@@ -726,24 +1090,6 @@ abstract class AppLocalizations {
/// **'Presets'**
String get settings_presets;
- /// No description provided for @settings_preset915Mhz.
- ///
- /// In en, this message translates to:
- /// **'915 MHz'**
- String get settings_preset915Mhz;
-
- /// No description provided for @settings_preset868Mhz.
- ///
- /// In en, this message translates to:
- /// **'868 MHz'**
- String get settings_preset868Mhz;
-
- /// No description provided for @settings_preset433Mhz.
- ///
- /// In en, this message translates to:
- /// **'433 MHz'**
- String get settings_preset433Mhz;
-
/// No description provided for @settings_frequency.
///
/// In en, this message translates to:
@@ -798,17 +1144,23 @@ abstract class AppLocalizations {
/// **'Invalid TX power (0-22 dBm)'**
String get settings_txPowerInvalid;
- /// No description provided for @settings_longRange.
+ /// No description provided for @settings_clientRepeat.
///
/// In en, this message translates to:
- /// **'Long Range'**
- String get settings_longRange;
+ /// **'Off-Grid Repeat'**
+ String get settings_clientRepeat;
- /// No description provided for @settings_fastSpeed.
+ /// No description provided for @settings_clientRepeatSubtitle.
///
/// In en, this message translates to:
- /// **'Fast Speed'**
- String get settings_fastSpeed;
+ /// **'Allow this device to repeat mesh packets for others'**
+ String get settings_clientRepeatSubtitle;
+
+ /// No description provided for @settings_clientRepeatFreqWarning.
+ ///
+ /// In en, this message translates to:
+ /// **'Off-grid repeat requires 433, 869, or 918 MHz frequency'**
+ String get settings_clientRepeatFreqWarning;
/// No description provided for @settings_error.
///
@@ -942,6 +1294,30 @@ abstract class AppLocalizations {
/// **'Български'**
String get appSettings_languageBg;
+ /// No description provided for @appSettings_languageRu.
+ ///
+ /// In en, this message translates to:
+ /// **'Русский'**
+ String get appSettings_languageRu;
+
+ /// No description provided for @appSettings_languageUk.
+ ///
+ /// In en, this message translates to:
+ /// **'Українська'**
+ String get appSettings_languageUk;
+
+ /// No description provided for @appSettings_enableMessageTracing.
+ ///
+ /// In en, this message translates to:
+ /// **'Enable Message Tracing'**
+ String get appSettings_enableMessageTracing;
+
+ /// No description provided for @appSettings_enableMessageTracingSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Show detailed routing and timing metadata for messages'**
+ String get appSettings_enableMessageTracingSubtitle;
+
/// No description provided for @appSettings_notifications.
///
/// In en, this message translates to:
@@ -1068,6 +1444,72 @@ abstract class AppLocalizations {
/// **'Auto route rotation disabled'**
String get appSettings_autoRouteRotationDisabled;
+ /// No description provided for @appSettings_maxRouteWeight.
+ ///
+ /// In en, this message translates to:
+ /// **'Max Route Weight'**
+ String get appSettings_maxRouteWeight;
+
+ /// No description provided for @appSettings_maxRouteWeightSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Maximum weight a path can accumulate from successful deliveries'**
+ String get appSettings_maxRouteWeightSubtitle;
+
+ /// No description provided for @appSettings_initialRouteWeight.
+ ///
+ /// In en, this message translates to:
+ /// **'Initial Route Weight'**
+ String get appSettings_initialRouteWeight;
+
+ /// No description provided for @appSettings_initialRouteWeightSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Starting weight for newly discovered paths'**
+ String get appSettings_initialRouteWeightSubtitle;
+
+ /// No description provided for @appSettings_routeWeightSuccessIncrement.
+ ///
+ /// In en, this message translates to:
+ /// **'Success Weight Increment'**
+ String get appSettings_routeWeightSuccessIncrement;
+
+ /// No description provided for @appSettings_routeWeightSuccessIncrementSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Weight added to a path after successful delivery'**
+ String get appSettings_routeWeightSuccessIncrementSubtitle;
+
+ /// No description provided for @appSettings_routeWeightFailureDecrement.
+ ///
+ /// In en, this message translates to:
+ /// **'Failure Weight Decrement'**
+ String get appSettings_routeWeightFailureDecrement;
+
+ /// No description provided for @appSettings_routeWeightFailureDecrementSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Weight removed from a path after failed delivery'**
+ String get appSettings_routeWeightFailureDecrementSubtitle;
+
+ /// No description provided for @appSettings_maxMessageRetries.
+ ///
+ /// In en, this message translates to:
+ /// **'Max Message Retries'**
+ String get appSettings_maxMessageRetries;
+
+ /// No description provided for @appSettings_maxMessageRetriesSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Number of retry attempts before marking a message as failed'**
+ String get appSettings_maxMessageRetriesSubtitle;
+
+ /// No description provided for @path_routeWeight.
+ ///
+ /// In en, this message translates to:
+ /// **'{weight}/{max}'**
+ String path_routeWeight(String weight, String max);
+
/// No description provided for @appSettings_battery.
///
/// In en, this message translates to:
@@ -1218,6 +1660,24 @@ abstract class AppLocalizations {
/// **'Offline Map Cache'**
String get appSettings_offlineMapCache;
+ /// No description provided for @appSettings_unitsTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Units'**
+ String get appSettings_unitsTitle;
+
+ /// No description provided for @appSettings_unitsMetric.
+ ///
+ /// In en, this message translates to:
+ /// **'Metric (m / km)'**
+ String get appSettings_unitsMetric;
+
+ /// No description provided for @appSettings_unitsImperial.
+ ///
+ /// In en, this message translates to:
+ /// **'Imperial (ft / mi)'**
+ String get appSettings_unitsImperial;
+
/// No description provided for @appSettings_noAreaSelected.
///
/// In en, this message translates to:
@@ -1278,11 +1738,47 @@ abstract class AppLocalizations {
/// **'Contacts will appear when devices advertise'**
String get contacts_contactsWillAppear;
+ /// No description provided for @contacts_unread.
+ ///
+ /// In en, this message translates to:
+ /// **'Unread'**
+ String get contacts_unread;
+
+ /// No description provided for @contacts_searchContactsNoNumber.
+ ///
+ /// In en, this message translates to:
+ /// **'Search Contacts...'**
+ String get contacts_searchContactsNoNumber;
+
/// No description provided for @contacts_searchContacts.
///
/// In en, this message translates to:
- /// **'Search contacts...'**
- String get contacts_searchContacts;
+ /// **'Search {number}{str} Contacts...'**
+ String contacts_searchContacts(int number, String str);
+
+ /// No description provided for @contacts_searchFavorites.
+ ///
+ /// In en, this message translates to:
+ /// **'Search {number}{str} Favorites...'**
+ String contacts_searchFavorites(int number, String str);
+
+ /// No description provided for @contacts_searchUsers.
+ ///
+ /// In en, this message translates to:
+ /// **'Search {number}{str} Users...'**
+ String contacts_searchUsers(int number, String str);
+
+ /// No description provided for @contacts_searchRepeaters.
+ ///
+ /// In en, this message translates to:
+ /// **'Search {number}{str} Repeaters...'**
+ String contacts_searchRepeaters(int number, String str);
+
+ /// No description provided for @contacts_searchRoomServers.
+ ///
+ /// In en, this message translates to:
+ /// **'Search {number}{str} Room servers...'**
+ String contacts_searchRoomServers(int number, String str);
/// No description provided for @contacts_noUnreadContacts.
///
@@ -1368,6 +1864,12 @@ abstract class AppLocalizations {
/// **'Group name is required'**
String get contacts_groupNameRequired;
+ /// No description provided for @contacts_groupNameReserved.
+ ///
+ /// In en, this message translates to:
+ /// **'This group name is reserved'**
+ String get contacts_groupNameReserved;
+
/// No description provided for @contacts_groupAlreadyExists.
///
/// In en, this message translates to:
@@ -1395,39 +1897,105 @@ abstract class AppLocalizations {
/// No description provided for @contacts_lastSeenNow.
///
/// In en, this message translates to:
- /// **'Last seen now'**
+ /// **'recently'**
String get contacts_lastSeenNow;
/// No description provided for @contacts_lastSeenMinsAgo.
///
/// In en, this message translates to:
- /// **'Last seen {minutes} mins ago'**
+ /// **'~ {minutes} min.'**
String contacts_lastSeenMinsAgo(int minutes);
/// No description provided for @contacts_lastSeenHourAgo.
///
/// In en, this message translates to:
- /// **'Last seen 1 hour ago'**
+ /// **'~ 1 hour'**
String get contacts_lastSeenHourAgo;
/// No description provided for @contacts_lastSeenHoursAgo.
///
/// In en, this message translates to:
- /// **'Last seen {hours} hours ago'**
+ /// **'~ {hours} hours'**
String contacts_lastSeenHoursAgo(int hours);
/// No description provided for @contacts_lastSeenDayAgo.
///
/// In en, this message translates to:
- /// **'Last seen 1 day ago'**
+ /// **'~ 1 day'**
String get contacts_lastSeenDayAgo;
/// No description provided for @contacts_lastSeenDaysAgo.
///
/// In en, this message translates to:
- /// **'Last seen {days} days ago'**
+ /// **'~ {days} days'**
String contacts_lastSeenDaysAgo(int days);
+ /// No description provided for @contact_info.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact Info'**
+ String get contact_info;
+
+ /// No description provided for @contact_settings.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact Settings'**
+ String get contact_settings;
+
+ /// No description provided for @contact_telemetry.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry'**
+ String get contact_telemetry;
+
+ /// No description provided for @contact_lastSeen.
+ ///
+ /// In en, this message translates to:
+ /// **'Last seen'**
+ String get contact_lastSeen;
+
+ /// No description provided for @contact_clearChat.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear Chat'**
+ String get contact_clearChat;
+
+ /// No description provided for @contact_teleBase.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Base'**
+ String get contact_teleBase;
+
+ /// No description provided for @contact_teleBaseSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow sharing battery level and basic telemetry'**
+ String get contact_teleBaseSubtitle;
+
+ /// No description provided for @contact_teleLoc.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Location'**
+ String get contact_teleLoc;
+
+ /// No description provided for @contact_teleLocSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow sharing location data'**
+ String get contact_teleLocSubtitle;
+
+ /// No description provided for @contact_teleEnv.
+ ///
+ /// In en, this message translates to:
+ /// **'Telemetry Environment'**
+ String get contact_teleEnv;
+
+ /// No description provided for @contact_teleEnvSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow sharing environment sensor data'**
+ String get contact_teleEnvSubtitle;
+
/// No description provided for @channels_title.
///
/// In en, this message translates to:
@@ -1500,6 +2068,18 @@ abstract class AppLocalizations {
/// **'Edit channel'**
String get channels_editChannel;
+ /// No description provided for @channels_muteChannel.
+ ///
+ /// In en, this message translates to:
+ /// **'Mute channel'**
+ String get channels_muteChannel;
+
+ /// No description provided for @channels_unmuteChannel.
+ ///
+ /// In en, this message translates to:
+ /// **'Unmute channel'**
+ String get channels_unmuteChannel;
+
/// No description provided for @channels_deleteChannel.
///
/// In en, this message translates to:
@@ -1512,6 +2092,12 @@ abstract class AppLocalizations {
/// **'Delete \"{name}\"? This cannot be undone.'**
String channels_deleteChannelConfirm(String name);
+ /// No description provided for @channels_channelDeleteFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed to delete channel \"{name}\"'**
+ String channels_channelDeleteFailed(String name);
+
/// No description provided for @channels_channelDeleted.
///
/// In en, this message translates to:
@@ -1710,6 +2296,18 @@ abstract class AppLocalizations {
/// **'No messages yet'**
String get chat_noMessages;
+ /// No description provided for @chat_sendMessage.
+ ///
+ /// In en, this message translates to:
+ /// **'Send message'**
+ String get chat_sendMessage;
+
+ /// No description provided for @chat_sendMessageTo.
+ ///
+ /// In en, this message translates to:
+ /// **'Send a message to {contactName}'**
+ String chat_sendMessageTo(String contactName);
+
/// No description provided for @chat_sendMessageToStart.
///
/// In en, this message translates to:
@@ -1740,12 +2338,6 @@ abstract class AppLocalizations {
/// **'Location'**
String get chat_location;
- /// No description provided for @chat_sendMessageTo.
- ///
- /// In en, this message translates to:
- /// **'Send a message to {contactName}'**
- String chat_sendMessageTo(String contactName);
-
/// No description provided for @chat_typeMessage.
///
/// In en, this message translates to:
@@ -2010,6 +2602,12 @@ abstract class AppLocalizations {
/// **'Path Management'**
String get chat_pathManagement;
+ /// No description provided for @chat_ShowAllPaths.
+ ///
+ /// In en, this message translates to:
+ /// **'Show all paths'**
+ String get chat_ShowAllPaths;
+
/// No description provided for @chat_routingMode.
///
/// In en, this message translates to:
@@ -2226,12 +2824,54 @@ abstract class AppLocalizations {
/// **'Unread: {count}'**
String chat_unread(int count);
+ /// No description provided for @chat_openLink.
+ ///
+ /// In en, this message translates to:
+ /// **'Open Link?'**
+ String get chat_openLink;
+
+ /// No description provided for @chat_openLinkConfirmation.
+ ///
+ /// In en, this message translates to:
+ /// **'Do you want to open this link in your browser?'**
+ String get chat_openLinkConfirmation;
+
+ /// No description provided for @chat_open.
+ ///
+ /// In en, this message translates to:
+ /// **'Open'**
+ String get chat_open;
+
+ /// No description provided for @chat_couldNotOpenLink.
+ ///
+ /// In en, this message translates to:
+ /// **'Could not open link: {url}'**
+ String chat_couldNotOpenLink(String url);
+
+ /// No description provided for @chat_invalidLink.
+ ///
+ /// In en, this message translates to:
+ /// **'Invalid link format'**
+ String get chat_invalidLink;
+
/// No description provided for @map_title.
///
/// In en, this message translates to:
/// **'Node Map'**
String get map_title;
+ /// No description provided for @map_lineOfSight.
+ ///
+ /// In en, this message translates to:
+ /// **'Line of Sight'**
+ String get map_lineOfSight;
+
+ /// No description provided for @map_losScreenTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Line of Sight'**
+ String get map_losScreenTitle;
+
/// No description provided for @map_noNodesWithLocation.
///
/// In en, this message translates to:
@@ -2334,6 +2974,12 @@ abstract class AppLocalizations {
/// **'Share marker here'**
String get map_shareMarkerHere;
+ /// No description provided for @map_setAsMyLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'Set as my location'**
+ String get map_setAsMyLocation;
+
/// No description provided for @map_pinLabel.
///
/// In en, this message translates to:
@@ -2418,6 +3064,12 @@ abstract class AppLocalizations {
/// **'Other Nodes'**
String get map_otherNodes;
+ /// No description provided for @map_showOverlaps.
+ ///
+ /// In en, this message translates to:
+ /// **'Repeater Key Overlaps'**
+ String get map_showOverlaps;
+
/// No description provided for @map_keyPrefix.
///
/// In en, this message translates to:
@@ -2448,6 +3100,24 @@ abstract class AppLocalizations {
/// **'Show shared markers'**
String get map_showSharedMarkers;
+ /// No description provided for @map_showGuessedLocations.
+ ///
+ /// In en, this message translates to:
+ /// **'Show guessed node locations'**
+ String get map_showGuessedLocations;
+
+ /// No description provided for @map_showDiscoveryContacts.
+ ///
+ /// In en, this message translates to:
+ /// **'Show Discovery Contacts'**
+ String get map_showDiscoveryContacts;
+
+ /// No description provided for @map_guessedLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'Guessed location'**
+ String get map_guessedLocation;
+
/// No description provided for @map_lastSeenTime.
///
/// In en, this message translates to:
@@ -2472,6 +3142,36 @@ abstract class AppLocalizations {
/// **'Manage Repeater'**
String get map_manageRepeater;
+ /// No description provided for @map_tapToAdd.
+ ///
+ /// In en, this message translates to:
+ /// **'Tap on nodes to add them to the path.'**
+ String get map_tapToAdd;
+
+ /// No description provided for @map_runTrace.
+ ///
+ /// In en, this message translates to:
+ /// **'Run path trace'**
+ String get map_runTrace;
+
+ /// No description provided for @map_runTraceWithReturnPath.
+ ///
+ /// In en, this message translates to:
+ /// **'Return back on the same path.'**
+ String get map_runTraceWithReturnPath;
+
+ /// No description provided for @map_removeLast.
+ ///
+ /// In en, this message translates to:
+ /// **'Remove Last'**
+ String get map_removeLast;
+
+ /// No description provided for @map_pathTraceCancelled.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace cancelled.'**
+ String get map_pathTraceCancelled;
+
/// No description provided for @mapCache_title.
///
/// In en, this message translates to:
@@ -2738,13 +3438,13 @@ abstract class AppLocalizations {
/// No description provided for @login_repeaterDescription.
///
/// In en, this message translates to:
- /// **'Enter the repeater password to access settings and status.'**
+ /// **'Enter the repeater password for guest or admin access.'**
String get login_repeaterDescription;
/// No description provided for @login_roomDescription.
///
/// In en, this message translates to:
- /// **'Enter the room password to access settings and status.'**
+ /// **'Enter the room password for guest or admin access.'**
String get login_roomDescription;
/// No description provided for @login_routing.
@@ -2909,12 +3609,30 @@ abstract class AppLocalizations {
/// **'Room Server Management'**
String get room_management;
+ /// No description provided for @repeater_guest.
+ ///
+ /// In en, this message translates to:
+ /// **'Repeater Information'**
+ String get repeater_guest;
+
+ /// No description provided for @room_guest.
+ ///
+ /// In en, this message translates to:
+ /// **'Room Server Information'**
+ String get room_guest;
+
/// No description provided for @repeater_managementTools.
///
/// In en, this message translates to:
/// **'Management Tools'**
String get repeater_managementTools;
+ /// No description provided for @repeater_guestTools.
+ ///
+ /// In en, this message translates to:
+ /// **'Guest Tools'**
+ String get repeater_guestTools;
+
/// No description provided for @repeater_status.
///
/// In en, this message translates to:
@@ -2951,17 +3669,17 @@ abstract class AppLocalizations {
/// **'Send commands to the repeater'**
String get repeater_cliSubtitle;
- /// No description provided for @repeater_neighbours.
+ /// No description provided for @repeater_neighbors.
///
/// In en, this message translates to:
/// **'Neighbors'**
- String get repeater_neighbours;
+ String get repeater_neighbors;
- /// No description provided for @repeater_neighboursSubtitle.
+ /// No description provided for @repeater_neighborsSubtitle.
///
/// In en, this message translates to:
/// **'View zero hop neighbors.'**
- String get repeater_neighboursSubtitle;
+ String get repeater_neighborsSubtitle;
/// No description provided for @repeater_settings.
///
@@ -2975,6 +3693,18 @@ abstract class AppLocalizations {
/// **'Configure repeater parameters'**
String get repeater_settingsSubtitle;
+ /// Repeater setting: auto sync device clock after successful login
+ ///
+ /// In en, this message translates to:
+ /// **'Clock sync after login'**
+ String get repeater_clockSyncAfterLogin;
+
+ /// Repeater setting subtitle: describes the clock sync after login behavior
+ ///
+ /// In en, this message translates to:
+ /// **'Automatically send \"clock sync\" after a successful login'**
+ String get repeater_clockSyncAfterLoginSubtitle;
+
/// No description provided for @repeater_statusTitle.
///
/// In en, this message translates to:
@@ -3628,6 +4358,18 @@ abstract class AppLocalizations {
/// **'Clock'**
String get repeater_cliQuickClock;
+ /// No description provided for @repeater_cliQuickClockSync.
+ ///
+ /// In en, this message translates to:
+ /// **'Clock Sync'**
+ String get repeater_cliQuickClockSync;
+
+ /// No description provided for @repeater_cliQuickDiscovery.
+ ///
+ /// In en, this message translates to:
+ /// **'Discover Neighbors'**
+ String get repeater_cliQuickDiscovery;
+
/// No description provided for @repeater_cliHelpAdvert.
///
/// In en, this message translates to:
@@ -4105,13 +4847,13 @@ abstract class AppLocalizations {
/// No description provided for @neighbors_receivedData.
///
/// In en, this message translates to:
- /// **'Received Neighbours Data'**
+ /// **'Received Neighbors Data'**
String get neighbors_receivedData;
/// No description provided for @neighbors_requestTimedOut.
///
/// In en, this message translates to:
- /// **'Neighbours request timed out.'**
+ /// **'Neighbors request timed out.'**
String get neighbors_requestTimedOut;
/// No description provided for @neighbors_errorLoading.
@@ -4120,16 +4862,16 @@ abstract class AppLocalizations {
/// **'Error loading neighbors: {error}'**
String neighbors_errorLoading(String error);
- /// No description provided for @neighbors_repeatersNeighbours.
+ /// No description provided for @neighbors_repeatersNeighbors.
///
/// In en, this message translates to:
- /// **'Repeaters Neighbours'**
- String get neighbors_repeatersNeighbours;
+ /// **'Repeaters Neighbors'**
+ String get neighbors_repeatersNeighbors;
/// No description provided for @neighbors_noData.
///
/// In en, this message translates to:
- /// **'No neighbours data available.'**
+ /// **'No neighbors data available.'**
String get neighbors_noData;
/// No description provided for @neighbors_unknownContact.
@@ -4624,6 +5366,24 @@ abstract class AppLocalizations {
/// **'All'**
String get listFilter_all;
+ /// No description provided for @listFilter_favorites.
+ ///
+ /// In en, this message translates to:
+ /// **'Favorites'**
+ String get listFilter_favorites;
+
+ /// No description provided for @listFilter_addToFavorites.
+ ///
+ /// In en, this message translates to:
+ /// **'Add to favorites'**
+ String get listFilter_addToFavorites;
+
+ /// No description provided for @listFilter_removeFromFavorites.
+ ///
+ /// In en, this message translates to:
+ /// **'Remove from favorites'**
+ String get listFilter_removeFromFavorites;
+
/// No description provided for @listFilter_users.
///
/// In en, this message translates to:
@@ -4653,6 +5413,981 @@ abstract class AppLocalizations {
/// In en, this message translates to:
/// **'New group'**
String get listFilter_newGroup;
+
+ /// No description provided for @pathTrace_you.
+ ///
+ /// In en, this message translates to:
+ /// **'You'**
+ String get pathTrace_you;
+
+ /// No description provided for @pathTrace_failed.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace failed.'**
+ String get pathTrace_failed;
+
+ /// No description provided for @pathTrace_notAvailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace not available.'**
+ String get pathTrace_notAvailable;
+
+ /// No description provided for @pathTrace_refreshTooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'Refresh Path Trace.'**
+ String get pathTrace_refreshTooltip;
+
+ /// No description provided for @pathTrace_someHopsNoLocation.
+ ///
+ /// In en, this message translates to:
+ /// **'One or more of the hops is missing a location!'**
+ String get pathTrace_someHopsNoLocation;
+
+ /// No description provided for @pathTrace_clearTooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear path.'**
+ String get pathTrace_clearTooltip;
+
+ /// No description provided for @losSelectStartEnd.
+ ///
+ /// In en, this message translates to:
+ /// **'Select start and end nodes for LOS.'**
+ String get losSelectStartEnd;
+
+ /// No description provided for @losRunFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Line-of-sight check failed: {error}'**
+ String losRunFailed(String error);
+
+ /// No description provided for @losClearAllPoints.
+ ///
+ /// In en, this message translates to:
+ /// **'Clear all points'**
+ String get losClearAllPoints;
+
+ /// No description provided for @losRunToViewElevationProfile.
+ ///
+ /// In en, this message translates to:
+ /// **'Run LOS to view elevation profile'**
+ String get losRunToViewElevationProfile;
+
+ /// No description provided for @losMenuTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS Menu'**
+ String get losMenuTitle;
+
+ /// No description provided for @losMenuSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Tap nodes or long-press map for custom points'**
+ String get losMenuSubtitle;
+
+ /// No description provided for @losShowDisplayNodes.
+ ///
+ /// In en, this message translates to:
+ /// **'Show display nodes'**
+ String get losShowDisplayNodes;
+
+ /// No description provided for @losCustomPoints.
+ ///
+ /// In en, this message translates to:
+ /// **'Custom points'**
+ String get losCustomPoints;
+
+ /// No description provided for @losCustomPointLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Custom {index}'**
+ String losCustomPointLabel(int index);
+
+ /// No description provided for @losPointA.
+ ///
+ /// In en, this message translates to:
+ /// **'Point A'**
+ String get losPointA;
+
+ /// No description provided for @losPointB.
+ ///
+ /// In en, this message translates to:
+ /// **'Point B'**
+ String get losPointB;
+
+ /// No description provided for @losAntennaA.
+ ///
+ /// In en, this message translates to:
+ /// **'Antenna A: {value} {unit}'**
+ String losAntennaA(String value, String unit);
+
+ /// No description provided for @losAntennaB.
+ ///
+ /// In en, this message translates to:
+ /// **'Antenna B: {value} {unit}'**
+ String losAntennaB(String value, String unit);
+
+ /// No description provided for @losRun.
+ ///
+ /// In en, this message translates to:
+ /// **'Run LOS'**
+ String get losRun;
+
+ /// No description provided for @losNoElevationData.
+ ///
+ /// In en, this message translates to:
+ /// **'No elevation data'**
+ String get losNoElevationData;
+
+ /// No description provided for @losProfileClear.
+ ///
+ /// In en, this message translates to:
+ /// **'{distance} {distanceUnit}, clear LOS, min clearance {clearance} {heightUnit}'**
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ );
+
+ /// No description provided for @losProfileBlocked.
+ ///
+ /// In en, this message translates to:
+ /// **'{distance} {distanceUnit}, blocked by {obstruction} {heightUnit}'**
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ );
+
+ /// No description provided for @losStatusChecking.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS: checking...'**
+ String get losStatusChecking;
+
+ /// No description provided for @losStatusNoData.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS: no data'**
+ String get losStatusNoData;
+
+ /// No description provided for @losStatusSummary.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS: {clear}/{total} clear, {blocked} blocked, {unknown} unknown'**
+ String losStatusSummary(int clear, int total, int blocked, int unknown);
+
+ /// No description provided for @losErrorElevationUnavailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Elevation data unavailable for one or more samples.'**
+ String get losErrorElevationUnavailable;
+
+ /// No description provided for @losErrorInvalidInput.
+ ///
+ /// In en, this message translates to:
+ /// **'Invalid points/elevation data for LOS calculation.'**
+ String get losErrorInvalidInput;
+
+ /// No description provided for @losRenameCustomPoint.
+ ///
+ /// In en, this message translates to:
+ /// **'Rename custom point'**
+ String get losRenameCustomPoint;
+
+ /// No description provided for @losPointName.
+ ///
+ /// In en, this message translates to:
+ /// **'Point name'**
+ String get losPointName;
+
+ /// No description provided for @losShowPanelTooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'Show LOS panel'**
+ String get losShowPanelTooltip;
+
+ /// No description provided for @losHidePanelTooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'Hide LOS panel'**
+ String get losHidePanelTooltip;
+
+ /// No description provided for @losElevationAttribution.
+ ///
+ /// In en, this message translates to:
+ /// **'Elevation data: Open-Meteo (CC BY 4.0)'**
+ String get losElevationAttribution;
+
+ /// No description provided for @losLegendRadioHorizon.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio horizon'**
+ String get losLegendRadioHorizon;
+
+ /// No description provided for @losLegendLosBeam.
+ ///
+ /// In en, this message translates to:
+ /// **'LOS beam'**
+ String get losLegendLosBeam;
+
+ /// No description provided for @losLegendTerrain.
+ ///
+ /// In en, this message translates to:
+ /// **'Terrain'**
+ String get losLegendTerrain;
+
+ /// No description provided for @losFrequencyLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Frequency'**
+ String get losFrequencyLabel;
+
+ /// No description provided for @losFrequencyInfoTooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'View calculation details'**
+ String get losFrequencyInfoTooltip;
+
+ /// No description provided for @losFrequencyDialogTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio horizon calculation'**
+ String get losFrequencyDialogTitle;
+
+ /// Explain how the calculation uses the baseline frequency and derived k-factor.
+ ///
+ /// In en, this message translates to:
+ /// **'Starting from k={baselineK} at {baselineFreq} MHz, the calculation adjusts the k-factor for the current {frequencyMHz} MHz band, which defines the curved radio horizon cap.'**
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ );
+
+ /// No description provided for @contacts_pathTrace.
+ ///
+ /// In en, this message translates to:
+ /// **'Path Trace'**
+ String get contacts_pathTrace;
+
+ /// No description provided for @contacts_ping.
+ ///
+ /// In en, this message translates to:
+ /// **'Ping'**
+ String get contacts_ping;
+
+ /// No description provided for @contacts_repeaterPathTrace.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace to repeater'**
+ String get contacts_repeaterPathTrace;
+
+ /// No description provided for @contacts_repeaterPing.
+ ///
+ /// In en, this message translates to:
+ /// **'Ping repeater'**
+ String get contacts_repeaterPing;
+
+ /// No description provided for @contacts_roomPathTrace.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace to room server'**
+ String get contacts_roomPathTrace;
+
+ /// No description provided for @contacts_roomPing.
+ ///
+ /// In en, this message translates to:
+ /// **'Ping room server'**
+ String get contacts_roomPing;
+
+ /// No description provided for @contacts_chatTraceRoute.
+ ///
+ /// In en, this message translates to:
+ /// **'Path trace route'**
+ String get contacts_chatTraceRoute;
+
+ /// No description provided for @contacts_pathTraceTo.
+ ///
+ /// In en, this message translates to:
+ /// **'Trace route to {name}'**
+ String contacts_pathTraceTo(String name);
+
+ /// No description provided for @contacts_clipboardEmpty.
+ ///
+ /// In en, this message translates to:
+ /// **'Clipboard is empty.'**
+ String get contacts_clipboardEmpty;
+
+ /// No description provided for @contacts_invalidAdvertFormat.
+ ///
+ /// In en, this message translates to:
+ /// **'Invalid contact data'**
+ String get contacts_invalidAdvertFormat;
+
+ /// No description provided for @contacts_contactImported.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact has been imported.'**
+ String get contacts_contactImported;
+
+ /// No description provided for @contacts_contactImportFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed to import contact.'**
+ String get contacts_contactImportFailed;
+
+ /// No description provided for @contacts_zeroHopAdvert.
+ ///
+ /// In en, this message translates to:
+ /// **'Zero Hop Advert'**
+ String get contacts_zeroHopAdvert;
+
+ /// No description provided for @contacts_floodAdvert.
+ ///
+ /// In en, this message translates to:
+ /// **'Flood Advert'**
+ String get contacts_floodAdvert;
+
+ /// No description provided for @contacts_copyAdvertToClipboard.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Advert to Clipboard'**
+ String get contacts_copyAdvertToClipboard;
+
+ /// No description provided for @contacts_addContactFromClipboard.
+ ///
+ /// In en, this message translates to:
+ /// **'Add Contact from Clipboard'**
+ String get contacts_addContactFromClipboard;
+
+ /// No description provided for @contacts_ShareContact.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy contact to Clipboard'**
+ String get contacts_ShareContact;
+
+ /// No description provided for @contacts_ShareContactZeroHop.
+ ///
+ /// In en, this message translates to:
+ /// **'Share contact by advert'**
+ String get contacts_ShareContactZeroHop;
+
+ /// No description provided for @contacts_zeroHopContactAdvertSent.
+ ///
+ /// In en, this message translates to:
+ /// **'Sent contact by advert.'**
+ String get contacts_zeroHopContactAdvertSent;
+
+ /// No description provided for @contacts_zeroHopContactAdvertFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Failed to send contact.'**
+ String get contacts_zeroHopContactAdvertFailed;
+
+ /// No description provided for @contacts_contactAdvertCopied.
+ ///
+ /// In en, this message translates to:
+ /// **'Advert copied to Clipboard.'**
+ String get contacts_contactAdvertCopied;
+
+ /// No description provided for @contacts_contactAdvertCopyFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Copying advert to Clipboard failed.'**
+ String get contacts_contactAdvertCopyFailed;
+
+ /// No description provided for @notification_activityTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'MeshCore Activity'**
+ String get notification_activityTitle;
+
+ /// No description provided for @notification_messagesCount.
+ ///
+ /// In en, this message translates to:
+ /// **'{count} {count, plural, =1{message} other{messages}}'**
+ String notification_messagesCount(int count);
+
+ /// No description provided for @notification_channelMessagesCount.
+ ///
+ /// In en, this message translates to:
+ /// **'{count} {count, plural, =1{channel message} other{channel messages}}'**
+ String notification_channelMessagesCount(int count);
+
+ /// No description provided for @notification_newNodesCount.
+ ///
+ /// In en, this message translates to:
+ /// **'{count} {count, plural, =1{new node} other{new nodes}}'**
+ String notification_newNodesCount(int count);
+
+ /// No description provided for @notification_newTypeDiscovered.
+ ///
+ /// In en, this message translates to:
+ /// **'New {contactType} discovered'**
+ String notification_newTypeDiscovered(String contactType);
+
+ /// No description provided for @notification_receivedNewMessage.
+ ///
+ /// In en, this message translates to:
+ /// **'Received new message'**
+ String get notification_receivedNewMessage;
+
+ /// No description provided for @settings_gpxExportRepeaters.
+ ///
+ /// In en, this message translates to:
+ /// **'Export repeaters / room server to GPX'**
+ String get settings_gpxExportRepeaters;
+
+ /// No description provided for @settings_gpxExportRepeatersSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Exports repeaters / roomserver with a location to GPX file.'**
+ String get settings_gpxExportRepeatersSubtitle;
+
+ /// No description provided for @settings_gpxExportContacts.
+ ///
+ /// In en, this message translates to:
+ /// **'Export companions to GPX'**
+ String get settings_gpxExportContacts;
+
+ /// No description provided for @settings_gpxExportContactsSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Exports companions with a location to GPX file.'**
+ String get settings_gpxExportContactsSubtitle;
+
+ /// No description provided for @settings_gpxExportAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Export all contacts to GPX'**
+ String get settings_gpxExportAll;
+
+ /// No description provided for @settings_gpxExportAllSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Exports all contacts with a location to GPX file.'**
+ String get settings_gpxExportAllSubtitle;
+
+ /// No description provided for @settings_gpxExportSuccess.
+ ///
+ /// In en, this message translates to:
+ /// **'Successfully exported GPX file.'**
+ String get settings_gpxExportSuccess;
+
+ /// No description provided for @settings_gpxExportNoContacts.
+ ///
+ /// In en, this message translates to:
+ /// **'No contacts to export.'**
+ String get settings_gpxExportNoContacts;
+
+ /// No description provided for @settings_gpxExportNotAvailable.
+ ///
+ /// In en, this message translates to:
+ /// **'Not supported on your device/OS'**
+ String get settings_gpxExportNotAvailable;
+
+ /// No description provided for @settings_gpxExportError.
+ ///
+ /// In en, this message translates to:
+ /// **'There was an error when exporting.'**
+ String get settings_gpxExportError;
+
+ /// No description provided for @settings_gpxExportRepeatersRoom.
+ ///
+ /// In en, this message translates to:
+ /// **'Repeater & room server locations'**
+ String get settings_gpxExportRepeatersRoom;
+
+ /// No description provided for @settings_gpxExportChat.
+ ///
+ /// In en, this message translates to:
+ /// **'Companion locations'**
+ String get settings_gpxExportChat;
+
+ /// No description provided for @settings_gpxExportAllContacts.
+ ///
+ /// In en, this message translates to:
+ /// **'All contacts locations'**
+ String get settings_gpxExportAllContacts;
+
+ /// No description provided for @settings_gpxExportShareText.
+ ///
+ /// In en, this message translates to:
+ /// **'Map data exported from meshcore-open'**
+ String get settings_gpxExportShareText;
+
+ /// No description provided for @settings_gpxExportShareSubject.
+ ///
+ /// In en, this message translates to:
+ /// **'meshcore-open GPX map data export'**
+ String get settings_gpxExportShareSubject;
+
+ /// No description provided for @snrIndicator_nearByRepeaters.
+ ///
+ /// In en, this message translates to:
+ /// **'Nearby Repeaters'**
+ String get snrIndicator_nearByRepeaters;
+
+ /// No description provided for @snrIndicator_lastSeen.
+ ///
+ /// In en, this message translates to:
+ /// **'Last seen'**
+ String get snrIndicator_lastSeen;
+
+ /// No description provided for @contactsSettings_title.
+ ///
+ /// In en, this message translates to:
+ /// **'Contacts settings'**
+ String get contactsSettings_title;
+
+ /// No description provided for @contactsSettings_autoAddTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Automatic Discovery'**
+ String get contactsSettings_autoAddTitle;
+
+ /// No description provided for @contactsSettings_otherTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Other contact related settings'**
+ String get contactsSettings_otherTitle;
+
+ /// No description provided for @contactsSettings_autoAddUsersTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Auto-add users'**
+ String get contactsSettings_autoAddUsersTitle;
+
+ /// No description provided for @contactsSettings_autoAddUsersSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow the companion to automatically add discovered users.'**
+ String get contactsSettings_autoAddUsersSubtitle;
+
+ /// No description provided for @contactsSettings_autoAddRepeatersTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Auto-add repeaters'**
+ String get contactsSettings_autoAddRepeatersTitle;
+
+ /// No description provided for @contactsSettings_autoAddRepeatersSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow the companion to automatically add discovered repeaters.'**
+ String get contactsSettings_autoAddRepeatersSubtitle;
+
+ /// No description provided for @contactsSettings_autoAddRoomServersTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Auto-add room servers'**
+ String get contactsSettings_autoAddRoomServersTitle;
+
+ /// No description provided for @contactsSettings_autoAddRoomServersSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow the companion to automatically add discovered room servers.'**
+ String get contactsSettings_autoAddRoomServersSubtitle;
+
+ /// No description provided for @contactsSettings_autoAddSensorsTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Auto-add sensors'**
+ String get contactsSettings_autoAddSensorsTitle;
+
+ /// No description provided for @contactsSettings_autoAddSensorsSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Allow the companion to automatically add discovered sensors.'**
+ String get contactsSettings_autoAddSensorsSubtitle;
+
+ /// No description provided for @contactsSettings_overwriteOldestTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Overwrite Oldest'**
+ String get contactsSettings_overwriteOldestTitle;
+
+ /// No description provided for @contactsSettings_overwriteOldestSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'When the contact list is full, the oldest non-favorited contact will be replaced.'**
+ String get contactsSettings_overwriteOldestSubtitle;
+
+ /// No description provided for @discoveredContacts_Title.
+ ///
+ /// In en, this message translates to:
+ /// **'Discovered Contacts'**
+ String get discoveredContacts_Title;
+
+ /// No description provided for @discoveredContacts_noMatching.
+ ///
+ /// In en, this message translates to:
+ /// **'No matching contacts'**
+ String get discoveredContacts_noMatching;
+
+ /// No description provided for @discoveredContacts_searchHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Search discovered contacts'**
+ String get discoveredContacts_searchHint;
+
+ /// No description provided for @discoveredContacts_contactAdded.
+ ///
+ /// In en, this message translates to:
+ /// **'Contact added'**
+ String get discoveredContacts_contactAdded;
+
+ /// No description provided for @discoveredContacts_addContact.
+ ///
+ /// In en, this message translates to:
+ /// **'Add Contact'**
+ String get discoveredContacts_addContact;
+
+ /// No description provided for @discoveredContacts_copyContact.
+ ///
+ /// In en, this message translates to:
+ /// **'Copy Contact to clipboard'**
+ String get discoveredContacts_copyContact;
+
+ /// No description provided for @discoveredContacts_deleteContact.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete Discovered Contact'**
+ String get discoveredContacts_deleteContact;
+
+ /// No description provided for @discoveredContacts_deleteContactAll.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete All Discovered Contacts'**
+ String get discoveredContacts_deleteContactAll;
+
+ /// No description provided for @discoveredContacts_deleteContactAllContent.
+ ///
+ /// In en, this message translates to:
+ /// **'Are you sure you want to delete all discovered contacts?'**
+ String get discoveredContacts_deleteContactAllContent;
+
+ /// No description provided for @chat_sendCooldown.
+ ///
+ /// In en, this message translates to:
+ /// **'Please wait a moment before sending again.'**
+ String get chat_sendCooldown;
+
+ /// No description provided for @appSettings_jumpToOldestUnread.
+ ///
+ /// In en, this message translates to:
+ /// **'Jump to oldest unread'**
+ String get appSettings_jumpToOldestUnread;
+
+ /// No description provided for @appSettings_jumpToOldestUnreadSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'When opening a chat with unread messages, scroll to the first unread instead of the latest.'**
+ String get appSettings_jumpToOldestUnreadSubtitle;
+
+ /// No description provided for @appSettings_languageHu.
+ ///
+ /// In en, this message translates to:
+ /// **'Hungarian'**
+ String get appSettings_languageHu;
+
+ /// No description provided for @appSettings_languageJa.
+ ///
+ /// In en, this message translates to:
+ /// **'Japanese'**
+ String get appSettings_languageJa;
+
+ /// No description provided for @appSettings_languageKo.
+ ///
+ /// In en, this message translates to:
+ /// **'Korean'**
+ String get appSettings_languageKo;
+
+ /// No description provided for @radioStats_tooltip.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio & mesh stats'**
+ String get radioStats_tooltip;
+
+ /// No description provided for @radioStats_screenTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio stats'**
+ String get radioStats_screenTitle;
+
+ /// No description provided for @radioStats_notConnected.
+ ///
+ /// In en, this message translates to:
+ /// **'Connect to a device to view radio statistics.'**
+ String get radioStats_notConnected;
+
+ /// No description provided for @radioStats_firmwareTooOld.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio statistics require companion firmware v8 or newer.'**
+ String get radioStats_firmwareTooOld;
+
+ /// No description provided for @radioStats_waiting.
+ ///
+ /// In en, this message translates to:
+ /// **'Waiting for data…'**
+ String get radioStats_waiting;
+
+ /// No description provided for @radioStats_noiseFloor.
+ ///
+ /// In en, this message translates to:
+ /// **'Noise floor: {noiseDbm} dBm'**
+ String radioStats_noiseFloor(int noiseDbm);
+
+ /// No description provided for @radioStats_lastRssi.
+ ///
+ /// In en, this message translates to:
+ /// **'Last RSSI: {rssiDbm} dBm'**
+ String radioStats_lastRssi(int rssiDbm);
+
+ /// No description provided for @radioStats_lastSnr.
+ ///
+ /// In en, this message translates to:
+ /// **'Last SNR: {snr} dB'**
+ String radioStats_lastSnr(String snr);
+
+ /// No description provided for @radioStats_txAir.
+ ///
+ /// In en, this message translates to:
+ /// **'TX airtime (total): {seconds} s'**
+ String radioStats_txAir(int seconds);
+
+ /// No description provided for @radioStats_rxAir.
+ ///
+ /// In en, this message translates to:
+ /// **'RX airtime (total): {seconds} s'**
+ String radioStats_rxAir(int seconds);
+
+ /// No description provided for @radioStats_chartCaption.
+ ///
+ /// In en, this message translates to:
+ /// **'Noise floor (dBm) over recent samples.'**
+ String get radioStats_chartCaption;
+
+ /// No description provided for @radioStats_stripNoise.
+ ///
+ /// In en, this message translates to:
+ /// **'Noise floor: {noiseDbm} dBm'**
+ String radioStats_stripNoise(int noiseDbm);
+
+ /// No description provided for @radioStats_stripWaiting.
+ ///
+ /// In en, this message translates to:
+ /// **'Fetching radio stats…'**
+ String get radioStats_stripWaiting;
+
+ /// No description provided for @radioStats_settingsTile.
+ ///
+ /// In en, this message translates to:
+ /// **'Radio stats'**
+ String get radioStats_settingsTile;
+
+ /// No description provided for @radioStats_settingsSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Noise floor, RSSI, SNR, and airtime'**
+ String get radioStats_settingsSubtitle;
+
+ /// No description provided for @translation_title.
+ ///
+ /// In en, this message translates to:
+ /// **'Translation'**
+ String get translation_title;
+
+ /// No description provided for @translation_enableTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Enable translation'**
+ String get translation_enableTitle;
+
+ /// No description provided for @translation_enableSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Translate incoming messages and allow pre-send translation.'**
+ String get translation_enableSubtitle;
+
+ /// No description provided for @translation_composerTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Translate before sending'**
+ String get translation_composerTitle;
+
+ /// No description provided for @translation_composerSubtitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Controls the default state of the composer translation icon.'**
+ String get translation_composerSubtitle;
+
+ /// No description provided for @translation_targetLanguage.
+ ///
+ /// In en, this message translates to:
+ /// **'Target language'**
+ String get translation_targetLanguage;
+
+ /// No description provided for @translation_useAppLanguage.
+ ///
+ /// In en, this message translates to:
+ /// **'Use app language'**
+ String get translation_useAppLanguage;
+
+ /// No description provided for @translation_downloadedModelLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Downloaded model'**
+ String get translation_downloadedModelLabel;
+
+ /// No description provided for @translation_presetModelLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Preset Hugging Face model'**
+ String get translation_presetModelLabel;
+
+ /// No description provided for @translation_manualUrlLabel.
+ ///
+ /// In en, this message translates to:
+ /// **'Manual model URL'**
+ String get translation_manualUrlLabel;
+
+ /// No description provided for @translation_downloadModel.
+ ///
+ /// In en, this message translates to:
+ /// **'Download model'**
+ String get translation_downloadModel;
+
+ /// No description provided for @translation_downloading.
+ ///
+ /// In en, this message translates to:
+ /// **'Downloading...'**
+ String get translation_downloading;
+
+ /// No description provided for @translation_working.
+ ///
+ /// In en, this message translates to:
+ /// **'Working...'**
+ String get translation_working;
+
+ /// No description provided for @translation_stop.
+ ///
+ /// In en, this message translates to:
+ /// **'Stop'**
+ String get translation_stop;
+
+ /// No description provided for @translation_mergingChunks.
+ ///
+ /// In en, this message translates to:
+ /// **'Merging downloaded chunks into final file...'**
+ String get translation_mergingChunks;
+
+ /// No description provided for @translation_downloadedModels.
+ ///
+ /// In en, this message translates to:
+ /// **'Downloaded models'**
+ String get translation_downloadedModels;
+
+ /// No description provided for @translation_deleteModel.
+ ///
+ /// In en, this message translates to:
+ /// **'Delete model'**
+ String get translation_deleteModel;
+
+ /// No description provided for @translation_modelDownloaded.
+ ///
+ /// In en, this message translates to:
+ /// **'Translation model downloaded.'**
+ String get translation_modelDownloaded;
+
+ /// No description provided for @translation_downloadStopped.
+ ///
+ /// In en, this message translates to:
+ /// **'Download stopped.'**
+ String get translation_downloadStopped;
+
+ /// No description provided for @translation_downloadFailed.
+ ///
+ /// In en, this message translates to:
+ /// **'Download failed: {error}'**
+ String translation_downloadFailed(String error);
+
+ /// No description provided for @translation_enterUrlFirst.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter a model URL first.'**
+ String get translation_enterUrlFirst;
+
+ /// No description provided for @scanner_linuxPairingShowPin.
+ ///
+ /// In en, this message translates to:
+ /// **'Show PIN'**
+ String get scanner_linuxPairingShowPin;
+
+ /// No description provided for @scanner_linuxPairingHidePin.
+ ///
+ /// In en, this message translates to:
+ /// **'Hide PIN'**
+ String get scanner_linuxPairingHidePin;
+
+ /// No description provided for @scanner_linuxPairingPinTitle.
+ ///
+ /// In en, this message translates to:
+ /// **'Bluetooth Pairing PIN'**
+ String get scanner_linuxPairingPinTitle;
+
+ /// No description provided for @scanner_linuxPairingPinPrompt.
+ ///
+ /// In en, this message translates to:
+ /// **'Enter PIN for {deviceName} (leave blank if none).'**
+ String scanner_linuxPairingPinPrompt(String deviceName);
+
+ /// No description provided for @translation_messageTranslation.
+ ///
+ /// In en, this message translates to:
+ /// **'Message translation'**
+ String get translation_messageTranslation;
+
+ /// No description provided for @translation_translateBeforeSending.
+ ///
+ /// In en, this message translates to:
+ /// **'Translate before sending'**
+ String get translation_translateBeforeSending;
+
+ /// No description provided for @translation_composerEnabledHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Messages will be translated before send.'**
+ String get translation_composerEnabledHint;
+
+ /// No description provided for @translation_composerDisabledHint.
+ ///
+ /// In en, this message translates to:
+ /// **'Send messages in the original typed language.'**
+ String get translation_composerDisabledHint;
+
+ /// No description provided for @translation_translateTo.
+ ///
+ /// In en, this message translates to:
+ /// **'Translate to {language}'**
+ String translation_translateTo(String language);
+
+ /// No description provided for @translation_translationOptions.
+ ///
+ /// In en, this message translates to:
+ /// **'Translation options'**
+ String get translation_translationOptions;
+
+ /// No description provided for @translation_systemLanguage.
+ ///
+ /// In en, this message translates to:
+ /// **'System language'**
+ String get translation_systemLanguage;
}
class _AppLocalizationsDelegate
@@ -4671,13 +6406,18 @@ class _AppLocalizationsDelegate
'en',
'es',
'fr',
+ 'hu',
'it',
+ 'ja',
+ 'ko',
'nl',
'pl',
'pt',
+ 'ru',
'sk',
'sl',
'sv',
+ 'uk',
'zh',
].contains(locale.languageCode);
@@ -4698,20 +6438,30 @@ AppLocalizations lookupAppLocalizations(Locale locale) {
return AppLocalizationsEs();
case 'fr':
return AppLocalizationsFr();
+ case 'hu':
+ return AppLocalizationsHu();
case 'it':
return AppLocalizationsIt();
+ case 'ja':
+ return AppLocalizationsJa();
+ case 'ko':
+ return AppLocalizationsKo();
case 'nl':
return AppLocalizationsNl();
case 'pl':
return AppLocalizationsPl();
case 'pt':
return AppLocalizationsPt();
+ case 'ru':
+ return AppLocalizationsRu();
case 'sk':
return AppLocalizationsSk();
case 'sl':
return AppLocalizationsSl();
case 'sv':
return AppLocalizationsSv();
+ case 'uk':
+ return AppLocalizationsUk();
case 'zh':
return AppLocalizationsZh();
}
diff --git a/lib/l10n/app_localizations_bg.dart b/lib/l10n/app_localizations_bg.dart
index 314e702..b3e1279 100644
--- a/lib/l10n/app_localizations_bg.dart
+++ b/lib/l10n/app_localizations_bg.dart
@@ -38,6 +38,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get common_delete => 'Изтрий';
+ @override
+ String get common_deleteAll => 'Изтрий всичко';
+
@override
String get common_close => 'Затвори';
@@ -108,6 +111,134 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Свържете се чрез TCP';
+
+ @override
+ String get tcpHostLabel => 'IP адрес';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Пристанище';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Въведете крайната точка и свържете се.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Свързване към $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Необходим е IP адрес.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Портът трябва да бъде между 1 и 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Транспортът чрез TCP не се поддържа на тази платформа.';
+
+ @override
+ String get tcpErrorTimedOut => 'Връзката TCP изтекла.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Неуспешно е установено TCP връзката: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Свържете се чрез USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Изберете открития сериен уред и свържете директно към вашия MeshCore възел.';
+
+ @override
+ String get usbScreenStatus => 'Изберете USB устройство';
+
+ @override
+ String get usbScreenNote =>
+ 'USB серийната връзка е активна на поддържаните Android устройства и настолни платформи.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Няма открити USB устройства. Включете едно и опитайте отново.';
+
+ @override
+ String get usbErrorPermissionDenied => 'Не беше разрешено достъпът през USB.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Избраното USB устройство вече не е налично.';
+
+ @override
+ String get usbErrorInvalidPort => 'Изберете валитно USB устройство.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Друг мол за свързване през USB вече е в процес на изпълнение.';
+
+ @override
+ String get usbErrorNotConnected => 'Няма свързано USB устройство.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Не успях да отворя избраното USB устройство.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Не успях да се свържа с избраното USB устройство.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'USB серийната комуникация не се поддържа на тази платформа.';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB връзката вече е активирана.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Няма избран USB устройство.';
+
+ @override
+ String get usbErrorPortClosed => 'USB връзката не е активна.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Връзката прекъсна. Уверете се, че устройството има софтуер за USB връзка.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Устройство за четене на уеб серийни данни';
+
+ @override
+ String get usbStatus_notConnected => 'Изберете USB устройство';
+
+ @override
+ String get usbStatus_connecting => 'Свързване към USB устройство...';
+
+ @override
+ String get usbStatus_searching => 'Търсене на USB устройства...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Неуспешно свързване през USB: $error';
+ }
+
@override
String get scanner_scanning => 'Сканиране за устройства...';
@@ -143,6 +274,23 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get scanner_scan => 'Сканирай';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth е изключен.';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Моля, активирайте Bluetooth, за да сканирате за устройства.';
+
+ @override
+ String get scanner_chromeRequired => 'Изисква се браузър Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Това уеб приложение изисква Google Chrome или браузър, базиран на Chromium, за поддръжка на Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Активирайте Bluetooth';
+
@override
String get device_quickSwitch => 'Бързо превключване';
@@ -224,6 +372,13 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get settings_longitude => 'Дължина';
+ @override
+ String get settings_contactSettings => 'Настройки за контакти';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Настройки за добавяне на контакти.';
+
@override
String get settings_privacyMode => 'Режим на поверителност';
@@ -243,6 +398,52 @@ class AppLocalizationsBg extends AppLocalizations {
String get settings_privacyModeDisabled =>
'Режим на поверителност е деактивиран';
+ @override
+ String get settings_privacy => 'Настройки на поверителността';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Контролирайте каква информация се споделя.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Изберете каква информация устройството ви споделя с другите.';
+
+ @override
+ String get settings_denyAll => 'Откажи всичко';
+
+ @override
+ String get settings_allowByContact => 'Позволи по флагове за контакт';
+
+ @override
+ String get settings_allowAll => 'Позволи всичко';
+
+ @override
+ String get settings_telemetryBaseMode => 'Базов режим на телеметрия';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Режим на местоположение на телеметрията';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Режим на средата на телеметрията';
+
+ @override
+ String get settings_advertLocation => 'Място на обявата';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Включи местоположение в обявата';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Мулти-потвърди: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Режим на телеметрията е обновен';
+
@override
String get settings_actions => 'Действия';
@@ -316,6 +517,10 @@ class AppLocalizationsBg extends AppLocalizations {
String get settings_aboutDescription =>
'Отворен софтуер за Flutter клиент за MeshCore LoRa мрежови устройства.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Данни за надморска височина на LOS: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Име';
@@ -340,15 +545,6 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get settings_presets => 'Предварителни настройки';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Честота (MHz)';
@@ -377,10 +573,15 @@ class AppLocalizationsBg extends AppLocalizations {
String get settings_txPowerInvalid => 'Невалидна мощност на TX (0-22 dBm)';
@override
- String get settings_longRange => 'Дълъг обхват';
+ String get settings_clientRepeat => 'Без електричество – повторение';
@override
- String get settings_fastSpeed => 'Бърза скорост';
+ String get settings_clientRepeatSubtitle =>
+ 'Позволете на това устройство да предава пакети към мрежата за други устройства.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'За повторение извън мрежата са необходими честоти от 433, 869 или 918 MHz.';
@override
String settings_error(String message) {
@@ -450,6 +651,20 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Руски';
+
+ @override
+ String get appSettings_languageUk => 'Украински';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Разрешаване на проследяване на съобщения';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Показване на подробни метаданни за маршрутизация и синхронизация за съобщения';
+
@override
String get appSettings_notifications => 'Уведомления';
@@ -526,6 +741,51 @@ class AppLocalizationsBg extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Автоматично маршрутизирането е деактивирано';
+ @override
+ String get appSettings_maxRouteWeight =>
+ 'Максимално допустимо тегло на маршрута';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Максималното тегло, което един маршрут може да събере от успешни доставки.';
+
+ @override
+ String get appSettings_initialRouteWeight =>
+ 'Първоначална тежест на маршрута';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Начално тегло за новооткрити маршрути';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Увеличение на теглото за успех';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Тегло, добавено към път след успешно доставяне.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Намаляване на теглото, свързано с неуспех';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Тегло, което е било премахнато от пътя след неуспешен опит за доставка.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Максимален брой опити за изпращане на съобщение';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Брой опити за повторно изпращане, преди съобщението да бъде маркирано като неуспешно.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Батерия';
@@ -610,6 +870,15 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Кеш на офлайн карти';
+ @override
+ String get appSettings_unitsTitle => 'единици';
+
+ @override
+ String get appSettings_unitsMetric => 'Метрика (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Имперска (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'Няма избрана област';
@@ -648,7 +917,35 @@ class AppLocalizationsBg extends AppLocalizations {
'Контактите ще се появят, когато устройствата рекламират.';
@override
- String get contacts_searchContacts => 'Търсене на контакти...';
+ String get contacts_unread => 'Непрочетено';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Търси контакти...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Търсене на контакти...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Търсене на $number$str любими...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Търсене на $number$str потребители...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Търсене на $number$str повтарящи се...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Търсене на $number$str сървъри в стаята...';
+ }
@override
String get contacts_noUnreadContacts => 'Няма непрочетени контакти';
@@ -696,6 +993,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Името на групата е задължително.';
+ @override
+ String get contacts_groupNameReserved => 'Това име на група е запазено';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Групата \"$name\" вече съществува.';
@@ -735,6 +1035,42 @@ class AppLocalizationsBg extends AppLocalizations {
return 'Последно видян $days дни преди.';
}
+ @override
+ String get contact_info => 'Контактна информация';
+
+ @override
+ String get contact_settings => 'Настройки за контакти';
+
+ @override
+ String get contact_telemetry => 'Телеметрия';
+
+ @override
+ String get contact_lastSeen => 'Последно видян';
+
+ @override
+ String get contact_clearChat => 'Изчисти чата';
+
+ @override
+ String get contact_teleBase => 'Базата данни за телеметрия';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Позволи споделяне на ниво на батерията и основна телеметрия';
+
+ @override
+ String get contact_teleLoc => 'Местоположение на телеметрията';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Позволи споделяне на данни за местоположение';
+
+ @override
+ String get contact_teleEnv => 'Среда на телеметрия';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Позволи споделяне на данни от средносферните датчици';
+
@override
String get channels_title => 'Канали';
@@ -773,6 +1109,12 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get channels_editChannel => 'Редактирай канал';
+ @override
+ String get channels_muteChannel => 'Заглуши канала';
+
+ @override
+ String get channels_unmuteChannel => 'Включи известията на канала';
+
@override
String get channels_deleteChannel => 'Изтрий канала';
@@ -781,6 +1123,11 @@ class AppLocalizationsBg extends AppLocalizations {
return 'Изтрий \"$name\"? Това не може да бъде отменено.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Неуспешно изтриване на канала \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Каналът \"$name\" е изтрит';
@@ -892,6 +1239,14 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get chat_noMessages => 'Няма съобщения.';
+ @override
+ String get chat_sendMessage => 'Изпратете съобщение';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Изпрати съобщение на $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Изпрати съобщение, за да започнеш.';
@@ -911,11 +1266,6 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get chat_location => 'Местоположение';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Изпрати съобщение на $contactName';
- }
-
@override
String get chat_typeMessage => 'Въведете съобщение...';
@@ -1068,6 +1418,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get chat_pathManagement => 'Управление на пътища';
+ @override
+ String get chat_ShowAllPaths => 'Покажи всички пътища';
+
@override
String get chat_routingMode => 'Режим на маршрутизиране';
@@ -1207,9 +1560,33 @@ class AppLocalizationsBg extends AppLocalizations {
return 'Непрочетени: $count';
}
+ @override
+ String get chat_openLink => 'Отваряне на връзката?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Искате ли да отворите тази връзка в браузъра си?';
+
+ @override
+ String get chat_open => 'Отвори';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Не можа да се отвори връзката: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Невалиден формат на връзката';
+
@override
String get map_title => 'Карта на възлите';
+ @override
+ String get map_lineOfSight => 'Линия на видимост';
+
+ @override
+ String get map_losScreenTitle => 'Линия на видимост';
+
@override
String get map_noNodesWithLocation => 'Няма възли с данни за местоположение.';
@@ -1267,6 +1644,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Споделете маркер тук';
+ @override
+ String get map_setAsMyLocation => 'Задайте като моя местоположение';
+
@override
String get map_pinLabel => 'Етикетиране на пин';
@@ -1312,6 +1692,9 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get map_otherNodes => 'Други възли';
+ @override
+ String get map_showOverlaps => 'Покриване на ключа на повтаряча';
+
@override
String get map_keyPrefix => 'Префикс на ключа';
@@ -1327,6 +1710,16 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Покажи споделени маркери';
+ @override
+ String get map_showGuessedLocations =>
+ 'Покажете местоположенията на предположените възли.';
+
+ @override
+ String get map_showDiscoveryContacts => 'Покажи контакти за откриване';
+
+ @override
+ String get map_guessedLocation => 'Предполагано местоположение';
+
@override
String get map_lastSeenTime => 'Последна видяна дата';
@@ -1339,6 +1732,22 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get map_manageRepeater => 'Управление на Повтарящ се Елемент';
+ @override
+ String get map_tapToAdd =>
+ 'Натиснете върху възлите, за да ги добавите към пътя.';
+
+ @override
+ String get map_runTrace => 'Изпълни Път на Следване';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Върни се по същия път.';
+
+ @override
+ String get map_removeLast => 'Премахни Последно';
+
+ @override
+ String get map_pathTraceCancelled => 'Отменен е следването на пътя.';
+
@override
String get mapCache_title => 'Кеш на офлайн карти';
@@ -1610,9 +2019,18 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get room_management => 'Управление на сървъра за стая';
+ @override
+ String get repeater_guest => 'Информация за ретранслаторите';
+
+ @override
+ String get room_guest => 'Информация за сървъра на стаята';
+
@override
String get repeater_managementTools => 'Инструменти за управление';
+ @override
+ String get repeater_guestTools => 'Инструменти за гости';
+
@override
String get repeater_status => 'Статус';
@@ -1634,10 +2052,10 @@ class AppLocalizationsBg extends AppLocalizations {
String get repeater_cliSubtitle => 'Изпрати команди към ретранслатора';
@override
- String get repeater_neighbours => 'Съседи';
+ String get repeater_neighbors => 'Съседи';
@override
- String get repeater_neighboursSubtitle =>
+ String get repeater_neighborsSubtitle =>
'Преглед на съседни възли с нулев скок.';
@override
@@ -1647,6 +2065,14 @@ class AppLocalizationsBg extends AppLocalizations {
String get repeater_settingsSubtitle =>
'Конфигурирайте параметрите на репитера';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Синхронизиране на часовника след влизане';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Автоматично изпращайте съобщение \"синхронизиране на часовника\" след успешно влизане.';
+
@override
String get repeater_statusTitle => 'Статус на повтарянето';
@@ -2023,6 +2449,12 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Часовник';
+ @override
+ String get repeater_cliQuickClockSync => 'Синхронизация на часовника';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Открий Съседи';
+
@override
String get repeater_cliHelpAdvert => 'Изпраща рекламен пакет';
@@ -2337,7 +2769,7 @@ class AppLocalizationsBg extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Повторители Съседи';
+ String get neighbors_repeatersNeighbors => 'Повторители Съседи';
@override
String get neighbors_noData => 'Няма налични данни за съседи.';
@@ -2567,32 +2999,32 @@ class AppLocalizationsBg extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Регенерейрай секрет';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Регенерация на секретния ключ за \"$name\"? Всички членове ще трябва да сканират новия QR код, за да продължат комуникацията.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Регенерация';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Секретно презареждане за \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Актуализирай тайна';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Секретно обновено за \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Сканьорвайте новия QR код, за да актуализирате секрета за \"$name\"';
}
@override
@@ -2644,6 +3076,15 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get listFilter_all => 'Всички';
+ @override
+ String get listFilter_favorites => 'Любими';
+
+ @override
+ String get listFilter_addToFavorites => 'Добави към любими';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Премахване от списъка с любими';
+
@override
String get listFilter_users => 'Потребители';
@@ -2658,4 +3099,607 @@ class AppLocalizationsBg extends AppLocalizations {
@override
String get listFilter_newGroup => 'Нова група';
+
+ @override
+ String get pathTrace_you => 'Вие';
+
+ @override
+ String get pathTrace_failed => 'Пътят за проследяване не успя.';
+
+ @override
+ String get pathTrace_notAvailable => 'Пътека за проследяване не е достъпна.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Обнови Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Един или повече от хмелите липсва местоположение!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Изчисти пътя';
+
+ @override
+ String get losSelectStartEnd => 'Изберете начални и крайни възли за LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Проверката на пряката видимост е неуспешна: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Изчистете всички точки';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Стартирайте LOS, за да видите профила на надморската височина';
+
+ @override
+ String get losMenuTitle => 'LOS меню';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Докоснете възли или натиснете продължително карта за персонализирани точки';
+
+ @override
+ String get losShowDisplayNodes => 'Показване на възли на дисплея';
+
+ @override
+ String get losCustomPoints => 'Персонализирани точки';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Персонализирано $index';
+ }
+
+ @override
+ String get losPointA => 'Точка А';
+
+ @override
+ String get losPointB => 'Точка Б';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Антена A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Антена B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Стартирайте LOS';
+
+ @override
+ String get losNoElevationData => 'Няма данни за надморска височина';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, чист LOS, минимално разстояние $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, блокиран от $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: проверка...';
+
+ @override
+ String get losStatusNoData => 'LOS: няма данни';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total ясно, $blocked блокирано, $unknown неизвестно';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Няма налични данни за надморска височина за една или повече проби.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Невалидни данни за точки/надморска височина за изчисляване на LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Преименувайте персонализирана точка';
+
+ @override
+ String get losPointName => 'Име на точката';
+
+ @override
+ String get losShowPanelTooltip => 'Показване на LOS панел';
+
+ @override
+ String get losHidePanelTooltip => 'Скриване на LOS панела';
+
+ @override
+ String get losElevationAttribution =>
+ 'Данни за надморска височина: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Радиохоризонт';
+
+ @override
+ String get losLegendLosBeam => 'Линия на видимост';
+
+ @override
+ String get losLegendTerrain => 'Терен';
+
+ @override
+ String get losFrequencyLabel => 'Честота';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Преглед на детайли за изчислението';
+
+ @override
+ String get losFrequencyDialogTitle => 'Изчисляване на радиохоризонта';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Започвайки от k=$baselineK при $baselineFreq MHz, изчислението коригира k-фактора за текущата $frequencyMHz MHz лента, която определя границата на извития радиохоризонт.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Пътен проследяване';
+
+ @override
+ String get contacts_ping => 'Пинг';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Трасировка до повторител';
+
+ @override
+ String get contacts_repeaterPing => 'Пингване на повторителя';
+
+ @override
+ String get contacts_roomPathTrace => 'Трасиране на път до съ';
+
+ @override
+ String get contacts_roomPing => 'Ping на сървъра на стаята';
+
+ @override
+ String get contacts_chatTraceRoute => 'Трасиране на път';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Проследи маршрут към $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Клипборда е празна.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Невалидни данни за контакт';
+
+ @override
+ String get contacts_contactImported => 'Контактът е импортиран.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Контактът не е успешно импортиран.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Реклама без скок';
+
+ @override
+ String get contacts_floodAdvert => 'Потопна реклама';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Копирай обявата в клипборда';
+
+ @override
+ String get contacts_addContactFromClipboard => 'Добави контакт от клипборда';
+
+ @override
+ String get contacts_ShareContact => 'Копирай контакт в клипборда';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Сподели контакт чрез обява';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Изпратен контакт по обява.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Неуспешно изпращане на контакт.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Рекламата е копирана в клипборда.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Копирането на обявата в клипборда не успя.';
+
+ @override
+ String get notification_activityTitle => 'Активност на MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'съобщения',
+ one: 'съобщение',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'съобщения в канали',
+ one: 'съобщение в канал',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'нови възли',
+ one: 'нов възел',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Открит нов $contactType';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Получено ново съобщение';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Експортиране на повтарящи се устройства / сървър на стаята до GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Изпраща повторители / roomserver с местоположение в GPX файл.';
+
+ @override
+ String get settings_gpxExportContacts => 'Експортирай спътници към GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Експортира спътници с местоположение в GPX файл.';
+
+ @override
+ String get settings_gpxExportAll => 'Експортирай всички контакти в GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Експортира всички контакти с местоположение в файл GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Успешно изlexport на файл GPX.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Няма контакти за изlexport.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Не е поддържан на вашето устройство/ОС';
+
+ @override
+ String get settings_gpxExportError => 'Възникна грешка при изнасяне.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Местоположения на повторител и сървър на стаята';
+
+ @override
+ String get settings_gpxExportChat => 'Местоположения на спътници';
+
+ @override
+ String get settings_gpxExportAllContacts =>
+ 'Местоположения на всички контакти';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Картинни данни изнесени от meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open износ на данни за карта в формат GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Близки повтарящи се устройства';
+
+ @override
+ String get snrIndicator_lastSeen => 'Последно видян';
+
+ @override
+ String get contactsSettings_title => 'Настройки на контактите';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Автоматично откриване';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Други настройки свързани с контакти';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Автоматично добавяне на потребители';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Позволи на спътника да добавя автоматично откритите потребители.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Автоматично добавяне на повтарящи се елементи';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Позволи на спътника да добавя автоматично откритите повтарящи се устройства.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Автоматично добавяне на сървъри на стаите';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Позволи на спътника да добавя автоматично откритите сървъри на стаите.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Автоматично добавяне на датчици';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Позволи на спътника да добавя автоматично откритите датчици.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Премахни най-старото';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Когато списъкът с контакти е пълен, най-старият неключов контакт ще бъде заменен.';
+
+ @override
+ String get discoveredContacts_Title => 'Открити контакти';
+
+ @override
+ String get discoveredContacts_noMatching => 'Няма съвпадащи контакти';
+
+ @override
+ String get discoveredContacts_searchHint => 'Търсене на открити контакти';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Контакт добавен';
+
+ @override
+ String get discoveredContacts_addContact => 'Добави контакт';
+
+ @override
+ String get discoveredContacts_copyContact => 'Копирай контакт в клипборда';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Изтрий контакт';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Изтриване на Всички Открити Контакти';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Сигурни ли сте, че искате да изтриете всички открити контакти?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Моля, изчакайте малко, преди да изпратите отново.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Преминете към най-старата непочетена статия';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Когато отворите чат с непрочетени съобщения, плъзнете надолу, за да видите първото непрочетено съобщение, вместо най-новото.';
+
+ @override
+ String get appSettings_languageHu => 'Унгарски';
+
+ @override
+ String get appSettings_languageJa => 'Японски';
+
+ @override
+ String get appSettings_languageKo => 'Корейски';
+
+ @override
+ String get radioStats_tooltip => 'Статистика за радио и мрежа';
+
+ @override
+ String get radioStats_screenTitle =>
+ 'Статистически данни за радиопредаванията';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Свържете се с устройство, за да видите статистически данни за радиопредаване.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Статистиката на радиостанцията изисква съвместимо софтуерно решение версия 8 или по-нова.';
+
+ @override
+ String get radioStats_waiting => 'Изчакване на данни…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Ниво на шума: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Последен RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Последна стойност на SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Време на въздух (общо): $seconds секунди';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Общо време на използване на RX (в секунди): $seconds с';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Ниво на шума (dBm) за последните измервания.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Ниво на шума: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Извличане на данни за радиото…';
+
+ @override
+ String get radioStats_settingsTile => 'Статистически данни за радиостанции';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Ниво на шума, RSSI, SNR и време на пренос';
+
+ @override
+ String get translation_title => 'Превод';
+
+ @override
+ String get translation_enableTitle => 'Активирайте превода';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Превеждайте входящите съобщения и позволявайте предварително превеждане преди изпращане.';
+
+ @override
+ String get translation_composerTitle => 'Преведете преди да изпратите';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Контролира началния статус на иконата за превод, създадена от композитора.';
+
+ @override
+ String get translation_targetLanguage => 'Целеви език';
+
+ @override
+ String get translation_useAppLanguage => 'Използвайте езика на приложението';
+
+ @override
+ String get translation_downloadedModelLabel => 'Изтегнат модел';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Предварително конфигуриран модел от Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'URL на ръководството';
+
+ @override
+ String get translation_downloadModel => 'Изтеглете модела';
+
+ @override
+ String get translation_downloading => 'Изтегляне...';
+
+ @override
+ String get translation_working => 'Работа...';
+
+ @override
+ String get translation_stop => 'Спрете';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Съединяване на изтеглените части в един файл...';
+
+ @override
+ String get translation_downloadedModels => 'Изтеглени модели';
+
+ @override
+ String get translation_deleteModel => 'Изтриване на модела';
+
+ @override
+ String get translation_modelDownloaded => 'Моделът за превод е изтеглен.';
+
+ @override
+ String get translation_downloadStopped => 'Изтеглянето беше прекъснато.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Не успях да изтегля: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Въведете първо URL адрес на модела.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Покажи PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Скриване на PIN кода';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'PIN за съвпадение чрез Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Въведете PIN кода за $deviceName (оставете празно, ако няма такъв).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Превод на съобщението';
+
+ @override
+ String get translation_translateBeforeSending =>
+ 'Преведете преди да изпратите';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Съобщенията ще бъдат преведени, преди да бъдат изпратени.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Изпращайте съобщения на оригиналния въведен език.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Превеждане на $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Опции за превод';
+
+ @override
+ String get translation_systemLanguage => 'Език на системата';
}
diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart
index b884f3c..d7c1691 100644
--- a/lib/l10n/app_localizations_de.dart
+++ b/lib/l10n/app_localizations_de.dart
@@ -38,6 +38,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get common_delete => 'Löschen';
+ @override
+ String get common_deleteAll => 'Alles löschen';
+
@override
String get common_close => 'Schließen';
@@ -108,6 +111,137 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Verbinden über TCP';
+
+ @override
+ String get tcpHostLabel => 'IP-Adresse';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected =>
+ 'Geben Sie den Endpunkt ein und verbinden Sie sich.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Verbindung zu $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Eine IP-Adresse ist erforderlich.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'Die Portnummer muss zwischen 1 und 65535 liegen.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Die TCP-Übertragung wird auf dieser Plattform nicht unterstützt.';
+
+ @override
+ String get tcpErrorTimedOut => 'Die TCP-Verbindung ist abgelaufen.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Fehler beim TCP-Verbindungsaufbau: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Verbinden über USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Wählen Sie ein erkannten serielles Gerät aus und verbinden Sie es direkt mit Ihrem MeshCore-Knoten.';
+
+ @override
+ String get usbScreenStatus => 'Wählen Sie ein USB-Gerät aus';
+
+ @override
+ String get usbScreenNote =>
+ 'Die USB-Serielle Schnittstelle ist auf unterstützten Android-Geräten und Desktop-Plattformen aktiv.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Keine USB-Geräte gefunden. Schließen Sie eines an und aktualisieren Sie.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Die USB-Berechtigung wurde abgelehnt.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Das ausgewählte USB-Gerät ist nicht mehr verfügbar.';
+
+ @override
+ String get usbErrorInvalidPort => 'Wählen Sie ein gültiges USB-Gerät aus.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Eine weitere Anfrage für eine USB-Verbindung ist bereits in Bearbeitung.';
+
+ @override
+ String get usbErrorNotConnected => 'Es ist kein USB-Gerät angeschlossen.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Fehlgeschlagen beim Öffnen des ausgewählten USB-Geräts.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Keine Verbindung zum ausgewählten USB-Gerät hergestellt.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Die USB-Serielle Schnittstelle wird auf dieser Plattform nicht unterstützt.';
+
+ @override
+ String get usbErrorAlreadyActive =>
+ 'Eine USB-Verbindung ist bereits hergestellt.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Kein USB-Gerät wurde ausgewählt.';
+
+ @override
+ String get usbErrorPortClosed => 'Die USB-Verbindung ist nicht aktiv.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Verbindung konnte nicht hergestellt werden. Stellen Sie sicher, dass das Gerät die entsprechende USB-Firmware enthält.';
+
+ @override
+ String get usbFallbackDeviceName => 'Web-Serielle Geräte';
+
+ @override
+ String get usbStatus_notConnected => 'Wählen Sie ein USB-Gerät aus';
+
+ @override
+ String get usbStatus_connecting => 'Verbindung zum USB-Gerät...';
+
+ @override
+ String get usbStatus_searching => 'Suche nach USB-Geräten...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Fehler beim USB-Verbindungsaufbau: $error';
+ }
+
@override
String get scanner_scanning => 'Scannen nach Geräten...';
@@ -143,6 +277,23 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get scanner_scan => 'Scannen';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth ist deaktiviert.';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Bitte aktivieren Sie Bluetooth, um nach Geräten zu suchen.';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome Browser erforderlich';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Diese Webanwendung erfordert Google Chrome oder einen Chromium-basierten Browser für die Bluetooth-Unterstützung.';
+
+ @override
+ String get scanner_enableBluetooth => 'Bluetooth aktivieren';
+
@override
String get device_quickSwitch => 'Schnelles Umschalten';
@@ -160,7 +311,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_appSettingsSubtitle =>
- 'Benachrichtigungen, Messaging und Kartenwahrnehmungen';
+ 'Benachrichtigungen, Messaging und Kartenwahrnehmung';
@override
String get settings_nodeSettings => 'Knoten-Einstellungen';
@@ -223,6 +374,13 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_longitude => 'Längengrad';
+ @override
+ String get settings_contactSettings => 'Kontakteinstellungen';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Einstellungen für das Hinzufügen von Kontakten';
+
@override
String get settings_privacyMode => 'Privatsphäreeinstellung';
@@ -240,14 +398,58 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Datenschutzmodus deaktiviert';
+ @override
+ String get settings_privacy => 'Datenschutzeinstellungen';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Steuern Sie die Informationen, die freigegeben werden.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Wählen Sie die Informationen, die Ihr Gerät mit anderen teilt.';
+
+ @override
+ String get settings_denyAll => 'Alle ablehnen';
+
+ @override
+ String get settings_allowByContact => 'Zulassen durch Kontaktflaggen';
+
+ @override
+ String get settings_allowAll => 'Alles zulassen';
+
+ @override
+ String get settings_telemetryBaseMode => 'Telemetrie-Basismodus';
+
+ @override
+ String get settings_telemetryLocationMode => 'Telemetrie-Ortsmodus';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Telemetrie-Umgebungsmodus';
+
+ @override
+ String get settings_advertLocation => 'Anzeigenort';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Ort in der Anzeige einbeziehen';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Mehrfach-Bestätigungen: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Telemetriemodus aktualisiert';
+
@override
String get settings_actions => 'Aktionen';
@override
- String get settings_sendAdvertisement => 'Sende eine Ankündigung';
+ String get settings_sendAdvertisement => 'Sende Ankündigung';
@override
- String get settings_sendAdvertisementSubtitle => 'Sende Ankündigung';
+ String get settings_sendAdvertisementSubtitle => 'Sende eine Ankündigung';
@override
String get settings_advertisementSent => 'Ankündigung gesendet';
@@ -267,7 +469,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_refreshContactsSubtitle =>
- 'Kontakte-Liste vom Gerät neu laden';
+ 'Kontakt-Liste vom Gerät neu laden';
@override
String get settings_rebootDevice => 'Gerät neu starten';
@@ -310,6 +512,10 @@ class AppLocalizationsDe extends AppLocalizations {
String get settings_aboutDescription =>
'Ein Open-Source-Flutter-Client für MeshCore LoRa-Meshnetzwerkgeräte.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS-Höhendaten: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Name';
@@ -334,15 +540,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get settings_presets => 'Voreinstellungen';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frequenz (MHz)';
@@ -371,10 +568,15 @@ class AppLocalizationsDe extends AppLocalizations {
String get settings_txPowerInvalid => 'Ungültige TX-Leistung (0-22 dBm)';
@override
- String get settings_longRange => 'Grosse Reichweite';
+ String get settings_clientRepeat => 'Wiederholung, ohne Stromanschluss';
@override
- String get settings_fastSpeed => 'Schnelle Geschwindigkeit';
+ String get settings_clientRepeatSubtitle =>
+ 'Ermöglichen Sie diesem Gerät, Mesh-Pakete für andere zu wiederholen.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Die Kommunikation ohne Stromversorgung erfordert Frequenzen von 433, 869 oder 918 MHz.';
@override
String settings_error(String message) {
@@ -444,6 +646,20 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Russisch';
+
+ @override
+ String get appSettings_languageUk => 'Ukrainisch';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Nachrichtenverfolgung aktivieren';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Detaillierte Routing- und Timing-Metadaten für Nachrichten anzeigen';
+
@override
String get appSettings_notifications => 'Benachrichtigungen';
@@ -523,6 +739,49 @@ class AppLocalizationsDe extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Automatische Routenrotation deaktiviert';
+ @override
+ String get appSettings_maxRouteWeight => 'Maximale Gesamtstreckenlänge';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Maximales Gewicht, das ein Weg durch erfolgreiche Lieferungen erreichen kann.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Anfangs-Streckengewicht';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Ausgangsgewicht für neu entdeckte Pfade';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Erhöhung des Erfolgsgewichts';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Gewicht, das einem Pfad nach erfolgreicher Lieferung hinzugefügt wird.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Reduzierung des Gewichts bei Fehlern';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Gewicht, das nach einem fehlgeschlagenen Versand von einem Weg entfernt wurde';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Maximale Anzahl an Wiederholungsversuchen';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Anzahl der Versuche, eine Nachricht erneut zu senden, bevor sie als fehlgeschlagen markiert wird.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Akku';
@@ -607,6 +866,15 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Offline-Karten-Cache';
+ @override
+ String get appSettings_unitsTitle => 'Einheiten';
+
+ @override
+ String get appSettings_unitsMetric => 'Metrisch (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperial (ft/mi)';
+
@override
String get appSettings_noAreaSelected => 'Kein Bereich ausgewählt';
@@ -644,7 +912,35 @@ class AppLocalizationsDe extends AppLocalizations {
'Kontakte werden angezeigt, wenn Geräte eine Ankündigung machen.';
@override
- String get contacts_searchContacts => 'Suche Kontakte...';
+ String get contacts_unread => 'Ungelesen';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Kontakte suchen...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Suche Kontakte...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Suche $number$str Favoriten...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Suche $number$str Benutzer...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Suche $number$str Repeater...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Suche $number$str Raumserver...';
+ }
@override
String get contacts_noUnreadContacts => 'Keine ungesehene Kontakte';
@@ -662,7 +958,7 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
- String get contacts_manageRepeater => 'Wiederholungen verwalten';
+ String get contacts_manageRepeater => 'Repeater verwalten';
@override
String get contacts_manageRoom => 'Raum-Server verwalten';
@@ -693,6 +989,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Der Gruppennamen ist erforderlich.';
+ @override
+ String get contacts_groupNameReserved => 'Dieser Gruppenname ist reserviert';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Die Gruppe \"$name\" existiert bereits.';
@@ -709,29 +1008,64 @@ class AppLocalizationsDe extends AppLocalizations {
String get contacts_noMembers => 'Keine Mitglieder';
@override
- String get contacts_lastSeenNow => 'gerade gesehen';
+ String get contacts_lastSeenNow => 'kürzlich';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Letzte Sichtung vor $minutes Minuten.';
+ return '~ $minutes Min.';
}
@override
- String get contacts_lastSeenHourAgo => 'Letzte Sichtung vor 1 Stunde.';
+ String get contacts_lastSeenHourAgo => '~ 1 Std.';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return 'Letzte Sichtung vor $hours Stunden.';
+ return '~ $hours Std.';
}
@override
- String get contacts_lastSeenDayAgo => 'Letzte Sichtung vor 1 Tag';
+ String get contacts_lastSeenDayAgo => '~ 1 Tag';
@override
String contacts_lastSeenDaysAgo(int days) {
- return 'Letzte Sichtung $days Tage zuvor';
+ return '~ $days Tage';
}
+ @override
+ String get contact_info => 'Kontaktinformationen';
+
+ @override
+ String get contact_settings => 'Kontakteinstellungen';
+
+ @override
+ String get contact_telemetry => 'Telemetrie';
+
+ @override
+ String get contact_lastSeen => 'Zuletzt gesehen';
+
+ @override
+ String get contact_clearChat => 'Chat löschen';
+
+ @override
+ String get contact_teleBase => 'Telemetriebasis';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Erlauben des Freigebens des Batteriestands und der grundlegenden Telemetrie';
+
+ @override
+ String get contact_teleLoc => 'Telemetrieort';
+
+ @override
+ String get contact_teleLocSubtitle => 'Teilen von Standortdaten zulassen';
+
+ @override
+ String get contact_teleEnv => 'Telemetrieumgebung';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Teilen von Umgebungsensordaten zulassen';
+
@override
String get channels_title => 'Kanäle';
@@ -770,6 +1104,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get channels_editChannel => 'Kanal bearbeiten';
+ @override
+ String get channels_muteChannel => 'Kanal stummschalten';
+
+ @override
+ String get channels_unmuteChannel => 'Kanal Stummschaltung aufheben';
+
@override
String get channels_deleteChannel => 'Lösche den Kanal';
@@ -778,6 +1118,11 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Löschen von \"$name\"? Dies kann nicht rückgängig gemacht werden.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Kanal $name konnte nicht gelöscht werden';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Kanal \"$name\" gelöscht';
@@ -796,7 +1141,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get channels_usePublicChannel => 'Verwende öffentlichen Kanal';
@override
- String get channels_standardPublicPsk => 'Standard-Öffentliche PSK';
+ String get channels_standardPublicPsk => 'Öffentliche Standard PSK';
@override
String get channels_pskHex => 'PSK (Hex)';
@@ -893,6 +1238,14 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_noMessages => 'Noch keine Nachrichten.';
+ @override
+ String get chat_sendMessage => 'Nachricht senden';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Sende eine Nachricht an $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Eine Nachricht senden, um anzufangen.';
@@ -912,11 +1265,6 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_location => 'Ort';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Sende eine Nachricht an $contactName';
- }
-
@override
String get chat_typeMessage => 'Eine Nachricht eingeben...';
@@ -1029,11 +1377,11 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
- String get debugFrame_textMessageHeader => 'Textnachricht-Frame:';
+ String get debugFrame_textMessageHeader => 'Textnachrichten Frame:';
@override
String debugFrame_destinationPubKey(String pubKey) {
- return '- Ziel-Pub-Schlüssel: $pubKey';
+ return '- Ziel-Public-Schlüssel: $pubKey';
}
@override
@@ -1068,6 +1416,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_pathManagement => 'Pfadverwaltung';
+ @override
+ String get chat_ShowAllPaths => 'Alle Pfade anzeigen';
+
@override
String get chat_routingMode => 'Routenmodus';
@@ -1080,7 +1431,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_recentAckPaths =>
- 'Aktuelle ACK-Pfade (tasten, um zu verwenden):';
+ 'Aktuelle ACK-Pfade (antippen, um zu verwenden):';
@override
String get chat_pathHistoryFull =>
@@ -1111,7 +1462,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get chat_noPathHistoryYet =>
- 'Keine eine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.';
+ 'Keine Pfadhistorie vorhanden.\nSende eine Nachricht, um Pfade zu entdecken.';
@override
String get chat_pathActions => 'Pfadaktionen:';
@@ -1206,9 +1557,33 @@ class AppLocalizationsDe extends AppLocalizations {
return 'Ungelesen: $count';
}
+ @override
+ String get chat_openLink => 'Link öffnen?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Möchten Sie diesen Link in Ihrem Browser öffnen?';
+
+ @override
+ String get chat_open => 'Öffnen';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Link konnte nicht geöffnet werden: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Ungültiges Link-Format';
+
@override
String get map_title => 'Karte';
+ @override
+ String get map_lineOfSight => 'Sichtlinie';
+
+ @override
+ String get map_losScreenTitle => 'Sichtlinie';
+
@override
String get map_noNodesWithLocation => 'Keine Knoten mit Standortdaten';
@@ -1266,6 +1641,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Teilen Sie den Marker hier.';
+ @override
+ String get map_setAsMyLocation => 'Als meine aktuelle Position festlegen';
+
@override
String get map_pinLabel => 'Pin Name';
@@ -1311,6 +1689,9 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get map_otherNodes => 'Andere Knoten';
+ @override
+ String get map_showOverlaps => 'Überlappungen der Repeater-Taste';
+
@override
String get map_keyPrefix => 'Schlüsselpräfix';
@@ -1326,6 +1707,16 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Zeige gemeinsam genutzte Marker';
+ @override
+ String get map_showGuessedLocations =>
+ 'Zeige die vermuteten Knotenpositionen';
+
+ @override
+ String get map_showDiscoveryContacts => 'Entdeckungs-Kontakte anzeigen';
+
+ @override
+ String get map_guessedLocation => 'Geschätzter Ort';
+
@override
String get map_lastSeenTime => 'Letzte Sichtung';
@@ -1338,6 +1729,23 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get map_manageRepeater => 'Repeater verwalten';
+ @override
+ String get map_tapToAdd =>
+ 'Tippen Sie auf Knoten, um sie zum Pfad hinzuzufügen.';
+
+ @override
+ String get map_runTrace => 'Pfadverlauf ausführen';
+
+ @override
+ String get map_runTraceWithReturnPath =>
+ 'Auf dem gleichen Pfad zurückkehren.';
+
+ @override
+ String get map_removeLast => 'Letztes Entfernen';
+
+ @override
+ String get map_pathTraceCancelled => 'Pfadverfolgung abgebrochen.';
+
@override
String get mapCache_title => 'Offline-Karten-Cache';
@@ -1394,7 +1802,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String mapCache_estimatedTiles(int count) {
- return 'Geschätzte Fliesen: $count';
+ return 'Geschätzte Kacheln: $count';
}
@override
@@ -1568,7 +1976,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get path_hexPrefixInstructions =>
- 'Gebe für jeden Hopfen 2-stellige Hex-Präfixe ein, getrennt durch Kommas.';
+ 'Gebe für jeden Zwischen-Hop das 2-stellige Hex-Präfix ein, getrennt durch Kommas.';
@override
String get path_hexPrefixExample =>
@@ -1609,9 +2017,18 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get room_management => 'Raum-Server-Verwaltung';
+ @override
+ String get repeater_guest => 'Informationen zu Repeatern';
+
+ @override
+ String get room_guest => 'Informationen zum Room Server';
+
@override
String get repeater_managementTools => 'Verwaltungs-Tools';
+ @override
+ String get repeater_guestTools => 'Gastwerkzeuge';
+
@override
String get repeater_status => 'Status';
@@ -1633,10 +2050,10 @@ class AppLocalizationsDe extends AppLocalizations {
String get repeater_cliSubtitle => 'Sende Befehle an den Repeater';
@override
- String get repeater_neighbours => 'Nachbarn';
+ String get repeater_neighbors => 'Nachbarn';
@override
- String get repeater_neighboursSubtitle => 'Anzahl der Hop-Nachbarn anzeigen.';
+ String get repeater_neighborsSubtitle => 'Anzahl der Hop-Nachbarn anzeigen.';
@override
String get repeater_settings => 'Einstellungen';
@@ -1644,6 +2061,14 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Repeater-parameter konfigurieren';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Uhrzeit-Synchronisation nach dem Anmelden';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatisch \"Uhrzeit-Synchronisierung\" nach erfolgreicher Anmeldung senden.';
+
@override
String get repeater_statusTitle => 'Repeaterstatus';
@@ -1665,7 +2090,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_statusRequestTimeout =>
- 'Statusanfrage zeitweise fehlgeschlagen.';
+ 'Statusanfrage durch Timeout fehlgeschlagen.';
@override
String repeater_errorLoadingStatus(String error) {
@@ -1742,7 +2167,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String repeater_duplicatesFloodDirect(String flood, String direct) {
- return 'Überflut: $flood, Direkt: $direct';
+ return 'Flut: $flood, Direkt: $direct';
}
@override
@@ -1773,7 +2198,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_guestPasswordHelper =>
- 'Schreibgeschützter Zugriffspasswort';
+ 'Schreibgeschütztes Zugriffspasswort';
@override
String get repeater_radioSettings => 'Funk Einstellungen';
@@ -1870,8 +2295,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get repeater_rebootRepeater => 'Neustart Repeater';
@override
- String get repeater_rebootRepeaterSubtitle =>
- 'Wiederholen Sie das Repeater-Gerät.';
+ String get repeater_rebootRepeaterSubtitle => 'Repeater-Gerät neu starten.';
@override
String get repeater_rebootRepeaterConfirm =>
@@ -1969,7 +2393,7 @@ class AppLocalizationsDe extends AppLocalizations {
String get repeater_cliTitle => 'Repeater CLI';
@override
- String get repeater_debugNextCommand => 'Fehlersuche Nächster Befehl';
+ String get repeater_debugNextCommand => 'Fehlersuche des nächsten Befehls';
@override
String get repeater_commandHelp => 'Hilfe';
@@ -1982,7 +2406,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_typeCommandOrUseQuick =>
- 'Geben Sie einen Befehl unten ein oder verwenden Sie Schnellbefehle';
+ 'Geben Sie unten einen Befehl ein oder verwenden Sie die Schnellbefehle';
@override
String get repeater_enterCommandHint => 'Geben Sie den Befehl ein...';
@@ -2025,6 +2449,12 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Uhr';
+ @override
+ String get repeater_cliQuickClockSync => 'Uhr Synchronisieren';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Entdecke Nachbarn';
+
@override
String get repeater_cliHelpAdvert => 'Sendet eine Ankündigung';
@@ -2108,7 +2538,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_cliHelpSetRxDelay =>
- 'Sets (experimentell) als Basis (muss > 1 sein für den Effekt) zur Anwendung einer leichten Verzögerung bei empfangenen Paketen, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.';
+ 'Fügt eine leichte Verzögerung bei empfangenen Paketen hinzu, basierend auf Signalstärke/Punktzahl. Auf 0 setzen, um die Funktion zu deaktivieren.';
@override
String get repeater_cliHelpSetTxDelay =>
@@ -2152,7 +2582,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_cliHelpGetBridgeType =>
- 'Ruft Brückentyp none, rs232, espnow ab.';
+ 'Ruft Brückentyp: none, rs232, espnow ab.';
@override
String get repeater_cliHelpLogStart =>
@@ -2179,7 +2609,7 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get repeater_cliHelpRegionLoad =>
- 'Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingedruckt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile/des Befehls.';
+ 'Hinweis: Dies ist ein spezieller Mehrbefehl-Aufruf. Jeder nachfolgende Befehl ist ein Regionsname (eingerückt mit Leerzeichen zur Angabe der übergeordneten Hierarchie, mit mindestens einem Leerzeichen). Beendet durch das Senden einer Leerzeile.';
@override
String get repeater_cliHelpRegionGet =>
@@ -2328,10 +2758,11 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
- String get neighbors_receivedData => 'Empfangene Nachbarendaten';
+ String get neighbors_receivedData => 'Empfangene Nachbarsdaten';
@override
- String get neighbors_requestTimedOut => 'Nachbarn melden zeitweise Ausfall.';
+ String get neighbors_requestTimedOut =>
+ 'Anfrage durch Timeout fehlgeschlagen.';
@override
String neighbors_errorLoading(String error) {
@@ -2339,19 +2770,19 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Wiederholer Nachbarn';
+ String get neighbors_repeatersNeighbors => 'Nachbarn';
@override
- String get neighbors_noData => 'Keine Nachbardaten verfügbar.';
+ String get neighbors_noData => 'Keine Nachbarsdaten verfügbar.';
@override
String neighbors_unknownContact(String pubkey) {
- return 'Unbekannte $pubkey';
+ return 'Unbekannt $pubkey';
}
@override
String neighbors_heardAgo(String time) {
- return 'Hörte: $time vor her.';
+ return 'Gehört vor: $time';
}
@override
@@ -2371,7 +2802,7 @@ class AppLocalizationsDe extends AppLocalizations {
'Die Detailangaben für dieses Paket sind nicht verfügbar.';
@override
- String get channelPath_messageDetails => 'Nachrichtsdetails';
+ String get channelPath_messageDetails => 'Nachrichtendetails';
@override
String get channelPath_senderLabel => 'Sender';
@@ -2570,32 +3001,32 @@ class AppLocalizationsDe extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Neugenerierung des Schlüssels';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Nehmen Sie den geheimen Schlüssel für \"$name\" neu auf? Alle Mitglieder müssen den neuen QR-Code scannen, um die Kommunikation fortzusetzen.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Neu generieren';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Wiederherstellung des Schlüssels für \"$name\" erfolgreich';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Aktualisieren Sie den Schlüssel';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Schlüssel für \"$name\" aktualisiert';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Scannen Sie den neuen QR-Code, um das Geheimnis für \"$name\" zu aktualisieren.';
}
@override
@@ -2607,14 +3038,14 @@ class AppLocalizationsDe extends AppLocalizations {
'Füge einen Hashtag-Kanal für diese Community hinzu';
@override
- String get community_selectCommunity => 'Wählen Sie Community';
+ String get community_selectCommunity => 'Wählen Sie eine Community';
@override
String get community_regularHashtag => 'Regulärer Hashtag';
@override
String get community_regularHashtagDesc =>
- 'Öffentliches Hashtag (jeder kann teilnehmen)';
+ 'Öffentlicher Hashtag (jeder kann teilnehmen)';
@override
String get community_communityHashtag => 'Community Hashtag';
@@ -2649,6 +3080,15 @@ class AppLocalizationsDe extends AppLocalizations {
@override
String get listFilter_all => 'Alle';
+ @override
+ String get listFilter_favorites => 'Favoriten';
+
+ @override
+ String get listFilter_addToFavorites => 'Zu Favoriten hinzufügen';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Aus Favoriten entfernen';
+
@override
String get listFilter_users => 'Benutzer';
@@ -2659,8 +3099,618 @@ class AppLocalizationsDe extends AppLocalizations {
String get listFilter_roomServers => 'Raumserver';
@override
- String get listFilter_unreadOnly => 'Nur nicht gelesen';
+ String get listFilter_unreadOnly => 'Nicht gelesen';
@override
String get listFilter_newGroup => 'Neue Gruppe';
+
+ @override
+ String get pathTrace_you => 'Du';
+
+ @override
+ String get pathTrace_failed => 'Pfadverfolgung fehlgeschlagen.';
+
+ @override
+ String get pathTrace_notAvailable => 'Pfadverfolgung nicht verfügbar.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Path Trace aktualisieren.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Bei einer oder mehreren Knoten fehlt der Standort!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Pfad löschen';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Wählen Sie Start- und Endknoten für LOS aus.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Sichtlinienprüfung fehlgeschlagen: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Löschen Sie alle Punkte';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Führen Sie LOS aus, um das Höhenprofil anzuzeigen';
+
+ @override
+ String get losMenuTitle => 'LOS-Menü';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Tippen Sie auf Knoten oder drücken Sie lange auf die Karte, um benutzerdefinierte Punkte anzuzeigen';
+
+ @override
+ String get losShowDisplayNodes => 'Anzeigeknoten anzeigen';
+
+ @override
+ String get losCustomPoints => 'Benutzerdefinierte Punkte';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Benutzerdefiniert $index';
+ }
+
+ @override
+ String get losPointA => 'Punkt A';
+
+ @override
+ String get losPointB => 'Punkt B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenne A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenne B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Führen Sie LOS aus';
+
+ @override
+ String get losNoElevationData => 'Keine Höhendaten';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, freie Sichtlinie, Mindestabstand $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blockiert durch $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: Überprüfen...';
+
+ @override
+ String get losStatusNoData => 'LOS: keine Daten';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'Sichtlinie: $clear/$total frei, $blocked blockiert, $unknown unbekannt';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Für eine oder mehrere Proben sind keine Höhendaten verfügbar.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Ungültige Punkte/Höhendaten für die LOS-Berechnung.';
+
+ @override
+ String get losRenameCustomPoint =>
+ 'Benennen Sie den benutzerdefinierten Punkt um';
+
+ @override
+ String get losPointName => 'Punktname';
+
+ @override
+ String get losShowPanelTooltip => 'LOS-Panel anzeigen';
+
+ @override
+ String get losHidePanelTooltip => 'LOS-Panel ausblenden';
+
+ @override
+ String get losElevationAttribution => 'Höhendaten: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Funkhorizont';
+
+ @override
+ String get losLegendLosBeam => 'Sichtlinie';
+
+ @override
+ String get losLegendTerrain => 'Gelände';
+
+ @override
+ String get losFrequencyLabel => 'Frequenz';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Details zur Berechnung anzeigen';
+
+ @override
+ String get losFrequencyDialogTitle => 'Berechnung des Funkhorizonts';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Ausgehend von k=$baselineK bei $baselineFreq MHz passt die Berechnung den k-Faktor für das aktuelle $frequencyMHz MHz-Band an, das die gekrümmte Funkhorizontobergrenze definiert.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Pfadverfolgung';
+
+ @override
+ String get contacts_ping => 'Pingen';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Pfadverfolgung zum Repeater';
+
+ @override
+ String get contacts_repeaterPing => 'Repeater pingen';
+
+ @override
+ String get contacts_roomPathTrace => 'Pfadverfolgung zum Raumserver';
+
+ @override
+ String get contacts_roomPing => 'Raumserver anpingen';
+
+ @override
+ String get contacts_chatTraceRoute => 'Pfadverfolgungsroute';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Route nach $name verfolgen';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Die Zwischenablage ist leer.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Ungültige Kontaktdaten';
+
+ @override
+ String get contacts_contactImported => 'Kontakt wurde importiert.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Kontakt konnte nicht importiert werden';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Zero-Hop-Ankündigung';
+
+ @override
+ String get contacts_floodAdvert => 'Flut-Ankündigung';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Ankündigung in die Zwischenablage kopieren';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Kontakt aus Zwischenablage hinzufügen';
+
+ @override
+ String get contacts_ShareContact => 'Kontakt in die Zwischenablage kopieren';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Kontakt über Anzeige teilen';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Kontakt über Anzeige gesendet';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Kontakt konnte nicht gesendet werden.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Anzeige in die Zwischenablage kopiert.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopieren der Ankündigung in die Zwischenablage fehlgeschlagen.';
+
+ @override
+ String get notification_activityTitle => 'MeshCore Aktivität';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Nachrichten',
+ one: 'Nachricht',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'Kanalnachrichten',
+ one: 'Kanalnachricht',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'neue Knoten',
+ one: 'neuer Knoten',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Neuer $contactType entdeckt';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Neue Nachricht empfangen';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Repeater und Raumserver als GPX exportieren';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exportiert Repeater und Raumserver mit einem Standort in eine GPX-Datei.';
+
+ @override
+ String get settings_gpxExportContacts => 'Kontakte als GPX exportieren';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exportiert Kontakte mit einem Ort in eine GPX-Datei.';
+
+ @override
+ String get settings_gpxExportAll => 'Alle Knoten als GPX exportieren';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exportiert alle Knoten mit einem Standort in eine GPX-Datei.';
+
+ @override
+ String get settings_gpxExportSuccess => 'GPX-Datei erfolgreich exportiert.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Keine Kontakte zum Exportieren.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Nicht auf Ihrem Gerät/Betriebssystem unterstützt';
+
+ @override
+ String get settings_gpxExportError =>
+ 'Beim Export ist ein Fehler aufgetreten.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Repeater- und Raumserver-Standorte';
+
+ @override
+ String get settings_gpxExportChat => 'Kontaktstandorte';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Alle Kontaktstandorte';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'GPX-Kartendaten aus meshcore-open exportiert';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'GPX-Kartendaten aus meshcore-open exportieren';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'In der Nähe befindliche Repeater';
+
+ @override
+ String get snrIndicator_lastSeen => 'Zuletzt gesehen';
+
+ @override
+ String get contactsSettings_title => 'Kontakteinstellungen';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatische Erkennung';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Weitere Einstellungen zu Kontakten';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Automatische Hinzufügung von Benutzern';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Ermöglichen Sie dem Begleiter, automatisch entdeckte Benutzer hinzuzufügen';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Automatisch Repeater hinzufügen';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Ermöglichen Sie dem Begleiter, automatisch entdeckte Repeater hinzuzufügen.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Automatisch Raumservers hinzufügen';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Ermöglichen Sie dem Begleiter, entdeckte Raumserver automatisch hinzuzufügen';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Automatisch Sensoren hinzufügen';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Ermöglichen Sie dem Begleiter, automatisch entdeckte Sensoren hinzuzufügen';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle =>
+ 'Überschreiben des Ältesten';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Wenn die Kontaktliste voll ist, wird der älteste nicht favorisierte Kontakt ersetzt.';
+
+ @override
+ String get discoveredContacts_Title => 'Entdeckte Kontakte';
+
+ @override
+ String get discoveredContacts_noMatching => 'Keine passenden Kontakte';
+
+ @override
+ String get discoveredContacts_searchHint => 'Entdeckte Kontakte suchen';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kontakt hinzugefügt';
+
+ @override
+ String get discoveredContacts_addContact => 'Kontakt hinzufügen';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Kontakt in die Zwischenablage kopieren';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Kontakt löschen';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Alle entdeckten Kontakte löschen';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Sind Sie sicher, dass Sie alle gefundenen Kontakte löschen möchten?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Bitte warten Sie einen Moment, bevor Sie erneut senden.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Zum ältesten, nicht gelesenen Eintrag springen';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Wenn Sie ein Chatfenster öffnen, in dem Nachrichten vorhanden sind, die noch nicht gelesen wurden, scrollen Sie zu der ersten unlesenen Nachricht, anstatt zur neuesten.';
+
+ @override
+ String get appSettings_languageHu => 'Ungarisch';
+
+ @override
+ String get appSettings_languageJa => 'Japanisch';
+
+ @override
+ String get appSettings_languageKo => 'Koreanisch';
+
+ @override
+ String get radioStats_tooltip => 'Daten zu Radio- und Mesh-Netzwerken';
+
+ @override
+ String get radioStats_screenTitle => 'Senderinformationen';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Verbinden Sie ein Gerät, um Radiostatisiken anzuzeigen.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Für die Verwendung der Funkstatistiken ist die Firmware-Version 8 oder höher erforderlich.';
+
+ @override
+ String get radioStats_waiting => 'Warte auf Daten…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Rauschpegel: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Letzter RSSI-Wert: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Letzter SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Gesamt-TX-Zeit: $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Gesamt-RX-Zeit: $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Rauschpegel (dBm) basierend auf den letzten Messwerten.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Rauschpegel: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Abrufen von Radiostatus…';
+
+ @override
+ String get radioStats_settingsTile => 'Senderinformationen';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Rauschpegel, RSSI, Signal-Rausch-Verhältnis (SNR) und Nutzzeit';
+
+ @override
+ String get translation_title => 'Übersetzung';
+
+ @override
+ String get translation_enableTitle => 'Aktivieren Sie die Übersetzung';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Nachrichten empfangen und übersetzen sowie die Möglichkeit bieten, Nachrichten vor dem Versenden zu übersetzen.';
+
+ @override
+ String get translation_composerTitle => 'Übersetzen Sie vor dem Versenden';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Steuert den Standardzustand des Icons für die Übersetzung des Komponisten.';
+
+ @override
+ String get translation_targetLanguage => 'Zielsprache';
+
+ @override
+ String get translation_useAppLanguage => 'Verwenden Sie die App-Sprache';
+
+ @override
+ String get translation_downloadedModelLabel => 'Heruntergeladenes Modell';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Vordefinierter Hugging Face-Modell';
+
+ @override
+ String get translation_manualUrlLabel => 'URL für das manuelle Modell';
+
+ @override
+ String get translation_downloadModel => 'Modell herunterladen';
+
+ @override
+ String get translation_downloading => 'Herunterladen...';
+
+ @override
+ String get translation_working => 'Arbeiten...';
+
+ @override
+ String get translation_stop => 'Stopp';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Zusammenführen der heruntergeladenen Teile in die finale Datei...';
+
+ @override
+ String get translation_downloadedModels => 'Heruntergeladene Modelle';
+
+ @override
+ String get translation_deleteModel => 'Modell löschen';
+
+ @override
+ String get translation_modelDownloaded =>
+ 'Übersetzungsmotor heruntergeladen.';
+
+ @override
+ String get translation_downloadStopped => 'Herunterladen abgebrochen.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Download fehlgeschlagen: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Geben Sie zunächst die URL eines Modells ein.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'PIN anzeigen';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'PIN ausblenden';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth-Paarungs-PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Geben Sie die PIN für $deviceName ein (leer lassen, falls keine).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Nachricht übersetzen';
+
+ @override
+ String get translation_translateBeforeSending =>
+ 'Übersetzen Sie vor dem Versenden';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Die Nachrichten werden vor dem Versenden übersetzt.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Nachrichten in der ursprünglichen, getippten Sprache senden.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Übersetzen Sie auf $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Übersetzungsmöglichkeiten';
+
+ @override
+ String get translation_systemLanguage => 'Sprache des Systems';
}
diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart
index 96ba1b9..a2a88b0 100644
--- a/lib/l10n/app_localizations_en.dart
+++ b/lib/l10n/app_localizations_en.dart
@@ -38,6 +38,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get common_delete => 'Delete';
+ @override
+ String get common_deleteAll => 'Delete All';
+
@override
String get common_close => 'Close';
@@ -108,6 +111,132 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Connect over TCP';
+
+ @override
+ String get tcpHostLabel => 'IP Address';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Enter endpoint and connect';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Connecting to $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'IP address is required.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Port must be between 1 and 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'TCP transport is not supported on this platform.';
+
+ @override
+ String get tcpErrorTimedOut => 'TCP connection timed out.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'TCP connection failed: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Connect over USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Choose a detected serial device and connect directly to your MeshCore node.';
+
+ @override
+ String get usbScreenStatus => 'Select a USB device';
+
+ @override
+ String get usbScreenNote =>
+ 'USB serial is active on supported Android devices and desktop platforms.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'No USB devices found. Plug one in and refresh.';
+
+ @override
+ String get usbErrorPermissionDenied => 'USB permission was denied.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'The selected USB device is no longer available.';
+
+ @override
+ String get usbErrorInvalidPort => 'Select a valid USB device.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Another USB connection request is already in progress.';
+
+ @override
+ String get usbErrorNotConnected => 'No USB device is connected.';
+
+ @override
+ String get usbErrorOpenFailed => 'Failed to open the selected USB device.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Failed to connect to the selected USB device.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'USB serial is not supported on this platform.';
+
+ @override
+ String get usbErrorAlreadyActive => 'A USB connection is already active.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'No USB device was selected.';
+
+ @override
+ String get usbErrorPortClosed => 'The USB connection is not open.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Connection timed out. Make sure the device has USB Companion firmware.';
+
+ @override
+ String get usbFallbackDeviceName => 'Web Serial Device';
+
+ @override
+ String get usbStatus_notConnected => 'Select a USB device';
+
+ @override
+ String get usbStatus_connecting => 'Connecting to USB device...';
+
+ @override
+ String get usbStatus_searching => 'Searching for USB devices...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'USB connection failed: $error';
+ }
+
@override
String get scanner_scanning => 'Scanning for devices...';
@@ -142,6 +271,23 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get scanner_scan => 'Scan';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth is off';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Please turn on Bluetooth to scan for devices';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome Browser Required';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'This web application requires Google Chrome or a Chromium-based browser for Bluetooth support.';
+
+ @override
+ String get scanner_enableBluetooth => 'Enable Bluetooth';
+
@override
String get device_quickSwitch => 'Quick switch';
@@ -222,6 +368,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
+ @override
+ String get settings_contactSettings => 'Contact Settings';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Settings for how contacts are added.';
+
@override
String get settings_privacyMode => 'Privacy Mode';
@@ -239,6 +392,48 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Privacy mode disabled';
+ @override
+ String get settings_privacy => 'Privacy Settings';
+
+ @override
+ String get settings_privacySubtitle => 'Control what information is shared.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Choose what information your device shares with others.';
+
+ @override
+ String get settings_denyAll => 'Deny all';
+
+ @override
+ String get settings_allowByContact => 'Allow by contact flags';
+
+ @override
+ String get settings_allowAll => 'Allow all';
+
+ @override
+ String get settings_telemetryBaseMode => 'Telemetry Base Mode';
+
+ @override
+ String get settings_telemetryLocationMode => 'Telemetry Location Mode';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Telemetry Environment Mode';
+
+ @override
+ String get settings_advertLocation => 'Advert Location';
+
+ @override
+ String get settings_advertLocationSubtitle => 'Include location in advert.';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Telemetry mode updated';
+
@override
String get settings_actions => 'Actions';
@@ -308,6 +503,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get settings_aboutDescription =>
'An open-source Flutter client for MeshCore LoRa mesh networking devices.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS elevation data: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Name';
@@ -332,15 +531,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get settings_presets => 'Presets';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frequency (MHz)';
@@ -369,10 +559,15 @@ class AppLocalizationsEn extends AppLocalizations {
String get settings_txPowerInvalid => 'Invalid TX power (0-22 dBm)';
@override
- String get settings_longRange => 'Long Range';
+ String get settings_clientRepeat => 'Off-Grid Repeat';
@override
- String get settings_fastSpeed => 'Fast Speed';
+ String get settings_clientRepeatSubtitle =>
+ 'Allow this device to repeat mesh packets for others';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Off-grid repeat requires 433, 869, or 918 MHz frequency';
@override
String settings_error(String message) {
@@ -442,6 +637,19 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Русский';
+
+ @override
+ String get appSettings_languageUk => 'Українська';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Enable Message Tracing';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Show detailed routing and timing metadata for messages';
+
@override
String get appSettings_notifications => 'Notifications';
@@ -518,6 +726,48 @@ class AppLocalizationsEn extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Auto route rotation disabled';
+ @override
+ String get appSettings_maxRouteWeight => 'Max Route Weight';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Maximum weight a path can accumulate from successful deliveries';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Initial Route Weight';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Starting weight for newly discovered paths';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Success Weight Increment';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Weight added to a path after successful delivery';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Failure Weight Decrement';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Weight removed from a path after failed delivery';
+
+ @override
+ String get appSettings_maxMessageRetries => 'Max Message Retries';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Number of retry attempts before marking a message as failed';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Battery';
@@ -602,6 +852,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Offline Map Cache';
+ @override
+ String get appSettings_unitsTitle => 'Units';
+
+ @override
+ String get appSettings_unitsMetric => 'Metric (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperial (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'No area selected';
@@ -638,7 +897,35 @@ class AppLocalizationsEn extends AppLocalizations {
'Contacts will appear when devices advertise';
@override
- String get contacts_searchContacts => 'Search contacts...';
+ String get contacts_unread => 'Unread';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Search Contacts...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Search $number$str Contacts...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Search $number$str Favorites...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Search $number$str Users...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Search $number$str Repeaters...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Search $number$str Room servers...';
+ }
@override
String get contacts_noUnreadContacts => 'No unread contacts';
@@ -686,6 +973,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Group name is required';
+ @override
+ String get contacts_groupNameReserved => 'This group name is reserved';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Group \"$name\" already exists';
@@ -701,29 +991,63 @@ class AppLocalizationsEn extends AppLocalizations {
String get contacts_noMembers => 'No members';
@override
- String get contacts_lastSeenNow => 'Last seen now';
+ String get contacts_lastSeenNow => 'recently';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Last seen $minutes mins ago';
+ return '~ $minutes min.';
}
@override
- String get contacts_lastSeenHourAgo => 'Last seen 1 hour ago';
+ String get contacts_lastSeenHourAgo => '~ 1 hour';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return 'Last seen $hours hours ago';
+ return '~ $hours hours';
}
@override
- String get contacts_lastSeenDayAgo => 'Last seen 1 day ago';
+ String get contacts_lastSeenDayAgo => '~ 1 day';
@override
String contacts_lastSeenDaysAgo(int days) {
- return 'Last seen $days days ago';
+ return '~ $days days';
}
+ @override
+ String get contact_info => 'Contact Info';
+
+ @override
+ String get contact_settings => 'Contact Settings';
+
+ @override
+ String get contact_telemetry => 'Telemetry';
+
+ @override
+ String get contact_lastSeen => 'Last seen';
+
+ @override
+ String get contact_clearChat => 'Clear Chat';
+
+ @override
+ String get contact_teleBase => 'Telemetry Base';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Allow sharing battery level and basic telemetry';
+
+ @override
+ String get contact_teleLoc => 'Telemetry Location';
+
+ @override
+ String get contact_teleLocSubtitle => 'Allow sharing location data';
+
+ @override
+ String get contact_teleEnv => 'Telemetry Environment';
+
+ @override
+ String get contact_teleEnvSubtitle => 'Allow sharing environment sensor data';
+
@override
String get channels_title => 'Channels';
@@ -762,6 +1086,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get channels_editChannel => 'Edit channel';
+ @override
+ String get channels_muteChannel => 'Mute channel';
+
+ @override
+ String get channels_unmuteChannel => 'Unmute channel';
+
@override
String get channels_deleteChannel => 'Delete channel';
@@ -770,6 +1100,11 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Delete \"$name\"? This cannot be undone.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Failed to delete channel \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Channel \"$name\" deleted';
@@ -878,6 +1213,14 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get chat_noMessages => 'No messages yet';
+ @override
+ String get chat_sendMessage => 'Send message';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Send a message to $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Send a message to get started';
@@ -897,11 +1240,6 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get chat_location => 'Location';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Send a message to $contactName';
- }
-
@override
String get chat_typeMessage => 'Type a message...';
@@ -1053,6 +1391,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get chat_pathManagement => 'Path Management';
+ @override
+ String get chat_ShowAllPaths => 'Show all paths';
+
@override
String get chat_routingMode => 'Routing mode';
@@ -1186,9 +1527,33 @@ class AppLocalizationsEn extends AppLocalizations {
return 'Unread: $count';
}
+ @override
+ String get chat_openLink => 'Open Link?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Do you want to open this link in your browser?';
+
+ @override
+ String get chat_open => 'Open';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Could not open link: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Invalid link format';
+
@override
String get map_title => 'Node Map';
+ @override
+ String get map_lineOfSight => 'Line of Sight';
+
+ @override
+ String get map_losScreenTitle => 'Line of Sight';
+
@override
String get map_noNodesWithLocation => 'No nodes with location data';
@@ -1246,6 +1611,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Share marker here';
+ @override
+ String get map_setAsMyLocation => 'Set as my location';
+
@override
String get map_pinLabel => 'Pin label';
@@ -1291,6 +1659,9 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get map_otherNodes => 'Other Nodes';
+ @override
+ String get map_showOverlaps => 'Repeater Key Overlaps';
+
@override
String get map_keyPrefix => 'Key Prefix';
@@ -1306,6 +1677,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Show shared markers';
+ @override
+ String get map_showGuessedLocations => 'Show guessed node locations';
+
+ @override
+ String get map_showDiscoveryContacts => 'Show Discovery Contacts';
+
+ @override
+ String get map_guessedLocation => 'Guessed location';
+
@override
String get map_lastSeenTime => 'Last Seen Time';
@@ -1318,6 +1698,21 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get map_manageRepeater => 'Manage Repeater';
+ @override
+ String get map_tapToAdd => 'Tap on nodes to add them to the path.';
+
+ @override
+ String get map_runTrace => 'Run path trace';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Return back on the same path.';
+
+ @override
+ String get map_removeLast => 'Remove Last';
+
+ @override
+ String get map_pathTraceCancelled => 'Path trace cancelled.';
+
@override
String get mapCache_title => 'Offline Map Cache';
@@ -1476,11 +1871,11 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get login_repeaterDescription =>
- 'Enter the repeater password to access settings and status.';
+ 'Enter the repeater password for guest or admin access.';
@override
String get login_roomDescription =>
- 'Enter the room password to access settings and status.';
+ 'Enter the room password for guest or admin access.';
@override
String get login_routing => 'Routing';
@@ -1584,9 +1979,18 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get room_management => 'Room Server Management';
+ @override
+ String get repeater_guest => 'Repeater Information';
+
+ @override
+ String get room_guest => 'Room Server Information';
+
@override
String get repeater_managementTools => 'Management Tools';
+ @override
+ String get repeater_guestTools => 'Guest Tools';
+
@override
String get repeater_status => 'Status';
@@ -1608,10 +2012,10 @@ class AppLocalizationsEn extends AppLocalizations {
String get repeater_cliSubtitle => 'Send commands to the repeater';
@override
- String get repeater_neighbours => 'Neighbors';
+ String get repeater_neighbors => 'Neighbors';
@override
- String get repeater_neighboursSubtitle => 'View zero hop neighbors.';
+ String get repeater_neighborsSubtitle => 'View zero hop neighbors.';
@override
String get repeater_settings => 'Settings';
@@ -1619,6 +2023,13 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Configure repeater parameters';
+ @override
+ String get repeater_clockSyncAfterLogin => 'Clock sync after login';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatically send \"clock sync\" after a successful login';
+
@override
String get repeater_statusTitle => 'Repeater Status';
@@ -1987,6 +2398,12 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Clock';
+ @override
+ String get repeater_cliQuickClockSync => 'Clock Sync';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Discover Neighbors';
+
@override
String get repeater_cliHelpAdvert => 'Sends an advertisement packet';
@@ -2287,10 +2704,10 @@ class AppLocalizationsEn extends AppLocalizations {
}
@override
- String get neighbors_receivedData => 'Received Neighbours Data';
+ String get neighbors_receivedData => 'Received Neighbors Data';
@override
- String get neighbors_requestTimedOut => 'Neighbours request timed out.';
+ String get neighbors_requestTimedOut => 'Neighbors request timed out.';
@override
String neighbors_errorLoading(String error) {
@@ -2298,10 +2715,10 @@ class AppLocalizationsEn extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Repeaters Neighbours';
+ String get neighbors_repeatersNeighbors => 'Repeaters Neighbors';
@override
- String get neighbors_noData => 'No neighbours data available.';
+ String get neighbors_noData => 'No neighbors data available.';
@override
String neighbors_unknownContact(String pubkey) {
@@ -2604,6 +3021,15 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get listFilter_all => 'All';
+ @override
+ String get listFilter_favorites => 'Favorites';
+
+ @override
+ String get listFilter_addToFavorites => 'Add to favorites';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Remove from favorites';
+
@override
String get listFilter_users => 'Users';
@@ -2618,4 +3044,593 @@ class AppLocalizationsEn extends AppLocalizations {
@override
String get listFilter_newGroup => 'New group';
+
+ @override
+ String get pathTrace_you => 'You';
+
+ @override
+ String get pathTrace_failed => 'Path trace failed.';
+
+ @override
+ String get pathTrace_notAvailable => 'Path trace not available.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Refresh Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'One or more of the hops is missing a location!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Clear path.';
+
+ @override
+ String get losSelectStartEnd => 'Select start and end nodes for LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Line-of-sight check failed: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Clear all points';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Run LOS to view elevation profile';
+
+ @override
+ String get losMenuTitle => 'LOS Menu';
+
+ @override
+ String get losMenuSubtitle => 'Tap nodes or long-press map for custom points';
+
+ @override
+ String get losShowDisplayNodes => 'Show display nodes';
+
+ @override
+ String get losCustomPoints => 'Custom points';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Custom $index';
+ }
+
+ @override
+ String get losPointA => 'Point A';
+
+ @override
+ String get losPointB => 'Point B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenna A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenna B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Run LOS';
+
+ @override
+ String get losNoElevationData => 'No elevation data';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, clear LOS, min clearance $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blocked by $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: checking...';
+
+ @override
+ String get losStatusNoData => 'LOS: no data';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total clear, $blocked blocked, $unknown unknown';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Elevation data unavailable for one or more samples.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Invalid points/elevation data for LOS calculation.';
+
+ @override
+ String get losRenameCustomPoint => 'Rename custom point';
+
+ @override
+ String get losPointName => 'Point name';
+
+ @override
+ String get losShowPanelTooltip => 'Show LOS panel';
+
+ @override
+ String get losHidePanelTooltip => 'Hide LOS panel';
+
+ @override
+ String get losElevationAttribution =>
+ 'Elevation data: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Radio horizon';
+
+ @override
+ String get losLegendLosBeam => 'LOS beam';
+
+ @override
+ String get losLegendTerrain => 'Terrain';
+
+ @override
+ String get losFrequencyLabel => 'Frequency';
+
+ @override
+ String get losFrequencyInfoTooltip => 'View calculation details';
+
+ @override
+ String get losFrequencyDialogTitle => 'Radio horizon calculation';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Starting from k=$baselineK at $baselineFreq MHz, the calculation adjusts the k-factor for the current $frequencyMHz MHz band, which defines the curved radio horizon cap.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Path Trace';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Path trace to repeater';
+
+ @override
+ String get contacts_repeaterPing => 'Ping repeater';
+
+ @override
+ String get contacts_roomPathTrace => 'Path trace to room server';
+
+ @override
+ String get contacts_roomPing => 'Ping room server';
+
+ @override
+ String get contacts_chatTraceRoute => 'Path trace route';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Trace route to $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Clipboard is empty.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Invalid contact data';
+
+ @override
+ String get contacts_contactImported => 'Contact has been imported.';
+
+ @override
+ String get contacts_contactImportFailed => 'Failed to import contact.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Zero Hop Advert';
+
+ @override
+ String get contacts_floodAdvert => 'Flood Advert';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Copy Advert to Clipboard';
+
+ @override
+ String get contacts_addContactFromClipboard => 'Add Contact from Clipboard';
+
+ @override
+ String get contacts_ShareContact => 'Copy contact to Clipboard';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Share contact by advert';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Sent contact by advert.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed => 'Failed to send contact.';
+
+ @override
+ String get contacts_contactAdvertCopied => 'Advert copied to Clipboard.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Copying advert to Clipboard failed.';
+
+ @override
+ String get notification_activityTitle => 'MeshCore Activity';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messages',
+ one: 'message',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'channel messages',
+ one: 'channel message',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'new nodes',
+ one: 'new node',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'New $contactType discovered';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Received new message';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Export repeaters / room server to GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exports repeaters / roomserver with a location to GPX file.';
+
+ @override
+ String get settings_gpxExportContacts => 'Export companions to GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exports companions with a location to GPX file.';
+
+ @override
+ String get settings_gpxExportAll => 'Export all contacts to GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exports all contacts with a location to GPX file.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Successfully exported GPX file.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'No contacts to export.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Not supported on your device/OS';
+
+ @override
+ String get settings_gpxExportError => 'There was an error when exporting.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Repeater & room server locations';
+
+ @override
+ String get settings_gpxExportChat => 'Companion locations';
+
+ @override
+ String get settings_gpxExportAllContacts => 'All contacts locations';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Map data exported from meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open GPX map data export';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Nearby Repeaters';
+
+ @override
+ String get snrIndicator_lastSeen => 'Last seen';
+
+ @override
+ String get contactsSettings_title => 'Contacts settings';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatic Discovery';
+
+ @override
+ String get contactsSettings_otherTitle => 'Other contact related settings';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle => 'Auto-add users';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Allow the companion to automatically add discovered users.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle => 'Auto-add repeaters';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Allow the companion to automatically add discovered repeaters.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Auto-add room servers';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Allow the companion to automatically add discovered room servers.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle => 'Auto-add sensors';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Allow the companion to automatically add discovered sensors.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Overwrite Oldest';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'When the contact list is full, the oldest non-favorited contact will be replaced.';
+
+ @override
+ String get discoveredContacts_Title => 'Discovered Contacts';
+
+ @override
+ String get discoveredContacts_noMatching => 'No matching contacts';
+
+ @override
+ String get discoveredContacts_searchHint => 'Search discovered contacts';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contact added';
+
+ @override
+ String get discoveredContacts_addContact => 'Add Contact';
+
+ @override
+ String get discoveredContacts_copyContact => 'Copy Contact to clipboard';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Delete Discovered Contact';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Delete All Discovered Contacts';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Are you sure you want to delete all discovered contacts?';
+
+ @override
+ String get chat_sendCooldown => 'Please wait a moment before sending again.';
+
+ @override
+ String get appSettings_jumpToOldestUnread => 'Jump to oldest unread';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'When opening a chat with unread messages, scroll to the first unread instead of the latest.';
+
+ @override
+ String get appSettings_languageHu => 'Hungarian';
+
+ @override
+ String get appSettings_languageJa => 'Japanese';
+
+ @override
+ String get appSettings_languageKo => 'Korean';
+
+ @override
+ String get radioStats_tooltip => 'Radio & mesh stats';
+
+ @override
+ String get radioStats_screenTitle => 'Radio stats';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Connect to a device to view radio statistics.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Radio statistics require companion firmware v8 or newer.';
+
+ @override
+ String get radioStats_waiting => 'Waiting for data…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Noise floor: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Last RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Last SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX airtime (total): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX airtime (total): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Noise floor (dBm) over recent samples.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Noise floor: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Fetching radio stats…';
+
+ @override
+ String get radioStats_settingsTile => 'Radio stats';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Noise floor, RSSI, SNR, and airtime';
+
+ @override
+ String get translation_title => 'Translation';
+
+ @override
+ String get translation_enableTitle => 'Enable translation';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Translate incoming messages and allow pre-send translation.';
+
+ @override
+ String get translation_composerTitle => 'Translate before sending';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Controls the default state of the composer translation icon.';
+
+ @override
+ String get translation_targetLanguage => 'Target language';
+
+ @override
+ String get translation_useAppLanguage => 'Use app language';
+
+ @override
+ String get translation_downloadedModelLabel => 'Downloaded model';
+
+ @override
+ String get translation_presetModelLabel => 'Preset Hugging Face model';
+
+ @override
+ String get translation_manualUrlLabel => 'Manual model URL';
+
+ @override
+ String get translation_downloadModel => 'Download model';
+
+ @override
+ String get translation_downloading => 'Downloading...';
+
+ @override
+ String get translation_working => 'Working...';
+
+ @override
+ String get translation_stop => 'Stop';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Merging downloaded chunks into final file...';
+
+ @override
+ String get translation_downloadedModels => 'Downloaded models';
+
+ @override
+ String get translation_deleteModel => 'Delete model';
+
+ @override
+ String get translation_modelDownloaded => 'Translation model downloaded.';
+
+ @override
+ String get translation_downloadStopped => 'Download stopped.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Download failed: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Enter a model URL first.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Show PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Hide PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth Pairing PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Enter PIN for $deviceName (leave blank if none).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Message translation';
+
+ @override
+ String get translation_translateBeforeSending => 'Translate before sending';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Messages will be translated before send.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Send messages in the original typed language.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Translate to $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Translation options';
+
+ @override
+ String get translation_systemLanguage => 'System language';
}
diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart
index 029ed11..a127012 100644
--- a/lib/l10n/app_localizations_es.dart
+++ b/lib/l10n/app_localizations_es.dart
@@ -38,6 +38,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get common_delete => 'Eliminar';
+ @override
+ String get common_deleteAll => 'Eliminar todo';
+
@override
String get common_close => 'Cerrar';
@@ -108,6 +111,135 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Establecer conexión a través de TCP';
+
+ @override
+ String get tcpHostLabel => 'Dirección IP';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Puerto';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Ingrese la dirección final y conecte.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Conectándose a $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Se requiere la dirección IP.';
+
+ @override
+ String get tcpErrorPortInvalid => 'El puerto debe estar entre 1 y 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'El protocolo de transporte TCP no está soportado en esta plataforma.';
+
+ @override
+ String get tcpErrorTimedOut => 'La conexión TCP ha caducado.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Error en la conexión TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Conecte mediante USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Seleccione el dispositivo de serie detectado y conéctelo directamente a su nodo MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Seleccione un dispositivo USB';
+
+ @override
+ String get usbScreenNote =>
+ 'La comunicación serial a través de USB está activa en dispositivos Android compatibles y en plataformas de escritorio.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'No se encontraron dispositivos USB. Conecte uno y vuelva a intentar.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Se denegó el permiso de acceso a través de USB.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'El dispositivo USB seleccionado ya no está disponible.';
+
+ @override
+ String get usbErrorInvalidPort => 'Seleccione un dispositivo USB válido.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Ya se ha iniciado una solicitud de conexión USB adicional.';
+
+ @override
+ String get usbErrorNotConnected => 'No hay ningún dispositivo USB conectado.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'No se pudo abrir el dispositivo USB seleccionado.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'No se pudo conectar con el dispositivo USB seleccionado.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'La comunicación serial a través de USB no está soportada en esta plataforma.';
+
+ @override
+ String get usbErrorAlreadyActive => 'La conexión USB ya está activa.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'No se ha seleccionado ningún dispositivo USB.';
+
+ @override
+ String get usbErrorPortClosed => 'La conexión USB no está activa.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'La conexión ha caducado. Asegúrese de que el dispositivo tenga el firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName => 'Dispositivo de serie web';
+
+ @override
+ String get usbStatus_notConnected => 'Seleccione un dispositivo USB';
+
+ @override
+ String get usbStatus_connecting => 'Conectándose al dispositivo USB...';
+
+ @override
+ String get usbStatus_searching => 'Buscando dispositivos USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Error al conectar mediante USB: $error';
+ }
+
@override
String get scanner_scanning => 'Escaneando dispositivos...';
@@ -143,6 +275,23 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get scanner_scan => 'Escanea';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth está desactivado.';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Por favor, active el Bluetooth para escanear dispositivos.';
+
+ @override
+ String get scanner_chromeRequired => 'Navegador Chrome requerido';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Esta aplicación web requiere Google Chrome o un navegador basado en Chromium para el soporte de Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Habilitar Bluetooth';
+
@override
String get device_quickSwitch => 'Cambiar rápidamente';
@@ -223,6 +372,13 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get settings_longitude => 'Longitud';
+ @override
+ String get settings_contactSettings => 'Configuración de contacto';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Configuración de cómo se agregan los contactos.';
+
@override
String get settings_privacyMode => 'Modo Privacidad';
@@ -240,6 +396,51 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Modo de privacidad desactivado';
+ @override
+ String get settings_privacy => 'Configuración de privacidad';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Controlar qué información se comparte.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Elige qué información comparte tu dispositivo con otros.';
+
+ @override
+ String get settings_denyAll => 'Denegar todo';
+
+ @override
+ String get settings_allowByContact => 'Permitir por banderas de contacto';
+
+ @override
+ String get settings_allowAll => 'Permitir todo';
+
+ @override
+ String get settings_telemetryBaseMode => 'Modo base de telemetría';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Modo de ubicación de telemetría';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Modo de entorno de telemetría';
+
+ @override
+ String get settings_advertLocation => 'Ubicación de anuncio';
+
+ @override
+ String get settings_advertLocationSubtitle => 'Incluir ubicación en anuncio';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Modo de telemetría actualizado';
+
@override
String get settings_actions => 'Acciones';
@@ -313,6 +514,10 @@ class AppLocalizationsEs extends AppLocalizations {
String get settings_aboutDescription =>
'Un cliente de código abierto de Flutter para dispositivos de red mesh LoRa de MeshCore.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Datos de elevación LOS: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Nombre';
@@ -337,15 +542,6 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get settings_presets => 'Preajustes';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frecuencia (MHz)';
@@ -374,10 +570,15 @@ class AppLocalizationsEs extends AppLocalizations {
String get settings_txPowerInvalid => 'Potencia de TX inválida (0-22 dBm)';
@override
- String get settings_longRange => 'Largo Alcance';
+ String get settings_clientRepeat => 'Repetir sin conexión';
@override
- String get settings_fastSpeed => 'Velocidad Rápida';
+ String get settings_clientRepeatSubtitle =>
+ 'Permita que este dispositivo repita los paquetes de red para otros usuarios.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Para la comunicación fuera de la red, se requiere una frecuencia de 433, 869 o 918 MHz.';
@override
String settings_error(String message) {
@@ -447,6 +648,20 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Ruso';
+
+ @override
+ String get appSettings_languageUk => 'Ucraniano';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Habilitar seguimiento de mensajes';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Mostrar metadatos detallados de enrutamiento y tiempo para los mensajes';
+
@override
String get appSettings_notifications => 'Notificaciones';
@@ -524,6 +739,49 @@ class AppLocalizationsEs extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Rotación de ruta automática desactivada';
+ @override
+ String get appSettings_maxRouteWeight => 'Peso máximo permitido para la ruta';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Peso máximo que una ruta puede acumular gracias a entregas exitosas.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Peso inicial de la ruta';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Peso inicial para rutas recién descubiertas';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Incremento de peso para el éxito';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Peso añadido a una ruta después de una entrega exitosa.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Reducción del peso asociado al fallo';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Peso retirado de un camino después de un intento de entrega fallido.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Número máximo de reintentos de envío de mensajes';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Número de intentos de reintento antes de marcar un mensaje como fallido.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batería';
@@ -608,6 +866,15 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Caché de Mapa Offline';
+ @override
+ String get appSettings_unitsTitle => 'Unidades';
+
+ @override
+ String get appSettings_unitsMetric => 'Métrico (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperial (pies/millas)';
+
@override
String get appSettings_noAreaSelected => 'No se ha seleccionado ningún área';
@@ -645,7 +912,35 @@ class AppLocalizationsEs extends AppLocalizations {
'Los contactos aparecerán cuando los dispositivos anuncien.';
@override
- String get contacts_searchContacts => 'Buscar contactos...';
+ String get contacts_unread => 'No leído';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Buscar contactos...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Buscar contactos...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Buscar $number$str Favoritos...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Buscar $number$str Usuarios...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Buscar $number$str Repetidores...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Buscar $number$str servidores de sala...';
+ }
@override
String get contacts_noUnreadContacts => 'No contactos sin leer';
@@ -694,6 +989,10 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'El nombre del grupo es obligatorio';
+ @override
+ String get contacts_groupNameReserved =>
+ 'Este nombre de grupo está reservado';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'El grupo \"$name\" ya existe';
@@ -714,25 +1013,61 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Última vez visto hace $minutes minutos.';
+ return '~ $minutes min.';
}
@override
- String get contacts_lastSeenHourAgo => 'Última vez que se vio hace 1 hora';
+ String get contacts_lastSeenHourAgo => '~ 1 hora';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return 'Última vez visto hace $hours horas.';
+ return '~ $hours horas';
}
@override
- String get contacts_lastSeenDayAgo => 'Última vez que se vio hace 1 día';
+ String get contacts_lastSeenDayAgo => '~ 1 día';
@override
String contacts_lastSeenDaysAgo(int days) {
- return 'Última vez visto hace $days días.';
+ return '~ $days días';
}
+ @override
+ String get contact_info => 'Información de contacto';
+
+ @override
+ String get contact_settings => 'Configuración de contacto';
+
+ @override
+ String get contact_telemetry => 'Telemetría';
+
+ @override
+ String get contact_lastSeen => 'Visto por última vez';
+
+ @override
+ String get contact_clearChat => 'Borrar chat';
+
+ @override
+ String get contact_teleBase => 'Base de Telemetría';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Permitir el intercambio de nivel de batería y telemetría básica';
+
+ @override
+ String get contact_teleLoc => 'Ubicación de telemetría';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Permitir el intercambio de datos de ubicación';
+
+ @override
+ String get contact_teleEnv => 'Entorno de Telemetría';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Permitir el intercambio de datos de sensores de entorno';
+
@override
String get channels_title => 'Canales';
@@ -771,6 +1106,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get channels_editChannel => 'Editar canal';
+ @override
+ String get channels_muteChannel => 'Silenciar canal';
+
+ @override
+ String get channels_unmuteChannel => 'Activar canal';
+
@override
String get channels_deleteChannel => 'Eliminar canal';
@@ -779,6 +1120,11 @@ class AppLocalizationsEs extends AppLocalizations {
return 'Eliminar \"$name\"? Esto no se puede deshacer.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'No se pudo eliminar el canal \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Canal \"$name\" eliminado';
@@ -892,6 +1238,14 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get chat_noMessages => 'Aún no hay mensajes';
+ @override
+ String get chat_sendMessage => 'Enviar mensaje';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Enviar un mensaje a $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Enviar un mensaje para comenzar';
@@ -911,11 +1265,6 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get chat_location => 'Ubicación';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Enviar un mensaje a $contactName';
- }
-
@override
String get chat_typeMessage => 'Escribe un mensaje...';
@@ -1067,6 +1416,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get chat_pathManagement => 'Gestión de Rutas';
+ @override
+ String get chat_ShowAllPaths => 'Mostrar todos los caminos';
+
@override
String get chat_routingMode => 'Modo de enrutamiento';
@@ -1204,9 +1556,33 @@ class AppLocalizationsEs extends AppLocalizations {
return 'Sin leer: $count';
}
+ @override
+ String get chat_openLink => '¿Abrir enlace?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ '¿Quiere abrir este enlace en su navegador?';
+
+ @override
+ String get chat_open => 'Abrir';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'No se pudo abrir el enlace: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Formato de enlace no válido';
+
@override
String get map_title => 'Mapa de Nodos';
+ @override
+ String get map_lineOfSight => 'Línea de visión';
+
+ @override
+ String get map_losScreenTitle => 'Línea de visión';
+
@override
String get map_noNodesWithLocation => 'No hay nodos con datos de ubicación';
@@ -1264,6 +1640,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Compartir marcador aquí';
+ @override
+ String get map_setAsMyLocation => 'Establecer mi ubicación';
+
@override
String get map_pinLabel => 'Etiqueta de marcador';
@@ -1309,6 +1688,9 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get map_otherNodes => 'Otros Nodos';
+ @override
+ String get map_showOverlaps => 'Superposiciones de tecla repetidora';
+
@override
String get map_keyPrefix => 'Prefijo de clave';
@@ -1324,6 +1706,16 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Mostrar marcadores compartidos';
+ @override
+ String get map_showGuessedLocations =>
+ 'Mostrar las ubicaciones estimadas de los nodos.';
+
+ @override
+ String get map_showDiscoveryContacts => 'Mostrar Contactos de Descubrimiento';
+
+ @override
+ String get map_guessedLocation => 'Ubicación estimada';
+
@override
String get map_lastSeenTime => 'Última vez que se vio';
@@ -1336,6 +1728,21 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get map_manageRepeater => 'Gestionar Repetidor';
+ @override
+ String get map_tapToAdd => 'Pulse en los nodos para agregarlos al camino.';
+
+ @override
+ String get map_runTrace => 'Ejecutar Rastreo de Ruta';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Volver atrás por el mismo camino.';
+
+ @override
+ String get map_removeLast => 'Eliminar último';
+
+ @override
+ String get map_pathTraceCancelled => 'Rastreo de ruta cancelado.';
+
@override
String get mapCache_title => 'Caché de Mapa Offline';
@@ -1608,9 +2015,18 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get room_management => 'Administración del Servidor de Habitación';
+ @override
+ String get repeater_guest => 'Información sobre repetidores';
+
+ @override
+ String get room_guest => 'Información del servidor';
+
@override
String get repeater_managementTools => 'Herramientas de Gestión';
+ @override
+ String get repeater_guestTools => 'Herramientas para invitados';
+
@override
String get repeater_status => 'Estado';
@@ -1632,10 +2048,10 @@ class AppLocalizationsEs extends AppLocalizations {
String get repeater_cliSubtitle => 'Enviar comandos al repetidor';
@override
- String get repeater_neighbours => 'Vecinos';
+ String get repeater_neighbors => 'Vecinos';
@override
- String get repeater_neighboursSubtitle => 'Ver vecinos de salto cero.';
+ String get repeater_neighborsSubtitle => 'Ver vecinos de salto cero.';
@override
String get repeater_settings => 'Configuración';
@@ -1643,6 +2059,14 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Configurar parámetros del repetidor';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Sincronización del reloj después de iniciar sesión';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Enviar automáticamente la función de \"sincronización de reloj\" después de un inicio de sesión exitoso.';
+
@override
String get repeater_statusTitle => 'Estado del Repetidor';
@@ -2019,6 +2443,12 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Reloj';
+ @override
+ String get repeater_cliQuickClockSync => 'Sincronización del reloj';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Descubrir Vecinos';
+
@override
String get repeater_cliHelpAdvert => 'Envía un paquete de publicidad';
@@ -2334,7 +2764,7 @@ class AppLocalizationsEs extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Repetidores Vecinos';
+ String get neighbors_repeatersNeighbors => 'Repetidores Vecinos';
@override
String get neighbors_noData => 'No hay datos de vecinos disponibles.';
@@ -2565,32 +2995,32 @@ class AppLocalizationsEs extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Regenerar Contraseña Secreta';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regenerar la clave secreta para \"$name\"? Todos los miembros deberán escanear el nuevo código QR para seguir comunicándose.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Regenerar';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Código secreto regenerado para \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Actualizar Contraseña';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Confidencialidad actualizada para \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Escanear el nuevo código QR para actualizar el secreto de \"$name\"';
}
@override
@@ -2643,6 +3073,15 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get listFilter_all => 'Todas';
+ @override
+ String get listFilter_favorites => 'Favoritos';
+
+ @override
+ String get listFilter_addToFavorites => 'Añadir a favoritos';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Eliminar de las favoritas';
+
@override
String get listFilter_users => 'Usuarios';
@@ -2657,4 +3096,614 @@ class AppLocalizationsEs extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nuevo grupo';
+
+ @override
+ String get pathTrace_you => 'Tú';
+
+ @override
+ String get pathTrace_failed => 'El trazado de ruta falló.';
+
+ @override
+ String get pathTrace_notAvailable => 'El trazado de ruta no está disponible.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Actualizar Path Trace';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Uno o más de los lúpulos carecen de una ubicación';
+
+ @override
+ String get pathTrace_clearTooltip => 'Borrar ruta';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Seleccione los nodos de inicio y fin para LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Error en la comprobación de la línea de visión: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Borrar todos los puntos';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Ejecute LOS para ver el perfil de elevación';
+
+ @override
+ String get losMenuTitle => 'Menú LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Toque nodos o mantenga presionado el mapa para puntos personalizados';
+
+ @override
+ String get losShowDisplayNodes => 'Mostrar nodos de visualización';
+
+ @override
+ String get losCustomPoints => 'Puntos personalizados';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Personalizado $index';
+ }
+
+ @override
+ String get losPointA => 'Punto A';
+
+ @override
+ String get losPointB => 'Punto B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antena A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antena B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Ejecutar LOS';
+
+ @override
+ String get losNoElevationData => 'Sin datos de elevación';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, despejar LOS, autorización mínima $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, bloqueado por $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: comprobando...';
+
+ @override
+ String get losStatusNoData => 'LOS: sin datos';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total claro, $blocked bloqueado, $unknown desconocido';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Datos de elevación no disponibles para una o más muestras.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Datos de puntos/elevación no válidos para el cálculo de LOS.';
+
+ @override
+ String get losRenameCustomPoint =>
+ 'Cambiar el nombre del punto personalizado';
+
+ @override
+ String get losPointName => 'Nombre del punto';
+
+ @override
+ String get losShowPanelTooltip => 'Mostrar panel LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Ocultar panel LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Datos de elevación: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Horizonte radioeléctrico';
+
+ @override
+ String get losLegendLosBeam => 'Línea de visión';
+
+ @override
+ String get losLegendTerrain => 'Terreno';
+
+ @override
+ String get losFrequencyLabel => 'Frecuencia';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Ver detalles del cálculo';
+
+ @override
+ String get losFrequencyDialogTitle => 'Cálculo del horizonte radioeléctrico';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'A partir de k=$baselineK en $baselineFreq MHz, el cálculo ajusta el factor k para la banda actual de $frequencyMHz MHz, que define el límite curvo del horizonte de radio.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Rastreo de caminos';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Rastrear ruta al repetidor';
+
+ @override
+ String get contacts_repeaterPing => 'Pingar repetidor';
+
+ @override
+ String get contacts_roomPathTrace =>
+ 'Rastreo de ruta al servidor de la habitación';
+
+ @override
+ String get contacts_roomPing => 'Pingar servidor de sala';
+
+ @override
+ String get contacts_chatTraceRoute => 'Ruta de trazado';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Rastrear ruta a $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'El portapapeles está vacío.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Datos de contacto no válidos';
+
+ @override
+ String get contacts_contactImported => 'El contacto ha sido importado.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Contacto no se importó correctamente.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Anuncio de Zero Hop';
+
+ @override
+ String get contacts_floodAdvert => 'Anuncio de inundación';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Copiar anuncio al portapapeles';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Agregar contacto desde el portapapeles';
+
+ @override
+ String get contacts_ShareContact => 'Copiar contacto al Portapapeles';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Compartir contacto por anuncio';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Envió contacto por anuncio.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'No se pudo enviar el contacto.';
+
+ @override
+ String get contacts_contactAdvertCopied => 'Anuncio copiado al Portapapeles.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Copiar anuncio al Portapapeles ha fallado.';
+
+ @override
+ String get notification_activityTitle => 'Actividad de MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'mensajes',
+ one: 'mensaje',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'mensajes de canal',
+ one: 'mensaje de canal',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nuevos nodos',
+ one: 'nuevo nodo',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nuevo $contactType descubierto';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nuevo mensaje recibido';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exportar repetidores / servidor de sala a GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exporta repetidores o roomserver con una ubicación a un archivo GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Exportar compañeros a GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exporta compañeros con una ubicación a archivo GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Exportar todos los contactos a GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exporta todos los contactos con una ubicación a un archivo GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Archivo GPX exportado con éxito.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'No hay contactos para exportar.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'No compatible con tu dispositivo/SO';
+
+ @override
+ String get settings_gpxExportError => 'Hubo un error al exportar.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Ubicaciones del servidor de repetidor y sala';
+
+ @override
+ String get settings_gpxExportChat => 'Ubicaciones de compañero';
+
+ @override
+ String get settings_gpxExportAllContacts =>
+ 'Todas las ubicaciones de contactos';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Datos del mapa exportados desde meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open exportación de datos de mapa GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Repetidores cercanos';
+
+ @override
+ String get snrIndicator_lastSeen => 'Visto por última vez';
+
+ @override
+ String get contactsSettings_title => 'Configuración de contactos';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Detección automática';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Otras configuraciones relacionadas con el contacto';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Agregar usuarios automáticamente';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Permitir que el compañero agregue automáticamente a los usuarios descubiertos.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Agregar repetidores automáticamente';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Permitir que el compañero agregue automáticamente los repetidores descubiertos.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Agregar automáticamente servidores de sala';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Permitir que el compañero agregue automáticamente los servidores de salas descubiertos.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Agregar sensores automáticamente';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Permitir que el compañero agregue automáticamente los sensores descubiertos.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle =>
+ 'Sobreescribir el más antiguo';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Cuando la lista de contactos esté llena, se reemplazará el contacto no favorito más antiguo.';
+
+ @override
+ String get discoveredContacts_Title => 'Contactos descubiertos';
+
+ @override
+ String get discoveredContacts_noMatching =>
+ 'No se encontraron contactos coincidentes';
+
+ @override
+ String get discoveredContacts_searchHint => 'Buscar contactos descubiertos';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contacto agregado';
+
+ @override
+ String get discoveredContacts_addContact => 'Agregar contacto';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Copiar contacto al portapapeles';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Eliminar contacto';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Eliminar Todos los Contactos Descubiertos';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ '¿Está seguro de que desea eliminar todos los contactos descubiertos!';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Por favor, espere un momento antes de reenviar.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Salta a los mensajes más antiguos sin leer';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Cuando abras una conversación con mensajes sin leer, desplázate hacia el primer mensaje sin leer en lugar del más reciente.';
+
+ @override
+ String get appSettings_languageHu => 'Húngaro';
+
+ @override
+ String get appSettings_languageJa => 'Japonés';
+
+ @override
+ String get appSettings_languageKo => 'Coreano';
+
+ @override
+ String get radioStats_tooltip => 'Estadísticas de radio y malla';
+
+ @override
+ String get radioStats_screenTitle => 'Estadísticas de radio';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Conéctese a un dispositivo para visualizar estadísticas de radio.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Las estadísticas de radio requieren un firmware compatible v8 o posterior.';
+
+ @override
+ String get radioStats_waiting => 'Esperando datos…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Nivel de ruido: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Último RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Último SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Tiempo de emisión en Texas (total): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Tiempo de transmisión de RX (total): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Nivel de ruido (dBm) en muestras recientes.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Nivel de ruido: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Obteniendo estadísticas de la radio…';
+
+ @override
+ String get radioStats_settingsTile => 'Estadísticas de radio';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Nivel de ruido, RSSI, SNR y tiempo de transmisión';
+
+ @override
+ String get translation_title => 'Traducción';
+
+ @override
+ String get translation_enableTitle => 'Habilitar la traducción';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Traducir los mensajes entrantes y permitir la traducción previa al envío.';
+
+ @override
+ String get translation_composerTitle => 'Traducir antes de enviar';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Controla el estado predeterminado del icono de traducción del compositor.';
+
+ @override
+ String get translation_targetLanguage => 'Idioma de destino';
+
+ @override
+ String get translation_useAppLanguage =>
+ 'Utilizar el idioma de la aplicación';
+
+ @override
+ String get translation_downloadedModelLabel => 'Modelo descargado';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Modelo predefinido de Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'URL del modelo manual';
+
+ @override
+ String get translation_downloadModel => 'Descargar el modelo';
+
+ @override
+ String get translation_downloading => 'Descargando...';
+
+ @override
+ String get translation_working => 'Trabajando...';
+
+ @override
+ String get translation_stop => '¡Detente!';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Combinando los fragmentos descargados en el archivo final...';
+
+ @override
+ String get translation_downloadedModels => 'Modelos descargados';
+
+ @override
+ String get translation_deleteModel => 'Eliminar modelo';
+
+ @override
+ String get translation_modelDownloaded => 'Modelo de traducción descargado.';
+
+ @override
+ String get translation_downloadStopped => 'La descarga se ha detenido.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'No se pudo descargar: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Primero, introduzca la URL del modelo.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Mostrar código PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Ocultar PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle =>
+ 'PIN para emparejar dispositivos Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Introduzca el código PIN para $deviceName (deje en blanco si no hay ninguno).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Traducción del mensaje';
+
+ @override
+ String get translation_translateBeforeSending => 'Traducir antes de enviar';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Los mensajes serán traducidos antes de ser enviados.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Envía mensajes utilizando el lenguaje escrito original.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Traducir a $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Opciones de traducción';
+
+ @override
+ String get translation_systemLanguage => 'Idioma del sistema';
}
diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart
index 1dce57f..a006391 100644
--- a/lib/l10n/app_localizations_fr.dart
+++ b/lib/l10n/app_localizations_fr.dart
@@ -38,6 +38,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get common_delete => 'Supprimer';
+ @override
+ String get common_deleteAll => 'Supprimer tout';
+
@override
String get common_close => 'Fermer';
@@ -108,6 +111,137 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Établir une connexion via TCP';
+
+ @override
+ String get tcpHostLabel => 'Adresse IP';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected =>
+ 'Entrez l\'adresse de destination et connectez-vous.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Connexion à $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Une adresse IP est obligatoire.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'La taille du port doit être comprise entre 1 et 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Le protocole TCP n\'est pas pris en charge sur cette plateforme.';
+
+ @override
+ String get tcpErrorTimedOut => 'La connexion TCP a expiré.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Échec de la connexion TCP : $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Connectez via USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Sélectionnez un périphérique série détecté et connectez-vous directement à votre nœud MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Sélectionnez un périphérique USB';
+
+ @override
+ String get usbScreenNote =>
+ 'La communication série USB est active sur les appareils Android et les plateformes de bureau compatibles.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Aucun périphérique USB n\'a été trouvé. Veuillez en brancher un et rafraîchir la page.';
+
+ @override
+ String get usbErrorPermissionDenied => 'L\'accès via USB a été refusé.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Le périphérique USB sélectionné n\'est plus disponible.';
+
+ @override
+ String get usbErrorInvalidPort => 'Sélectionnez un périphérique USB valide.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Une autre demande de connexion USB est déjà en cours.';
+
+ @override
+ String get usbErrorNotConnected => 'Aucun appareil USB n\'est connecté.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Impossible d\'ouvrir l\'appareil USB sélectionné.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Impossible de se connecter à l\'appareil USB sélectionné.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'La communication série USB n\'est pas prise en charge sur cette plateforme.';
+
+ @override
+ String get usbErrorAlreadyActive => 'Une connexion USB est déjà établie.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Aucun appareil USB n\'a été sélectionné.';
+
+ @override
+ String get usbErrorPortClosed => 'La connexion USB n\'est pas établie.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'La connexion a expiré. Assurez-vous que l\'appareil dispose du firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Dispositif de communication série sur le Web';
+
+ @override
+ String get usbStatus_notConnected => 'Sélectionnez un périphérique USB';
+
+ @override
+ String get usbStatus_connecting => 'Connexion au périphérique USB...';
+
+ @override
+ String get usbStatus_searching => 'Recherche de périphériques USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Échec de la connexion USB : $error';
+ }
+
@override
String get scanner_scanning => 'Recherche de périphériques...';
@@ -143,6 +277,23 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get scanner_scan => 'Scanner';
+ @override
+ String get scanner_bluetoothOff => 'Le Bluetooth est désactivé.';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Veuillez activer le Bluetooth pour rechercher des appareils.';
+
+ @override
+ String get scanner_chromeRequired => 'Navigateur Chrome requis';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Cette application web nécessite Google Chrome ou un navigateur basé sur Chromium pour le support Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Activer le Bluetooth';
+
@override
String get device_quickSwitch => 'Basculement rapide';
@@ -204,18 +355,19 @@ class AppLocalizationsFr extends AppLocalizations {
String get settings_locationInvalid => 'Latitude ou longitude invalide.';
@override
- String get settings_locationGPSEnable => 'Habilita GPS';
+ String get settings_locationGPSEnable => 'Activer le GPS';
@override
String get settings_locationGPSEnableSubtitle =>
- 'Habilita la actualización automática de la ubicación mediante GPS.';
+ 'Activer la mise à jour automatique de la position via GPS';
@override
- String get settings_locationIntervalSec => 'Intervalo pour GPS (Segundos)';
+ String get settings_locationIntervalSec =>
+ 'Intervalle de mise-à-jour du GPS (Secondes)';
@override
String get settings_locationIntervalInvalid =>
- 'El intervalo debe ser de al menos 60 segundos y menor que 86400 segundos.';
+ 'L\'intervalle doit être compris entre 60 et 86400 secondes.';
@override
String get settings_latitude => 'Latitude';
@@ -223,6 +375,13 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
+ @override
+ String get settings_contactSettings => 'Paramètres de contact';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Paramètres pour l\'ajout de contacts';
+
@override
String get settings_privacyMode => 'Mode de confidentialité';
@@ -241,6 +400,52 @@ class AppLocalizationsFr extends AppLocalizations {
String get settings_privacyModeDisabled =>
'Mode de confidentialité désactivé';
+ @override
+ String get settings_privacy => 'Paramètres de confidentialité';
+
+ @override
+ String get settings_privacySubtitle => 'Contrôlez les informations partagées';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Choisissez les informations que votre appareil partage avec les autres.';
+
+ @override
+ String get settings_denyAll => 'Refuser tout';
+
+ @override
+ String get settings_allowByContact => 'Autoriser par drapeaux de contact';
+
+ @override
+ String get settings_allowAll => 'Autoriser tout';
+
+ @override
+ String get settings_telemetryBaseMode => 'Mode de base Télémétrie';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Mode d\'emplacement de télémétrie';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Mode d\'environnement de télémétrie';
+
+ @override
+ String get settings_advertLocation => 'Emplacement de l\'annonce';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Inclure l\'emplacement dans l\'annonce';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs : $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated =>
+ 'Le mode télémétrie a été mis à jour';
+
@override
String get settings_actions => 'Actions';
@@ -272,7 +477,7 @@ class AppLocalizationsFr extends AppLocalizations {
'Recharger la liste des contacts depuis l\'appareil';
@override
- String get settings_rebootDevice => 'Réinitialiser l\'appareil';
+ String get settings_rebootDevice => 'Redémarrer l\'appareil';
@override
String get settings_rebootDeviceSubtitle => 'Redémarrer l\'appareil MeshCore';
@@ -313,6 +518,10 @@ class AppLocalizationsFr extends AppLocalizations {
String get settings_aboutDescription =>
'Un client Flutter open source pour les appareils de réseau mesh MeshCore LoRa.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Données d\'élévation LOS : Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Nom';
@@ -337,15 +546,6 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get settings_presets => 'Préréglages';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Fréquence (MHz)';
@@ -359,10 +559,10 @@ class AppLocalizationsFr extends AppLocalizations {
String get settings_bandwidth => 'Bande passante';
@override
- String get settings_spreadingFactor => 'Facteur de répartition';
+ String get settings_spreadingFactor => 'Facteur de répartition (SF)';
@override
- String get settings_codingRate => 'Taux de codage';
+ String get settings_codingRate => 'Taux de codage (CR)';
@override
String get settings_txPower => 'TX Puissance (dBm)';
@@ -374,10 +574,15 @@ class AppLocalizationsFr extends AppLocalizations {
String get settings_txPowerInvalid => 'Puissance TX invalide (0-22 dBm)';
@override
- String get settings_longRange => 'Portée Longue';
+ String get settings_clientRepeat => 'Répétition hors réseau';
@override
- String get settings_fastSpeed => 'Vitesse Rapide';
+ String get settings_clientRepeatSubtitle =>
+ 'Permettez à cet appareil de répéter les paquets de données pour les autres.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Pour les transmissions hors réseau, il est nécessaire d\'utiliser les fréquences de 433, 869 ou 918 MHz.';
@override
String settings_error(String message) {
@@ -447,6 +652,20 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Russe';
+
+ @override
+ String get appSettings_languageUk => 'Ukrainien';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Activer le traçage des messages';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Afficher les métadonnées détaillées de routage et de synchronisation des messages';
+
@override
String get appSettings_notifications => 'Notifications';
@@ -525,6 +744,50 @@ class AppLocalizationsFr extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Rotation de l\'itinéraire automatique désactivée';
+ @override
+ String get appSettings_maxRouteWeight =>
+ 'Poids maximal autorisé pour le trajet';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Poids maximal qu\'un itinéraire peut accumuler grâce à des livraisons réussies.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Poids initial de l\'itinéraire';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Poids de départ pour les nouveaux chemins découverts';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Augmentation du poids de réussite';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Poids ajouté à un itinéraire après une livraison réussie.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Réduction du poids de pénalité';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Poids retiré d\'un itinéraire après une tentative de livraison infructueuse.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Nombre maximal de tentatives de récupération de messages';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Nombre de tentatives de relance avant de marquer un message comme ayant échoué.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batterie';
@@ -553,11 +816,11 @@ class AppLocalizationsFr extends AppLocalizations {
String get appSettings_mapDisplay => 'Affichage de la carte';
@override
- String get appSettings_showRepeaters => 'Afficher les répétiteurs';
+ String get appSettings_showRepeaters => 'Afficher les répéteurs';
@override
String get appSettings_showRepeatersSubtitle =>
- 'Afficher les nœuds répétiteurs sur la carte';
+ 'Afficher les nœuds répéteurs sur la carte';
@override
String get appSettings_showChatNodes => 'Afficher les nœuds de discussion';
@@ -609,6 +872,15 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Cache de Carte Hors Ligne';
+ @override
+ String get appSettings_unitsTitle => 'Unités';
+
+ @override
+ String get appSettings_unitsMetric => 'Métrique (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Impérial (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'Aucune zone sélectionnée';
@@ -647,7 +919,35 @@ class AppLocalizationsFr extends AppLocalizations {
'Les contacts apparaîtront lorsque les appareils font leur annonce.';
@override
- String get contacts_searchContacts => 'Rechercher des contacts...';
+ String get contacts_unread => 'Non lu';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Rechercher des contacts...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Rechercher des contacts...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Rechercher $number$str Favoris...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Rechercher $number$str utilisateurs...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Rechercher $number$str Répéteurs...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Rechercher $number$str room server...';
+ }
@override
String get contacts_noUnreadContacts => 'Aucun contact non lu';
@@ -664,13 +964,13 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get contacts_manageRepeater => 'Gérer le répétiteur';
+ String get contacts_manageRepeater => 'Gérer le répéteur';
@override
- String get contacts_manageRoom => 'Gestionar Servidor de Habitación';
+ String get contacts_manageRoom => 'Gérer le Room Server';
@override
- String get contacts_roomLogin => 'Connexion Salle';
+ String get contacts_roomLogin => 'Connexion Room Server';
@override
String get contacts_openChat => 'Ouverture du Chat';
@@ -687,7 +987,7 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get contacts_newGroup => 'Nouvelle Groupe';
+ String get contacts_newGroup => 'Nouveau Groupe';
@override
String get contacts_groupName => 'Nom du groupe';
@@ -695,6 +995,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Le nom du groupe est obligatoire.';
+ @override
+ String get contacts_groupNameReserved => 'Ce nom de groupe est réservé';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Le groupe \"$name\" existe déjà.';
@@ -711,29 +1014,65 @@ class AppLocalizationsFr extends AppLocalizations {
String get contacts_noMembers => 'Aucun membre';
@override
- String get contacts_lastSeenNow => 'Dernière fois vu maintenant';
+ String get contacts_lastSeenNow => 'Vu maintenant';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Dernière fois vu il y a $minutes minutes.';
+ return '~ $minutes min.';
}
@override
- String get contacts_lastSeenHourAgo => 'Dernière fois vu il y a 1 heure.';
+ String get contacts_lastSeenHourAgo => '~ 1 heure';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return 'Dernière fois vu il y a $hours heures.';
+ return '~ $hours heures';
}
@override
- String get contacts_lastSeenDayAgo => 'Dernière fois vu il y a 1 jour';
+ String get contacts_lastSeenDayAgo => '~ 1 jour';
@override
String contacts_lastSeenDaysAgo(int days) {
- return 'Dernière activité il y a $days jours';
+ return '~ $days jours';
}
+ @override
+ String get contact_info => 'Informations de contact';
+
+ @override
+ String get contact_settings => 'Paramètres de contact';
+
+ @override
+ String get contact_telemetry => 'Télémétrie';
+
+ @override
+ String get contact_lastSeen => 'Dernière fois vu';
+
+ @override
+ String get contact_clearChat => 'Effacer la conversation';
+
+ @override
+ String get contact_teleBase => 'Base de télémétrie';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Autoriser le partage du niveau de batterie et de la télémétrie de base';
+
+ @override
+ String get contact_teleLoc => 'Emplacement de télémétrie';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Autoriser le partage des données de localisation';
+
+ @override
+ String get contact_teleEnv => 'Environnement Télémétrie';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Autoriser le partage des données des capteurs d\'environnement';
+
@override
String get channels_title => 'Canaux';
@@ -772,6 +1111,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get channels_editChannel => 'Modifier le canal';
+ @override
+ String get channels_muteChannel => 'Désactiver les notifications du canal';
+
+ @override
+ String get channels_unmuteChannel => 'Réactiver les notifications du canal';
+
@override
String get channels_deleteChannel => 'Supprimer le canal';
@@ -780,6 +1125,11 @@ class AppLocalizationsFr extends AppLocalizations {
return 'Supprimer $name? Cela ne peut pas être annulé.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Échec de la suppression de la chaîne \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Le canal \"$name\" a été supprimé';
@@ -845,7 +1195,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get channels_sortAZ => 'A à Z';
@override
- String get channels_sortLatestMessages => 'Dernières messages';
+ String get channels_sortLatestMessages => 'Derniers messages';
@override
String get channels_sortUnread => 'Non lu';
@@ -879,7 +1229,7 @@ class AppLocalizationsFr extends AppLocalizations {
'N\'importe qui peut rejoindre les canaux #hashtag.';
@override
- String get channels_scanQrCode => 'Scanner un code QR';
+ String get channels_scanQrCode => 'Scanner un QR code';
@override
String get channels_scanQrCodeComingSoon => 'Bientôt disponible';
@@ -888,11 +1238,19 @@ class AppLocalizationsFr extends AppLocalizations {
String get channels_enterHashtag => 'Entrez le hashtag';
@override
- String get channels_hashtagHint => 'ex. #équipe';
+ String get channels_hashtagHint => 'ex. #equipe';
@override
String get chat_noMessages => 'Aucun message pour le moment.';
+ @override
+ String get chat_sendMessage => 'Envoyer un message';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Envoyer un message à $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Envoyer un message pour commencer';
@@ -912,11 +1270,6 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get chat_location => 'Emplacement';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Envoyer un message à $contactName';
- }
-
@override
String get chat_typeMessage => 'Saisir un message...';
@@ -936,7 +1289,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String chat_retryCount(int current, int max) {
- return 'Réessayer $current/$max';
+ return 'Essai $current/$max';
}
@override
@@ -1069,6 +1422,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get chat_pathManagement => 'Gestion des chemins';
+ @override
+ String get chat_ShowAllPaths => 'Afficher tous les chemins';
+
@override
String get chat_routingMode => 'Mode de routage';
@@ -1087,18 +1443,18 @@ class AppLocalizationsFr extends AppLocalizations {
'L\'historique du chemin est plein. Supprimez les entrées pour en ajouter de nouvelles.';
@override
- String get chat_hopSingular => 'Sautez';
+ String get chat_hopSingular => 'saut';
@override
- String get chat_hopPlural => 'sautez';
+ String get chat_hopPlural => 'sauts';
@override
String chat_hopsCount(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
- other: 'hops',
- one: 'hop',
+ other: 'sauts',
+ one: 'saut',
);
return '$count $_temp0';
}
@@ -1136,7 +1492,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get chat_floodModeSubtitle =>
- 'Utiliser le commutateur de routage dans la barre d\'application';
+ 'Désactive l\'apprentissage du chemin (à éviter). Utiliser le commutateur de routage dans la barre d\'application pour rebasculer en mode auto par la suite.';
@override
String get chat_floodModeEnabled =>
@@ -1209,9 +1565,33 @@ class AppLocalizationsFr extends AppLocalizations {
return 'Non lu : $count';
}
+ @override
+ String get chat_openLink => 'Ouvrir le lien ?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Voulez-vous ouvrir ce lien dans votre navigateur ?';
+
+ @override
+ String get chat_open => 'Ouvrir';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Impossible d\'ouvrir le lien : $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Format de lien invalide';
+
@override
String get map_title => 'Carte des nœuds';
+ @override
+ String get map_lineOfSight => 'Ligne de vue';
+
+ @override
+ String get map_losScreenTitle => 'Ligne de vue';
+
@override
String get map_noNodesWithLocation =>
'Aucun nœud avec des données de localisation';
@@ -1234,10 +1614,10 @@ class AppLocalizationsFr extends AppLocalizations {
String get map_chat => 'Chat';
@override
- String get map_repeater => 'Répétiteur';
+ String get map_repeater => 'Répéteur';
@override
- String get map_room => 'Salle';
+ String get map_room => 'Room Server';
@override
String get map_sensor => 'Capteur';
@@ -1270,6 +1650,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Partager le marqueur ici';
+ @override
+ String get map_setAsMyLocation => 'Définir comme ma localisation';
+
@override
String get map_pinLabel => 'Étiquete de repin';
@@ -1315,6 +1698,9 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get map_otherNodes => 'Autres nœuds';
+ @override
+ String get map_showOverlaps => 'Chevauchement de la touche répétitive';
+
@override
String get map_keyPrefix => 'Préfixe clé';
@@ -1330,6 +1716,16 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Afficher les marqueurs partagés';
+ @override
+ String get map_showGuessedLocations =>
+ 'Afficher les emplacements des nœuds estimés';
+
+ @override
+ String get map_showDiscoveryContacts => 'Afficher les contacts de découverte';
+
+ @override
+ String get map_guessedLocation => 'Lieu deviné';
+
@override
String get map_lastSeenTime => 'Dernière fois vu';
@@ -1337,10 +1733,26 @@ class AppLocalizationsFr extends AppLocalizations {
String get map_sharedPin => 'Clé partagée';
@override
- String get map_joinRoom => 'Rejoindre la salle';
+ String get map_joinRoom => 'Rejoindre le room server';
@override
- String get map_manageRepeater => 'Gérer le répétiteur';
+ String get map_manageRepeater => 'Gérer le répéteur';
+
+ @override
+ String get map_tapToAdd =>
+ 'Appuyez sur les nœuds pour les ajouter au chemin.';
+
+ @override
+ String get map_runTrace => 'Exécuter la traçage de chemin';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Revenir sur le même chemin.';
+
+ @override
+ String get map_removeLast => 'Supprimer le dernier';
+
+ @override
+ String get map_pathTraceCancelled => 'Traçage de chemin annulé';
@override
String get mapCache_title => 'Cache de Carte Hors Ligne';
@@ -1371,7 +1783,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
- return 'Tiles mis en cache ($downloaded) ($failed ratés)';
+ return 'Tuiles mis en cache ($downloaded) ($failed ratés)';
}
@override
@@ -1425,11 +1837,11 @@ class AppLocalizationsFr extends AppLocalizations {
String east,
String west,
) {
- return 'N $north, S $south, E $east, W $west';
+ return 'N $north, S $south, E $east, O $west';
}
@override
- String get time_justNow => 'Il y a tout juste maintenant';
+ String get time_justNow => 'Maintenant';
@override
String time_minutesAgo(int minutes) {
@@ -1484,10 +1896,10 @@ class AppLocalizationsFr extends AppLocalizations {
'Êtes-vous sûr de vouloir vous déconnecter de cet appareil ?';
@override
- String get login_repeaterLogin => 'Connexion au répétiteur';
+ String get login_repeaterLogin => 'Connexion au répéteur';
@override
- String get login_roomLogin => 'Connexion Salle';
+ String get login_roomLogin => 'Connexion Room Server';
@override
String get login_password => 'Mot de passe';
@@ -1504,7 +1916,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get login_repeaterDescription =>
- 'Entrez le mot de passe du répétiteur pour accéder aux paramètres et à l\'état.';
+ 'Entrez le mot de passe du répéteur pour accéder aux paramètres et à l\'état.';
@override
String get login_roomDescription =>
@@ -1590,7 +2002,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get path_noRepeatersFound =>
- 'Aucun répéteur ou serveur de salle n\'a été trouvé.';
+ 'Aucun répéteur ou room server n\'a été trouvé.';
@override
String get path_customPathsRequire =>
@@ -1609,20 +2021,29 @@ class AppLocalizationsFr extends AppLocalizations {
String get path_setPath => 'Définir le chemin';
@override
- String get repeater_management => 'Gestion des répétiteurs';
+ String get repeater_management => 'Gestion des répéteurs';
@override
- String get room_management => 'Administración del Servidor de Habitación';
+ String get room_management => 'Administrattion Room Server';
+
+ @override
+ String get repeater_guest => 'Informations sur les répéteurs';
+
+ @override
+ String get room_guest => 'Informations sur le serveur';
@override
String get repeater_managementTools => 'Outils de Gestion';
+ @override
+ String get repeater_guestTools => 'Outils pour les invités';
+
@override
String get repeater_status => 'État';
@override
String get repeater_statusSubtitle =>
- 'Afficher l\'état, les statistiques et les voisins du répétiteur';
+ 'Afficher l\'état, les statistiques et les voisins du répéteur';
@override
String get repeater_telemetry => 'Télémetrie';
@@ -1635,24 +2056,31 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_cli => 'CLI';
@override
- String get repeater_cliSubtitle => 'Envoyer des commandes au répétiteur';
+ String get repeater_cliSubtitle => 'Envoyer des commandes au répéteur';
@override
- String get repeater_neighbours => 'Voisins';
+ String get repeater_neighbors => 'Voisins';
@override
- String get repeater_neighboursSubtitle =>
- 'Afficher les voisins de saut nuls.';
+ String get repeater_neighborsSubtitle => 'Afficher les voisins de saut nuls.';
@override
String get repeater_settings => 'Paramètres';
@override
String get repeater_settingsSubtitle =>
- 'Configurer les paramètres du répétiteur';
+ 'Configurer les paramètres du répéteur';
@override
- String get repeater_statusTitle => 'État du répétiteur';
+ String get repeater_clockSyncAfterLogin =>
+ 'Synchronisation de l\'horloge après la connexion';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Envoyer automatiquement une notification \"synchronisation de l\'heure\" après une connexion réussie.';
+
+ @override
+ String get repeater_statusTitle => 'État du répéteur';
@override
String get repeater_routingMode => 'Mode de routage';
@@ -1725,7 +2153,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_received => 'Reçu';
@override
- String get repeater_duplicates => 'Dupliques';
+ String get repeater_duplicates => 'Doublons';
@override
String repeater_daysHoursMinsSecs(
@@ -1758,16 +2186,16 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get repeater_settingsTitle => 'Paramètres du répétiteur';
+ String get repeater_settingsTitle => 'Paramètres du répéteur';
@override
String get repeater_basicSettings => 'Paramètres de base';
@override
- String get repeater_repeaterName => 'Nom du répétiteur';
+ String get repeater_repeaterName => 'Nom du répéteur';
@override
- String get repeater_repeaterNameHelper => 'Afficher le nom de ce répétiteur';
+ String get repeater_repeaterNameHelper => 'Afficher le nom de ce répéteur';
@override
String get repeater_adminPassword => 'Mot de passe Administrateur';
@@ -1801,10 +2229,10 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_bandwidth => 'Bande passante';
@override
- String get repeater_spreadingFactor => 'Facteur de répartition';
+ String get repeater_spreadingFactor => 'Facteur de répartition (SF)';
@override
- String get repeater_codingRate => 'Taux de codage';
+ String get repeater_codingRate => 'Taux de codage (CR)';
@override
String get repeater_locationSettings => 'Paramètres de localisation';
@@ -1827,11 +2255,11 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_features => 'Fonctionnalités';
@override
- String get repeater_packetForwarding => 'Transfert de paquets';
+ String get repeater_packetForwarding => 'Mode répéteur';
@override
String get repeater_packetForwardingSubtitle =>
- 'Activer le répétiteur pour transmettre des paquets';
+ 'Activer le répéteur pour transmettre des paquets';
@override
String get repeater_guestAccess => 'Accès Invité';
@@ -1873,18 +2301,18 @@ class AppLocalizationsFr extends AppLocalizations {
'Intervalle d\'annonces cryptées';
@override
- String get repeater_dangerZone => 'Zone d\'alerte';
+ String get repeater_dangerZone => 'Zone dangereuse';
@override
String get repeater_rebootRepeater => 'Redémarrer Répéteur';
@override
String get repeater_rebootRepeaterSubtitle =>
- 'Réinitialiser l\'appareil répétiteur';
+ 'Réinitialiser l\'appareil répéteur';
@override
String get repeater_rebootRepeaterConfirm =>
- 'Êtes-vous sûr de vouloir redémarrer ce répétiteur ?';
+ 'Êtes-vous sûr de vouloir redémarrer ce répéteur ?';
@override
String get repeater_regenerateIdentityKey => 'Ré générer la clé d\'identité';
@@ -1895,18 +2323,18 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get repeater_regenerateIdentityKeyConfirm =>
- 'Cela générera une nouvelle identité pour le répétiteur. Continuer ?';
+ 'Cela générera une nouvelle identité pour le répéteur. Continuer ?';
@override
String get repeater_eraseFileSystem => 'Supprimer le système de fichiers';
@override
String get repeater_eraseFileSystemSubtitle =>
- 'Formater le système de fichiers du répétiteur';
+ 'Formater le système de fichiers du répéteur';
@override
String get repeater_eraseFileSystemConfirm =>
- 'AVERTISSEMENT : Cela effacera toutes les données du répétiteur. Cela ne peut pas être annulé !';
+ 'AVERTISSEMENT : Cela effacera toutes les données du répéteur. Cela ne peut pas être annulé !';
@override
String get repeater_eraseSerialOnly =>
@@ -1974,7 +2402,7 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get repeater_cliTitle => 'Répétiteur CLI';
+ String get repeater_cliTitle => 'Répéteur CLI';
@override
String get repeater_debugNextCommand => 'Déboguer Prochaine Commande';
@@ -2034,6 +2462,12 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Horloge';
+ @override
+ String get repeater_cliQuickClockSync => 'Synchronisation de l\'horloge';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Découvrir les voisins';
+
@override
String get repeater_cliHelpAdvert => 'Envoie un paquet d\'annonce';
@@ -2066,11 +2500,11 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get repeater_cliHelpSetRepeat =>
- 'Active ou désactive le rôle du répétiteur pour ce nœud.';
+ 'Active ou désactive le rôle du répéteur pour ce nœud.';
@override
String get repeater_cliHelpSetAllowReadOnly =>
- '(Serveur de pièce) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)';
+ '(Room server) Si \"activé\", alors un mot de passe vide permettra la connexion, mais ne permettra pas de publier dans la pièce. (lecture seule uniquement)';
@override
String get repeater_cliHelpSetFloodMax =>
@@ -2090,7 +2524,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get repeater_cliHelpSetAdvertInterval =>
- 'Définit l\'intervalle du minuteur pour envoyer un paquet d\'annonce local (sans relais). Définir sur 0 pour désactiver.';
+ 'Définit l\'intervalle entre chaque émission d\'une annonce locale (sans relais). Définir sur 0 pour désactiver.';
@override
String get repeater_cliHelpSetFloodAdvertInterval =>
@@ -2176,7 +2610,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get repeater_cliHelpNeighbors =>
- 'Affiche une liste d\'autres nœuds répétiteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4';
+ 'Affiche une liste d\'autres nœuds répéteurs entendus via des annonces sans relais. Chaque ligne est id-préfixe-hexadécimal:timestamp:snr-fois-4';
@override
String get repeater_cliHelpNeighborRemove =>
@@ -2264,12 +2698,11 @@ class AppLocalizationsFr extends AppLocalizations {
String get repeater_logging => 'Journalisation';
@override
- String get repeater_neighborsRepeaterOnly =>
- 'Voisins (Uniquement répétiteur)';
+ String get repeater_neighborsRepeaterOnly => 'Voisins (Uniquement répéteur)';
@override
String get repeater_regionManagementRepeaterOnly =>
- 'Gestion des régions (uniquement pour le répétiteur)';
+ 'Gestion des régions (uniquement pour le répéteur)';
@override
String get repeater_regionNote =>
@@ -2348,7 +2781,7 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Répéteurs Voisins';
+ String get neighbors_repeatersNeighbors => 'Répéteurs Voisins';
@override
String get neighbors_noData =>
@@ -2374,7 +2807,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get channelPath_otherObservedPaths => 'Autres chemins observés';
@override
- String get channelPath_repeaterHops => 'Sauts du répétiteur';
+ String get channelPath_repeaterHops => 'Sauts du répéteur';
@override
String get channelPath_noHopDetails =>
@@ -2442,7 +2875,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get channelPath_noRepeaterLocations =>
- 'Aucune position de répétiteur disponible pour ce chemin.';
+ 'Aucune position de répéteur disponible pour ce chemin.';
@override
String channelPath_primaryPath(int index) {
@@ -2489,11 +2922,11 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get community_scanQr => 'Scanner la communauté QR';
+ String get community_scanQr => 'Scanner un QR code de communauté';
@override
String get community_scanInstructions =>
- 'Pointez l\'appareil photo vers un code QR communautaire.';
+ 'Pointez l\'appareil photo vers un QR code de communauté.';
@override
String get community_showQr => 'Afficher le QR Code';
@@ -2533,7 +2966,7 @@ class AppLocalizationsFr extends AppLocalizations {
'Les canaux hashtag de la communauté ne sont accessibles qu\'aux membres de la communauté';
@override
- String get community_invalidQrCode => 'Code QR de communauté non valide';
+ String get community_invalidQrCode => 'QR code de communauté non valide';
@override
String get community_alreadyMember => 'Déjà membre';
@@ -2557,7 +2990,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get community_scanOrCreate =>
- 'Scanner un code QR ou créer une communauté pour commencer';
+ 'Scanner un QR code ou créer une communauté pour commencer';
@override
String get community_manageCommunities => 'Gérer les Communautés';
@@ -2581,32 +3014,32 @@ class AppLocalizationsFr extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Régénérer le secret';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Régénérer la clé secrète pour \"$name\" ? Tous les membres devront scanner le nouveau QR code pour continuer à communiquer.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Régénérer';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Mot de passe secret régénéré pour \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Mettre à jour le secret';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Modification secrète mise à jour pour \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Scanner le nouveau QR code pour mettre à jour le mot de passe pour \"$name\"';
}
@override
@@ -2614,7 +3047,7 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get community_addHashtagChannelDesc =>
- 'Ajouter un canal hachage pour cette communauté';
+ 'Ajouter un canal hashtag pour cette communauté';
@override
String get community_selectCommunity => 'Sélectionner Communauté';
@@ -2645,7 +3078,7 @@ class AppLocalizationsFr extends AppLocalizations {
String get listFilter_sortBy => 'Trier par';
@override
- String get listFilter_latestMessages => 'Dernières messages';
+ String get listFilter_latestMessages => 'Derniers messages';
@override
String get listFilter_heardRecently => 'Écoute récemment';
@@ -2659,6 +3092,15 @@ class AppLocalizationsFr extends AppLocalizations {
@override
String get listFilter_all => 'Tout';
+ @override
+ String get listFilter_favorites => 'Préférences';
+
+ @override
+ String get listFilter_addToFavorites => 'Ajouter à mes favoris';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Supprimer des favoris';
+
@override
String get listFilter_users => 'Utilisateurs';
@@ -2666,11 +3108,626 @@ class AppLocalizationsFr extends AppLocalizations {
String get listFilter_repeaters => 'Répéteurs';
@override
- String get listFilter_roomServers => 'Serveurs de pièce';
+ String get listFilter_roomServers => 'Room servers';
@override
String get listFilter_unreadOnly => 'Messages non lus seulement';
@override
- String get listFilter_newGroup => 'Nouvelle groupe';
+ String get listFilter_newGroup => 'Nouveau groupe';
+
+ @override
+ String get pathTrace_you => 'Vous';
+
+ @override
+ String get pathTrace_failed => 'Traçage du chemin échoué.';
+
+ @override
+ String get pathTrace_notAvailable => 'Tracé de chemin non disponible.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Actualiser Path Trace';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Un ou plusieurs des sauts manquent d\'une localisation !';
+
+ @override
+ String get pathTrace_clearTooltip => 'Effacer le chemin';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Sélectionnez les nœuds de début et de fin pour LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Échec de la vérification de la ligne de vue : $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Effacer tous les points';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Exécutez LOS pour afficher le profil d\'altitude';
+
+ @override
+ String get losMenuTitle => 'Menu LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Appuyez sur les nœuds ou appuyez longuement sur la carte pour des points personnalisés';
+
+ @override
+ String get losShowDisplayNodes => 'Afficher les nœuds d\'affichage';
+
+ @override
+ String get losCustomPoints => 'Points personnalisés';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Personnalisé $index';
+ }
+
+ @override
+ String get losPointA => 'Point A';
+
+ @override
+ String get losPointB => 'Point B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenne A : $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenne B : $value $unit';
+ }
+
+ @override
+ String get losRun => 'Exécuter la LOS';
+
+ @override
+ String get losNoElevationData => 'Aucune donnée d\'altitude';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, LOS clair, clairance minimale $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, bloqué par $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS : vérification...';
+
+ @override
+ String get losStatusNoData => 'LOS : aucune donnée';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS : $clear/$total clair, $blocked bloqué, $unknown inconnu';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Données d\'altitude indisponibles pour un ou plusieurs échantillons.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Données de points/d\'altitude non valides pour le calcul de la LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Renommer le point personnalisé';
+
+ @override
+ String get losPointName => 'Nom du point';
+
+ @override
+ String get losShowPanelTooltip => 'Afficher le panneau LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Masquer le panneau LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Données d’altitude : Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Horizon radio';
+
+ @override
+ String get losLegendLosBeam => 'Ligne de visée';
+
+ @override
+ String get losLegendTerrain => 'Terrain';
+
+ @override
+ String get losFrequencyLabel => 'Fréquence';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Voir les détails du calcul';
+
+ @override
+ String get losFrequencyDialogTitle => 'Calcul de l’horizon radio';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'À partir de k=$baselineK à $baselineFreq MHz, le calcul ajuste le facteur k pour la bande actuelle de $frequencyMHz MHz, ce qui définit la limite incurvée de l\'horizon radio.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Traçage de chemin';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Tracer le chemin vers le répéteur';
+
+ @override
+ String get contacts_repeaterPing => 'Pinguer le répéteur';
+
+ @override
+ String get contacts_roomPathTrace => 'Traçage du chemin vers le room server';
+
+ @override
+ String get contacts_roomPing => 'Pinguer le room server';
+
+ @override
+ String get contacts_chatTraceRoute => 'Tracer le chemin';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Tracer l\'itinéraire vers $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Le presse-papiers est vide.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Données de contact non valides';
+
+ @override
+ String get contacts_contactImported => 'Le contact a été importé.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Échec de l\'importation du contact.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Annonce Zero saut';
+
+ @override
+ String get contacts_floodAdvert => 'Annonce à tout le réseau';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Copier l\'annonce dans le presse-papiers';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Ajouter un contact depuis le presse-papiers';
+
+ @override
+ String get contacts_ShareContact =>
+ 'Copier le contact dans le presse-papiers';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Partager un contact par annonce';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Envoyer un contact par annonce.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Échec de l\'envoi du contact.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Annonce copiée dans le presse-papiers.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'La copie de l\'annonce vers le presse-papiers a échoué.';
+
+ @override
+ String get notification_activityTitle => 'Activité MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messages',
+ one: 'message',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messages de canal',
+ one: 'message de canal',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nouveaux nœuds',
+ one: 'nouveau nœud',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nouveau $contactType découvert';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nouveau message reçu';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exporter les répéteurs / room servers au format GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exporte les répéteurs / roomserver avec une localisation vers un fichier GPX.';
+
+ @override
+ String get settings_gpxExportContacts =>
+ 'Exporter les compagnons au format GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exporte les compagnons avec un emplacement vers un fichier GPX.';
+
+ @override
+ String get settings_gpxExportAll =>
+ 'Exporter tous les contacts au format GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exporte tous les contacts avec une localisation vers un fichier GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Fichier GPX exporté avec succès.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Aucun contact à exporter.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Non pris en charge sur votre appareil/Système d\'exploitation';
+
+ @override
+ String get settings_gpxExportError =>
+ 'Une erreur s\'est produite lors de l\'exportation.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Emplacements des répéteurs et room servers';
+
+ @override
+ String get settings_gpxExportChat => 'Emplacements des compagnons';
+
+ @override
+ String get settings_gpxExportAllContacts =>
+ 'Tous les emplacements des contacts';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Données de carte exportées à partir de meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open exporter les données de carte GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Répéteurs à proximité';
+
+ @override
+ String get snrIndicator_lastSeen => 'Dernière fois vu';
+
+ @override
+ String get contactsSettings_title => 'Paramètres des contacts';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Découverte automatique';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Autres paramètres liés aux contacts';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Ajouter automatiquement les utilisateurs';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Autoriser le compagnon à ajouter automatiquement les utilisateurs découverts';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Ajouter automatiquement les répéteurs';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Autoriser le compagnon à ajouter automatiquement les répéteurs découverts';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Ajouter automatiquement les room servers';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Autoriser le compagnon à ajouter automatiquement les room servers découverts';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Ajouter automatiquement les capteurs';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Autoriser le compagnon à ajouter automatiquement les capteurs découverts.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Écraser le plus ancien';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Lorsque la liste de contacts est pleine, le contact le plus ancien non favori sera remplacé.';
+
+ @override
+ String get discoveredContacts_Title => 'Contacts découverts';
+
+ @override
+ String get discoveredContacts_noMatching => 'Aucun contact correspondant';
+
+ @override
+ String get discoveredContacts_searchHint =>
+ 'Rechercher des contacts découverts';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contact ajouté';
+
+ @override
+ String get discoveredContacts_addContact => 'Ajouter un contact';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Copier le contact dans le presse-papiers';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Supprimer le contact';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Supprimer tous les contacts découverts';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Êtes-vous sûr de vouloir supprimer tous les contacts découverts ?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Veuillez patienter un instant avant de réessayer.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Accéder au message le plus ancien non lu';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Lorsque vous ouvrez une conversation contenant des messages non lus, faites défiler la page jusqu\'au premier message non lu, plutôt que jusqu\'au dernier.';
+
+ @override
+ String get appSettings_languageHu => 'Hongrois';
+
+ @override
+ String get appSettings_languageJa => 'Japonais';
+
+ @override
+ String get appSettings_languageKo => 'Coréen';
+
+ @override
+ String get radioStats_tooltip =>
+ 'Statistiques des radios et des réseaux sans fil';
+
+ @override
+ String get radioStats_screenTitle => 'Statistiques de radio';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Connectez-vous à un appareil pour visualiser les statistiques de la radio.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Les statistiques radio nécessitent un firmware compatible v8 ou une version ultérieure.';
+
+ @override
+ String get radioStats_waiting => 'En attente des données…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Niveau de bruit : $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Dernier RSSI : $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Dernier SNR : $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Temps d\'antenne à la télévision du Texas (total) : $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Temps d\'utilisation de l\'appareil RX (total) : $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Niveau de bruit (dBm) sur les échantillons récents.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Niveau de bruit : $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting =>
+ 'Récupération des statistiques de la radio…';
+
+ @override
+ String get radioStats_settingsTile => 'Statistiques de radio';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Niveau de bruit, RSSI, rapport signal/bruit (SNR) et temps d\'antenne';
+
+ @override
+ String get translation_title => 'Traduction';
+
+ @override
+ String get translation_enableTitle => 'Activer la traduction';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Traduire les messages entrants et permettre la traduction avant l\'envoi.';
+
+ @override
+ String get translation_composerTitle => 'Traduire avant d\'envoyer';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Contrôle l\'état par défaut de l\'icône de traduction du composant.';
+
+ @override
+ String get translation_targetLanguage => 'Langue cible';
+
+ @override
+ String get translation_useAppLanguage =>
+ 'Utiliser la langue de l\'application';
+
+ @override
+ String get translation_downloadedModelLabel => 'Modèle téléchargé';
+
+ @override
+ String get translation_presetModelLabel => 'Modèle Hugging Face préconfiguré';
+
+ @override
+ String get translation_manualUrlLabel => 'URL du modèle manuel';
+
+ @override
+ String get translation_downloadModel => 'Télécharger le modèle';
+
+ @override
+ String get translation_downloading => 'Téléchargement...';
+
+ @override
+ String get translation_working => 'Au travail...';
+
+ @override
+ String get translation_stop => 'Arrêtez';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Fusion des fragments téléchargés dans le fichier final...';
+
+ @override
+ String get translation_downloadedModels => 'Modèles téléchargés';
+
+ @override
+ String get translation_deleteModel => 'Supprimer le modèle';
+
+ @override
+ String get translation_modelDownloaded => 'Modèle de traduction téléchargé.';
+
+ @override
+ String get translation_downloadStopped =>
+ 'Le téléchargement a été interrompu.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Échec du téléchargement : $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Entrez d\'abord l\'URL du modèle.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Afficher le code PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Masquer le code PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle =>
+ 'Code PIN pour la connexion Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Entrez le code PIN pour $deviceName (laissez vide si nécessaire).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Traduction du message';
+
+ @override
+ String get translation_translateBeforeSending => 'Traduire avant d\'envoyer';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Les messages seront traduits avant d\'être envoyés.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Envoyez des messages dans la langue originale, telle que vous l\'avez tapée.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Traduire en $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Options de traduction';
+
+ @override
+ String get translation_systemLanguage => 'Langue du système';
}
diff --git a/lib/l10n/app_localizations_hu.dart b/lib/l10n/app_localizations_hu.dart
new file mode 100644
index 0000000..1ad8558
--- /dev/null
+++ b/lib/l10n/app_localizations_hu.dart
@@ -0,0 +1,3725 @@
+// ignore: unused_import
+import 'package:intl/intl.dart' as intl;
+import 'app_localizations.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Hungarian (`hu`).
+class AppLocalizationsHu extends AppLocalizations {
+ AppLocalizationsHu([String locale = 'hu']) : super(locale);
+
+ @override
+ String get appTitle => 'MeshCore Open';
+
+ @override
+ String get nav_contacts => 'Kapcsolatok';
+
+ @override
+ String get nav_channels => 'Csatornák';
+
+ @override
+ String get nav_map => 'Térkép';
+
+ @override
+ String get common_cancel => 'Át kell venni';
+
+ @override
+ String get common_ok => 'Rendben';
+
+ @override
+ String get common_connect => 'Kapcsolódj';
+
+ @override
+ String get common_unknownDevice => 'Tudatlan eszköz';
+
+ @override
+ String get common_save => 'Mentés';
+
+ @override
+ String get common_delete => 'Töröl';
+
+ @override
+ String get common_deleteAll => 'Minden törlés';
+
+ @override
+ String get common_close => 'Bezárás';
+
+ @override
+ String get common_edit => 'Szerkesztés';
+
+ @override
+ String get common_add => 'Hozzáad';
+
+ @override
+ String get common_settings => 'Beállítások';
+
+ @override
+ String get common_disconnect => 'Csatlakozást megszakasztani';
+
+ @override
+ String get common_connected => 'Kapcsolódó';
+
+ @override
+ String get common_disconnected => 'Elválasztva';
+
+ @override
+ String get common_create => 'Készítsd';
+
+ @override
+ String get common_continue => 'Folytatás';
+
+ @override
+ String get common_share => 'Ossza meg';
+
+ @override
+ String get common_copy => 'Másolat';
+
+ @override
+ String get common_retry => 'Újrapróbálja';
+
+ @override
+ String get common_hide => 'Elrejt';
+
+ @override
+ String get common_remove => 'Eltávolít';
+
+ @override
+ String get common_enable => 'Engedélyezés';
+
+ @override
+ String get common_disable => 'Leteteszt';
+
+ @override
+ String get common_reboot => 'Újraindítás';
+
+ @override
+ String get common_loading => 'Betöltés...';
+
+ @override
+ String get common_notAvailable => '—';
+
+ @override
+ String common_voltageValue(String volts) {
+ return '$volts V';
+ }
+
+ @override
+ String common_percentValue(int percent) {
+ return '$percent%';
+ }
+
+ @override
+ String get scanner_title => 'MeshCore nyitott';
+
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'TCP-n keresztül kapcsolódjon';
+
+ @override
+ String get tcpHostLabel => 'IP-cím';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Múzeum';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected =>
+ 'Adja meg a célpontot, majd kapcsolja össze.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Kapcsolat a $endpoint-hez...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Az IP-címet meg kell adni.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Az érték 1 és 65535 között kell lennie.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'A TCP-protokoll nem támogatott ez a platformon.';
+
+ @override
+ String get tcpErrorTimedOut => 'A TCP-kapcsolat időtúllépett.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'A TCP-kapcsolat sikertelen: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'USB-en keresztül csatlakoztassuk';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Válasszon egy azonosított soros eszközt, és közvetlenül csatlakoztassa a MeshCore-hoz.';
+
+ @override
+ String get usbScreenStatus => 'Válasszon egy USB-es eszközt';
+
+ @override
+ String get usbScreenNote =>
+ 'Az USB-es soros kommunikáció a támogatott Android eszközökön és asztali rendszereken is elérhető.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Nincs USB eszköz megtalálva. Csatlakoztasson egyet, majd frissítse a rendszert.';
+
+ @override
+ String get usbErrorPermissionDenied => 'A USB-es hozzáférés megtagadva.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Az kiválasztott USB eszköz már nem elérhető.';
+
+ @override
+ String get usbErrorInvalidPort => 'Válasszon egy érvényes USB-eszközt.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Egy másik USB-csatlakozás kérése már folyamatban van.';
+
+ @override
+ String get usbErrorNotConnected => 'Nincs csatlakoztatott USB eszköz.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Nem sikerült megnyitni a kiválasztott USB-eszközöt.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Nem sikerült kapcsolatot létesíteni a kiválasztott USB-eszközhöz.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Ez a platform nem támogat USB-es soros kommunikációt.';
+
+ @override
+ String get usbErrorAlreadyActive => 'Az USB-kapcsolat már be van állítva.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Nincs kiválasztva USB eszköz.';
+
+ @override
+ String get usbErrorPortClosed => 'Az USB-kapcsolat nem aktív.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Kapcsolódás sikertelen. Ellenőrizze, hogy a eszköz rendelkezik-e USB-hez tartozó firmware-rel.';
+
+ @override
+ String get usbFallbackDeviceName => 'Web-szériás eszköz';
+
+ @override
+ String get usbStatus_notConnected => 'Válasszon egy USB-es eszközt';
+
+ @override
+ String get usbStatus_connecting => 'USB eszközhez való csatlakozás...';
+
+ @override
+ String get usbStatus_searching => 'USB eszközök keresése...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'USB-kapcsolat sikertelen: $error';
+ }
+
+ @override
+ String get scanner_scanning => 'Készülékek keresése...';
+
+ @override
+ String get scanner_connecting => 'Kapcsolódás...';
+
+ @override
+ String get scanner_disconnecting => 'Kapcsolat megszakad...';
+
+ @override
+ String get scanner_notConnected => 'Nem csatlakozva';
+
+ @override
+ String scanner_connectedTo(String deviceName) {
+ return 'Kapcsolódik a $deviceName-hez';
+ }
+
+ @override
+ String get scanner_searchingDevices => 'MeshCore eszközök keresése...';
+
+ @override
+ String get scanner_tapToScan =>
+ 'A Tap Scan funkció segítségével kereshet MeshCore eszközöket.';
+
+ @override
+ String scanner_connectionFailed(String error) {
+ return 'Kapcsolódás sikertelen: $error';
+ }
+
+ @override
+ String get scanner_stop => 'Megállj';
+
+ @override
+ String get scanner_scan => 'Szkenálás';
+
+ @override
+ String get scanner_bluetoothOff => 'A Bluetooth kikapcsolva';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Kérjük, kapcsolja be a Bluetooth-ot, hogy eszközök keresése lehessen.';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome böngésző szükséges';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Ez az alkalmazás a Bluetooth funkcióhoz Google Chrome-ot vagy Chromium alapú böngészőt igényel.';
+
+ @override
+ String get scanner_enableBluetooth => 'Engedje be a Bluetooth funkciót';
+
+ @override
+ String get device_quickSwitch => 'Gyors váltás';
+
+ @override
+ String get device_meshcore => 'MeshCore';
+
+ @override
+ String get settings_title => 'Beállítások';
+
+ @override
+ String get settings_deviceInfo => 'A készülék információi';
+
+ @override
+ String get settings_appSettings => 'Alkalmazási beállítások';
+
+ @override
+ String get settings_appSettingsSubtitle =>
+ 'Értesítések, üzenetküldés és térképi beállítások';
+
+ @override
+ String get settings_nodeSettings => 'Műközép beállítások';
+
+ @override
+ String get settings_nodeName => 'Vonal neve';
+
+ @override
+ String get settings_nodeNameNotSet => 'Nem megállapított';
+
+ @override
+ String get settings_nodeNameHint => 'Adja meg a csomópont nevét';
+
+ @override
+ String get settings_nodeNameUpdated => 'Neve frissítve';
+
+ @override
+ String get settings_radioSettings => 'Rádióbeállítások';
+
+ @override
+ String get settings_radioSettingsSubtitle =>
+ 'Frekvencia, teljesítmény, szélesítési tényező';
+
+ @override
+ String get settings_radioSettingsUpdated => 'A rádió beállítások frissítve';
+
+ @override
+ String get settings_location => 'Helyszín';
+
+ @override
+ String get settings_locationSubtitle => 'GPS koordináták';
+
+ @override
+ String get settings_locationUpdated =>
+ 'A helyzet és a GPS beállítások frissítve';
+
+ @override
+ String get settings_locationBothRequired =>
+ 'Kérjük, adja meg a földrajzi szélességet és hosszúságot.';
+
+ @override
+ String get settings_locationInvalid =>
+ 'Érvénytelen szélesszög vagy hosszszög.';
+
+ @override
+ String get settings_locationGPSEnable => 'GPS engedélyezve';
+
+ @override
+ String get settings_locationGPSEnableSubtitle =>
+ 'Lehetővé teszi, hogy a GPS automatikusan frissítse a helyzetet.';
+
+ @override
+ String get settings_locationIntervalSec =>
+ 'GPS-számolási intervallum (másodpercek)';
+
+ @override
+ String get settings_locationIntervalInvalid =>
+ 'Az intervallum legalább 60 másodpercnek, de legfeljebb 86400 másodpercnak kell lennie.';
+
+ @override
+ String get settings_latitude => 'Nyugat-–––––––––––––––––––––––––––––––';
+
+ @override
+ String get settings_longitude => 'hosszúság';
+
+ @override
+ String get settings_contactSettings => 'Kapcsolat beállítások';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Beállítások, amelyek meghatározzák, hogyan lehet új kapcsolatokat hozzáadni.';
+
+ @override
+ String get settings_privacyMode => 'Adatvédelem mód';
+
+ @override
+ String get settings_privacyModeSubtitle =>
+ 'Elrejtsük a nevét/a helyszínt az űrianyagokban';
+
+ @override
+ String get settings_privacyModeToggle =>
+ 'Engedje be a privát üzemmódot, hogy elrejtse a nevét és a helyét az online hirdetésekben.';
+
+ @override
+ String get settings_privacyModeEnabled => 'Adatvédelem mód beállítva';
+
+ @override
+ String get settings_privacyModeDisabled => 'Adatvédelem mód kikapcsolva';
+
+ @override
+ String get settings_privacy => 'Adatvédelem beállítások';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Ellenőrizd, hogy milyen információkat osztanak meg.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Válassza ki, hogy az eszközének melyik információkat oszt meg másokkal.';
+
+ @override
+ String get settings_denyAll => 'Elutasítom';
+
+ @override
+ String get settings_allowByContact =>
+ 'Lehetővé teszi a kapcsolatok kezelését';
+
+ @override
+ String get settings_allowAll => 'Engedje meg mindent';
+
+ @override
+ String get settings_telemetryBaseMode => 'Adatkapcsolati alapállapot';
+
+ @override
+ String get settings_telemetryLocationMode => 'Adatkapcsolási helyszín mód';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Adatkapcsolati környezeti mód';
+
+ @override
+ String get settings_advertLocation => 'Reklám megjelenési hely';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'A hirdetés tartalmazza a helyszínt.';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Többszöri visszaigazolások: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'A telemetriamód frissítve';
+
+ @override
+ String get settings_actions => 'Tevékenységek';
+
+ @override
+ String get settings_sendAdvertisement => 'Hirdetés küldése';
+
+ @override
+ String get settings_sendAdvertisementSubtitle => 'A nyilvános megjelenés';
+
+ @override
+ String get settings_advertisementSent => 'Hirdetés elküldve';
+
+ @override
+ String get settings_syncTime => 'Szinkronizációs idő';
+
+ @override
+ String get settings_syncTimeSubtitle =>
+ 'Állítsa a készülék időzítését a telefon időjére';
+
+ @override
+ String get settings_timeSynchronized => 'Időben szinkronizált';
+
+ @override
+ String get settings_refreshContacts => 'Újraindítsd a kapcsolatok listát';
+
+ @override
+ String get settings_refreshContactsSubtitle =>
+ 'Újra töltse a kontaktlista-adatokat a készülékről';
+
+ @override
+ String get settings_rebootDevice => 'Újraindítás';
+
+ @override
+ String get settings_rebootDeviceSubtitle =>
+ 'Indítsa újra a MeshCore eszközt.';
+
+ @override
+ String get settings_rebootDeviceConfirm =>
+ 'Biztosan szeretné újraindítani a készüléket? Ebben az esetben a kapcsolat megszűnik.';
+
+ @override
+ String get settings_debug => 'Hibakeresés';
+
+ @override
+ String get settings_bleDebugLog => 'BLE hibaelhárítási napló';
+
+ @override
+ String get settings_bleDebugLogSubtitle =>
+ 'BLE parancsok, válaszok és alapvető adatok';
+
+ @override
+ String get settings_appDebugLog => 'App-debug log';
+
+ @override
+ String get settings_appDebugLogSubtitle => 'Programozási hibajelzések';
+
+ @override
+ String get settings_about => 'Ról';
+
+ @override
+ String settings_aboutVersion(String version) {
+ return 'MeshCore Open $version verzió';
+ }
+
+ @override
+ String get settings_aboutLegalese =>
+ '2026-os MeshCore nyílt forráskódú projekt';
+
+ @override
+ String get settings_aboutDescription =>
+ 'Egy nyílt forráskódú Flutter kliens a MeshCore LoRa hálózati eszközök számára.';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS magassági adatok: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => 'Név';
+
+ @override
+ String get settings_infoId => 'Az azonosító';
+
+ @override
+ String get settings_infoStatus => 'Állapot';
+
+ @override
+ String get settings_infoBattery => 'Akku';
+
+ @override
+ String get settings_infoPublicKey => 'Nyelvkönyv';
+
+ @override
+ String get settings_infoContactsCount => 'Kapcsolatok száma';
+
+ @override
+ String get settings_infoChannelCount => 'Csatorna száma';
+
+ @override
+ String get settings_presets => 'Előre beállított beállítások';
+
+ @override
+ String get settings_frequency => 'Frekvencia (MHz)';
+
+ @override
+ String get settings_frequencyHelper => '300,0 – 2500,0';
+
+ @override
+ String get settings_frequencyInvalid =>
+ 'Érvénytelen frekvencia (300-2500 MHz)';
+
+ @override
+ String get settings_bandwidth => 'Kapacitás';
+
+ @override
+ String get settings_spreadingFactor => 'Terjesztési tényező';
+
+ @override
+ String get settings_codingRate => 'Kódolási sebesség';
+
+ @override
+ String get settings_txPower => 'TX teljesítmény (dBm)';
+
+ @override
+ String get settings_txPowerHelper => '0 – 22';
+
+ @override
+ String get settings_txPowerInvalid =>
+ 'Érvénytelen TX teljesítmény (0-22 dBm)';
+
+ @override
+ String get settings_clientRepeat => 'Autonóm rendszer újra';
+
+ @override
+ String get settings_clientRepeatSubtitle =>
+ 'Engedje, hogy ez a eszköz mások számára is ismételje a hálózati csomagokat.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'A hálózat nélküli kommunikációhoz 433, 869 vagy 918 MHz frekvenciát igényel.';
+
+ @override
+ String settings_error(String message) {
+ return 'Hiba: $message';
+ }
+
+ @override
+ String get appSettings_title => 'Alkalmazási beállítások';
+
+ @override
+ String get appSettings_appearance => 'Megjelenés';
+
+ @override
+ String get appSettings_theme => 'Téma';
+
+ @override
+ String get appSettings_themeSystem => 'Alapértékek';
+
+ @override
+ String get appSettings_themeLight => 'Világítás';
+
+ @override
+ String get appSettings_themeDark => 'Sötét';
+
+ @override
+ String get appSettings_language => 'Nyelv';
+
+ @override
+ String get appSettings_languageSystem => 'Alapértékek';
+
+ @override
+ String get appSettings_languageEn => 'Angol';
+
+ @override
+ String get appSettings_languageFr => 'Francia';
+
+ @override
+ String get appSettings_languageEs => 'Spanyol';
+
+ @override
+ String get appSettings_languageDe => 'Német';
+
+ @override
+ String get appSettings_languagePl => 'Lengyel';
+
+ @override
+ String get appSettings_languageSl => 'szlovén nyelv';
+
+ @override
+ String get appSettings_languagePt => 'Portugál';
+
+ @override
+ String get appSettings_languageIt => 'Olasz';
+
+ @override
+ String get appSettings_languageZh => 'Kínai';
+
+ @override
+ String get appSettings_languageSv => 'Svéd';
+
+ @override
+ String get appSettings_languageNl => 'Hollandi';
+
+ @override
+ String get appSettings_languageSk => 'Szlovén nyelvre fordítás';
+
+ @override
+ String get appSettings_languageBg => 'Bulgár';
+
+ @override
+ String get appSettings_languageRu => 'Orosz';
+
+ @override
+ String get appSettings_languageUk => 'Украинский';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Engedje meg a üzenetek nyomon követését';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Adja meg a üzenetek részletes útvonal- és időzítési adatokat.';
+
+ @override
+ String get appSettings_notifications => 'Értesítések';
+
+ @override
+ String get appSettings_enableNotifications => 'Engedélyezze az értesítéseket';
+
+ @override
+ String get appSettings_enableNotificationsSubtitle =>
+ 'Kapjon értesítéseket üzenetekről és hirdetésekről.';
+
+ @override
+ String get appSettings_notificationPermissionDenied =>
+ 'A értesítési engedély megtagadva';
+
+ @override
+ String get appSettings_notificationsEnabled =>
+ 'A figyelmeztetések engedélyezve';
+
+ @override
+ String get appSettings_notificationsDisabled =>
+ 'A figyelmeztetések kikapcsolva';
+
+ @override
+ String get appSettings_messageNotifications => 'Üzenet értesítések';
+
+ @override
+ String get appSettings_messageNotificationsSubtitle =>
+ 'A figyelmeztetést megjelenítve, amikor új üzenet érkezik';
+
+ @override
+ String get appSettings_channelMessageNotifications =>
+ 'Csatorna-üzenetek értesítése';
+
+ @override
+ String get appSettings_channelMessageNotificationsSubtitle =>
+ 'A figyelmeztetést megjelenítve, amikor új üzenet érkezik a csatornáról';
+
+ @override
+ String get appSettings_advertisementNotifications => 'Reklám értesítések';
+
+ @override
+ String get appSettings_advertisementNotificationsSubtitle =>
+ 'A figyelmeztetést megjelenítve, amikor új csomópontok kerülnek felfedezésre.';
+
+ @override
+ String get appSettings_messaging => 'Üzenetek küldése';
+
+ @override
+ String get appSettings_clearPathOnMaxRetry =>
+ 'Egyértelmű út a Max Retry funkció használatával';
+
+ @override
+ String get appSettings_clearPathOnMaxRetrySubtitle =>
+ 'A kapcsolat visszaállítás 5 sikertelen továbbítás után';
+
+ @override
+ String get appSettings_pathsWillBeCleared =>
+ 'Ha 5-szer sikertelenül próbálunk, a útvonalat automatikusan tisztítjuk.';
+
+ @override
+ String get appSettings_pathsWillNotBeCleared =>
+ 'A utak automatikusan nem tisztítódnak.';
+
+ @override
+ String get appSettings_autoRouteRotation => 'Autóútok forgása';
+
+ @override
+ String get appSettings_autoRouteRotationSubtitle =>
+ 'Válasszon a legjobb útvonalak között, vagy válassza a vízözön-módot.';
+
+ @override
+ String get appSettings_autoRouteRotationEnabled =>
+ 'Az automatikus útvonalváltás engedélyezve';
+
+ @override
+ String get appSettings_autoRouteRotationDisabled =>
+ 'Az automatikus útvonal-választás funkció kikapcsolva.';
+
+ @override
+ String get appSettings_maxRouteWeight => 'Maximális útvonal súly';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'A lehető legnagyobb súly, amit egy útvonal sikeres szállítmányok során összegyűjthet.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'A kezdeti útvonal súlya';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Az új, felfedezett útvonalakhoz tartozó kezdeti súly';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Sikerhez vezető növelés';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'A sikeresen teljesített útvonalhoz hozzáadott súly.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement => 'Hibás súly csökkenése';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'A jártatásból eltávolított súly, ami a sikertelen szállítás következménye.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Maximális üzenetek újraküldési próbálkozások';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'A próbálkozások száma, mielőtt egy üzenetet hibásnak jelölünk.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
+ @override
+ String get appSettings_battery => 'Akku';
+
+ @override
+ String get appSettings_batteryChemistry => 'Aakkum töltés kémia';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return 'Beállítások $deviceName-hez';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst =>
+ 'Csatlakozzon egy eszközhez, hogy kiválassza';
+
+ @override
+ String get appSettings_batteryNmc => '18650 NMC (3,0-4,2 V)';
+
+ @override
+ String get appSettings_batteryLifepo4 => 'LiFePO4 (2,6–3,65 V)';
+
+ @override
+ String get appSettings_batteryLipo => 'LiPo (3,0-4,2 V)';
+
+ @override
+ String get appSettings_mapDisplay => 'Térkép megjelenítése';
+
+ @override
+ String get appSettings_showRepeaters => 'Megismétlés';
+
+ @override
+ String get appSettings_showRepeatersSubtitle =>
+ 'A térképen megjelenítsük a repeater-eket.';
+
+ @override
+ String get appSettings_showChatNodes => 'Megjeleníts kommunikációs pontokat';
+
+ @override
+ String get appSettings_showChatNodesSubtitle =>
+ 'A chat-szobákat megjelenítsük a térképen';
+
+ @override
+ String get appSettings_showOtherNodes => 'Mutasson további csomópontokat';
+
+ @override
+ String get appSettings_showOtherNodesSubtitle =>
+ 'Mutassa meg a többi hálózati elemet a térképen';
+
+ @override
+ String get appSettings_timeFilter => 'Időbeli szűrés';
+
+ @override
+ String get appSettings_timeFilterShowAll =>
+ 'Mutassa meg az összes csomópontot';
+
+ @override
+ String appSettings_timeFilterShowLast(int hours) {
+ return 'Mutasson az utolsó $hours órából származó adatokat.';
+ }
+
+ @override
+ String get appSettings_mapTimeFilter => 'Térkép időszűrő';
+
+ @override
+ String get appSettings_showNodesDiscoveredWithin =>
+ 'Megjeleníts olyan węzveket, amelyek a következő területen lettek felfedezve:';
+
+ @override
+ String get appSettings_allTime => 'Minden időpont';
+
+ @override
+ String get appSettings_lastHour => 'Az utolsó óra';
+
+ @override
+ String get appSettings_last6Hours => 'Az utóban 6 óra';
+
+ @override
+ String get appSettings_last24Hours => 'Az utóbbi 24 óra';
+
+ @override
+ String get appSettings_lastWeek => 'A múlt héten';
+
+ @override
+ String get appSettings_offlineMapCache => 'Offline térkép tárolás';
+
+ @override
+ String get appSettings_unitsTitle => 'Egységek';
+
+ @override
+ String get appSettings_unitsMetric => 'Méter (m / kilométer)';
+
+ @override
+ String get appSettings_unitsImperial => 'Királyi (láb / mérföld)';
+
+ @override
+ String get appSettings_noAreaSelected => 'Nincs kiválasztott terület.';
+
+ @override
+ String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
+ return 'Kiválasztott terület (zoom: $minZoom-$maxZoom)';
+ }
+
+ @override
+ String get appSettings_debugCard => 'Hibakeresés';
+
+ @override
+ String get appSettings_appDebugLogging =>
+ 'App-ban történő hibakereséshez használt naplózás';
+
+ @override
+ String get appSettings_appDebugLoggingSubtitle =>
+ 'Log alkalmazás hibaelhárítási üzenetek';
+
+ @override
+ String get appSettings_appDebugLoggingEnabled =>
+ 'Az alkalmazás hibaelhárítási naplózás engedélyezve';
+
+ @override
+ String get appSettings_appDebugLoggingDisabled =>
+ 'Az alkalmazás hibaelhárítási naplózatának bekapcsolása kiküszöbölve';
+
+ @override
+ String get contacts_title => 'Kapcsolatok';
+
+ @override
+ String get contacts_noContacts => 'Jelenleg még nincs kapcsolat.';
+
+ @override
+ String get contacts_contactsWillAppear =>
+ 'A kapcsolatok megjelennek, amikor a eszközök hirdetnek.';
+
+ @override
+ String get contacts_unread => 'Olvasatlan';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Kapcsolatok keresése...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Keresés $number-ban $str…';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Keresés $number$str... Kedvencek';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Search $number$str Users...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Keresés $number-on, $str típusú adóállomások között...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Keresés $number-ban $str...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => 'Nincs olvasatlan üzenetek';
+
+ @override
+ String get contacts_noContactsFound =>
+ 'Nincs megtalálva semmilyen kapcsolat vagy csoport.';
+
+ @override
+ String get contacts_deleteContact => 'Kapcsolattól töröl';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return 'Hogy töröljem a $contactName nevű személyt a kontaktlistából?';
+ }
+
+ @override
+ String get contacts_manageRepeater => 'Ellenőriző eszköz kezelése';
+
+ @override
+ String get contacts_manageRoom => 'A szobai szerver kezelése';
+
+ @override
+ String get contacts_roomLogin => 'Szoba szerverbe való bejelentkezés';
+
+ @override
+ String get contacts_openChat => 'Nyitott beszélgetés';
+
+ @override
+ String get contacts_editGroup => 'Edit csoport';
+
+ @override
+ String get contacts_deleteGroup => 'Csoport törlése';
+
+ @override
+ String contacts_deleteGroupConfirm(String groupName) {
+ return 'Hogy töröljem a \"$groupName\"-t?';
+ }
+
+ @override
+ String get contacts_newGroup => 'Új csoport';
+
+ @override
+ String get contacts_groupName => 'Csoport neve';
+
+ @override
+ String get contacts_groupNameRequired =>
+ 'A csoportnak meg kell adni a nevét.';
+
+ @override
+ String get contacts_groupNameReserved => 'Ez a csoportnév foglalt';
+
+ @override
+ String contacts_groupAlreadyExists(String name) {
+ return 'A \"$name\" nevű csoport már létezik.';
+ }
+
+ @override
+ String get contacts_filterContacts => 'Szűrj kontaktokat...';
+
+ @override
+ String get contacts_noContactsMatchFilter =>
+ 'Nincs találat a megadott szűrés alapján.';
+
+ @override
+ String get contacts_noMembers => 'Nincsenek tagok';
+
+ @override
+ String get contacts_lastSeenNow => 'utóbbi időben';
+
+ @override
+ String contacts_lastSeenMinsAgo(int minutes) {
+ return '~ $minutes perc';
+ }
+
+ @override
+ String get contacts_lastSeenHourAgo => 'Kb. 1 óra';
+
+ @override
+ String contacts_lastSeenHoursAgo(int hours) {
+ return '~ $hours óra';
+ }
+
+ @override
+ String get contacts_lastSeenDayAgo => 'Kb. 1 nap';
+
+ @override
+ String contacts_lastSeenDaysAgo(int days) {
+ return '~ $days days';
+ }
+
+ @override
+ String get contact_info => 'Kapcsolattartási információk';
+
+ @override
+ String get contact_settings => 'Kapcsolat beállítások';
+
+ @override
+ String get contact_telemetry => 'Adatvisszaadás';
+
+ @override
+ String get contact_lastSeen => 'Utoljára, amikor látták';
+
+ @override
+ String get contact_clearChat => 'Tiszta beszélgetés';
+
+ @override
+ String get contact_teleBase => 'Adatgyűjtő központ';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Engedje meg a akkumulátor töltöttségi szintjének és alapvető adatoknak megosztását.';
+
+ @override
+ String get contact_teleLoc => 'Adatkapcsolati helyszín';
+
+ @override
+ String get contact_teleLocSubtitle => 'Engedje meg a helyadatok megosztását';
+
+ @override
+ String get contact_teleEnv => 'Adatkapcsolati környezet';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Engedje meg az érzékelő adatok megosztását';
+
+ @override
+ String get channels_title => 'Csatornák';
+
+ @override
+ String get channels_noChannelsConfigured => 'Nincs konfigurált csatorna.';
+
+ @override
+ String get channels_addPublicChannel => 'Hozzon létre nyilvános csatornát';
+
+ @override
+ String get channels_searchChannels => 'Keresési opciók...';
+
+ @override
+ String get channels_noChannelsFound => 'Nincs megtalálható csatorna';
+
+ @override
+ String channels_channelIndex(int index) {
+ return '$index-os csatorna';
+ }
+
+ @override
+ String get channels_hashtagChannel => 'Hashtag-ok közössége';
+
+ @override
+ String get channels_public => 'A nyilvánosság számára';
+
+ @override
+ String get channels_private => 'Személyes';
+
+ @override
+ String get channels_publicChannel => 'Össztávos csatorna';
+
+ @override
+ String get channels_privateChannel => 'Személyes csatorna';
+
+ @override
+ String get channels_editChannel => 'Csatorna szerkesztése';
+
+ @override
+ String get channels_muteChannel => 'Csendes csatorna';
+
+ @override
+ String get channels_unmuteChannel => 'Engedje be a hangot';
+
+ @override
+ String get channels_deleteChannel => 'Mozdony törlése';
+
+ @override
+ String channels_deleteChannelConfirm(String name) {
+ return 'Törlés $name? Ez nem visszafordítható.';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Nem sikerült törölni a \"$name\" nevű csatornát.';
+ }
+
+ @override
+ String channels_channelDeleted(String name) {
+ return 'A \"$name\" nevű csatorna törölve';
+ }
+
+ @override
+ String get channels_addChannel => 'Csatorna hozzáadása';
+
+ @override
+ String get channels_channelIndexLabel => 'Csatorna index';
+
+ @override
+ String get channels_channelName => 'Csatorna neve';
+
+ @override
+ String get channels_usePublicChannel => 'Használja a nyilvános csatornát';
+
+ @override
+ String get channels_standardPublicPsk =>
+ 'Általános, állami által finanszírozott PSK';
+
+ @override
+ String get channels_pskHex => 'PSK (Hexadecimális kód)';
+
+ @override
+ String get channels_generateRandomPsk => 'Véletlenszerűen generáljon PSK-t';
+
+ @override
+ String get channels_enterChannelName => 'Kérjük, adja meg egy csatorna nevét';
+
+ @override
+ String get channels_pskMustBe32Hex =>
+ 'A PSK 32-bázisú hexadecimális karakterből áll.';
+
+ @override
+ String channels_channelAdded(String name) {
+ return 'A \"$name\" csatorna hozzáadva';
+ }
+
+ @override
+ String channels_editChannelTitle(int index) {
+ return 'Módosítsd a csatornát $index';
+ }
+
+ @override
+ String get channels_smazCompression => 'SMAZ kompresszió';
+
+ @override
+ String channels_channelUpdated(String name) {
+ return 'A $name csatorna frissítve';
+ }
+
+ @override
+ String get channels_publicChannelAdded => 'A nyilvános csatorna hozzáadva';
+
+ @override
+ String get channels_sortBy => 'Szűrés';
+
+ @override
+ String get channels_sortManual => 'Használati útmutató';
+
+ @override
+ String get channels_sortAZ => 'A-Z';
+
+ @override
+ String get channels_sortLatestMessages => 'Legfrissebb üzenetek';
+
+ @override
+ String get channels_sortUnread => 'Olvasatlan';
+
+ @override
+ String get channels_createPrivateChannel => 'Létrehoz egy privát csatornát';
+
+ @override
+ String get channels_createPrivateChannelDesc =>
+ 'Titkos kulcs segítségével védelem.';
+
+ @override
+ String get channels_joinPrivateChannel =>
+ 'Csatlakozzon egy privát csatornához';
+
+ @override
+ String get channels_joinPrivateChannelDesc =>
+ 'Kézzel adja meg a titkos kulcsot.';
+
+ @override
+ String get channels_joinPublicChannel =>
+ 'Csatlakozzon a nyilvános csatornához';
+
+ @override
+ String get channels_joinPublicChannelDesc =>
+ 'Bárki csatlakozhat ehhez a csatornához.';
+
+ @override
+ String get channels_joinHashtagChannel =>
+ 'Csatlakozzon egy hashtage-os csatornához';
+
+ @override
+ String get channels_joinHashtagChannelDesc =>
+ 'Bárkinek lehet csatlakoznia a hashtagekhez tartozó csatornához.';
+
+ @override
+ String get channels_scanQrCode => 'Scanned egy QR-kódot';
+
+ @override
+ String get channels_scanQrCodeComingSoon => 'Hamarosan';
+
+ @override
+ String get channels_enterHashtag => 'Írja be a hashtaget';
+
+ @override
+ String get channels_hashtagHint => 'pl. #csapat';
+
+ @override
+ String get chat_noMessages => 'Még nincs üzenet.';
+
+ @override
+ String get chat_sendMessage => 'Üzenet küldése';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Küldj üzenetet $contactName-nek';
+ }
+
+ @override
+ String get chat_sendMessageToStart => 'Küldj egy üzenetet, hogy elindulj!';
+
+ @override
+ String get chat_originalMessageNotFound => 'A eredeti üzenet nem található.';
+
+ @override
+ String chat_replyingTo(String name) {
+ return 'Replying to $name';
+ }
+
+ @override
+ String chat_replyTo(String name) {
+ return 'Reply to $name';
+ }
+
+ @override
+ String get chat_location => 'Helyszín';
+
+ @override
+ String get chat_typeMessage => 'Írjon üzenetet...';
+
+ @override
+ String chat_messageTooLong(int maxBytes) {
+ return 'A üzenet túl hosszú (a maximális $maxBytes bájt).';
+ }
+
+ @override
+ String get chat_messageCopied => 'Üzenet másolva';
+
+ @override
+ String get chat_messageDeleted => 'Üzenet törölve';
+
+ @override
+ String get chat_retryingMessage => 'Újrapróbálási üzenet';
+
+ @override
+ String chat_retryCount(int current, int max) {
+ return 'Újrapróbál $current/$max';
+ }
+
+ @override
+ String get chat_sendGif => 'Küldj GIF-ot';
+
+ @override
+ String get chat_reply => 'Válasz';
+
+ @override
+ String get chat_addReaction => 'Hozzon létre reakciót';
+
+ @override
+ String get chat_me => 'Én';
+
+ @override
+ String get emojiCategorySmileys => 'Emoji';
+
+ @override
+ String get emojiCategoryGestures => 'Testmozgások';
+
+ @override
+ String get emojiCategoryHearts => 'Szívak';
+
+ @override
+ String get emojiCategoryObjects => 'Tárgyak';
+
+ @override
+ String get gifPicker_title => 'Válasszon egy GIF-et';
+
+ @override
+ String get gifPicker_searchHint => 'GIF-ek keresése...';
+
+ @override
+ String get gifPicker_poweredBy => 'Forrás: GIPHY';
+
+ @override
+ String get gifPicker_noGifsFound => 'Nincsenek GIF-ek megtalálva.';
+
+ @override
+ String get gifPicker_failedLoad => 'Nem sikerült betölteni a GIF-fájlokat.';
+
+ @override
+ String get gifPicker_failedSearch => 'Nem sikerült a GIF-eket megtalálni.';
+
+ @override
+ String get gifPicker_noInternet => 'Nincs internetkapcsolat.';
+
+ @override
+ String get debugLog_appTitle => 'App-debug log';
+
+ @override
+ String get debugLog_bleTitle => 'BLE hibajelentő napló';
+
+ @override
+ String get debugLog_copyLog => 'Másolat napló';
+
+ @override
+ String get debugLog_clearLog => 'Jelzett napló';
+
+ @override
+ String get debugLog_copied => 'Hibajelentő napló másolva';
+
+ @override
+ String get debugLog_bleCopied => 'BLE-log másolva';
+
+ @override
+ String get debugLog_noEntries =>
+ 'Jelenleg még nem léteznek hibaelhárítási naplókat.';
+
+ @override
+ String get debugLog_enableInSettings =>
+ 'Engedje be az alkalmazás hibaelhárítási naplózását a beállítások menüben.';
+
+ @override
+ String get debugLog_frames => 'Keretek';
+
+ @override
+ String get debugLog_rawLogRx => 'Az eredeti Log-RX';
+
+ @override
+ String get debugLog_noBleActivity =>
+ 'Jelenleg nincs BLE-hez kapcsolódó tevékenység.';
+
+ @override
+ String debugFrame_length(int count) {
+ return 'Keret hossza: $count bájt';
+ }
+
+ @override
+ String debugFrame_command(String value) {
+ return 'Parancs: 0x$value';
+ }
+
+ @override
+ String get debugFrame_textMessageHeader => 'Címzett:';
+
+ @override
+ String debugFrame_destinationPubKey(String pubKey) {
+ return '- Célhely: $pubKey';
+ }
+
+ @override
+ String debugFrame_timestamp(int timestamp) {
+ return '- Időbélyeg: $timestamp';
+ }
+
+ @override
+ String debugFrame_flags(String value) {
+ return '- Jelvények: 0x$value';
+ }
+
+ @override
+ String debugFrame_textType(int type, String label) {
+ return '- Tartalom típusa: $type ($label)';
+ }
+
+ @override
+ String get debugFrame_textTypeCli => 'Parancssori felület (CLI)';
+
+ @override
+ String get debugFrame_textTypePlain => 'Egyszerű, alap, hagyományos';
+
+ @override
+ String debugFrame_text(String text) {
+ return '- Tartalom: \"$text\"';
+ }
+
+ @override
+ String get debugFrame_hexDump => 'Hex-dump:';
+
+ @override
+ String get chat_pathManagement => 'Útvonal-kezelés';
+
+ @override
+ String get chat_ShowAllPaths => 'Mutasson meg minden útvonalat';
+
+ @override
+ String get chat_routingMode => 'Útvonal-kezelési mód';
+
+ @override
+ String get chat_autoUseSavedPath =>
+ 'Automatikus (az eddigi útvonal használata)';
+
+ @override
+ String get chat_forceFloodMode => 'Erőforrás-alapú áramlás mód';
+
+ @override
+ String get chat_recentAckPaths =>
+ 'Legutóbbi használt útvonalak (gombra kattintva):';
+
+ @override
+ String get chat_pathHistoryFull =>
+ 'Az előző lépések listája teljes. Törölj ki a bejegyzéseket, hogy újokat hozzáadhatsd.';
+
+ @override
+ String get chat_hopSingular => 'ugor';
+
+ @override
+ String get chat_hopPlural => 'babér';
+
+ @override
+ String chat_hopsCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'ugrások',
+ one: 'ugrás',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String get chat_successes => 'sikerek';
+
+ @override
+ String get chat_removePath => 'Törölje a elérési útvonalat';
+
+ @override
+ String get chat_noPathHistoryYet =>
+ 'Még nincs útvonal-történet.\nKüldjön egy üzenetet, hogy megtudja a lehetséges útvonalakat.';
+
+ @override
+ String get chat_pathActions => 'Céltúrások:';
+
+ @override
+ String get chat_setCustomPath => 'Beállítsd a saját útvonalat';
+
+ @override
+ String get chat_setCustomPathSubtitle => 'Kézzel megadott útvonal';
+
+ @override
+ String get chat_clearPath => 'Egyértelmű út';
+
+ @override
+ String get chat_clearPathSubtitle =>
+ 'A parancs új küldéskor újra kell aktivizálnia.';
+
+ @override
+ String get chat_pathCleared =>
+ 'Útvonal cleared. A következő üzenet újból feltérképezheti az útvonalat.';
+
+ @override
+ String get chat_floodModeSubtitle =>
+ 'Használja a \"útvonal\" kapcsolót az alkalmazás sávjában.';
+
+ @override
+ String get chat_floodModeEnabled =>
+ 'Árvízvédelmi mód bekapcsolva. A visszaállítás a alkalmazásban található útvonal ikon segítségével.';
+
+ @override
+ String get chat_fullPath => 'Teljes elérési út';
+
+ @override
+ String get chat_pathDetailsNotAvailable =>
+ 'Az útvonal részletei még nem elérhetők. Próbálja meg küldeni egy üzenetet, hogy frissítse az információkat.';
+
+ @override
+ String chat_pathSetHops(int hopCount, String status) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hopCount,
+ locale: localeName,
+ other: 'hops',
+ one: 'hop',
+ );
+ return 'Path set: $hopCount $_temp0 - $status';
+ }
+
+ @override
+ String get chat_pathSavedLocally =>
+ 'Helyileg mentve. Kapcsolódjon a szinkronizáláshoz.';
+
+ @override
+ String get chat_pathDeviceConfirmed => 'A készülék megvan.';
+
+ @override
+ String get chat_pathDeviceNotConfirmed => 'A készülék még nem bizonyított.';
+
+ @override
+ String get chat_type => 'Típus';
+
+ @override
+ String get chat_path => 'Út';
+
+ @override
+ String get chat_publicKey => 'Nyelvkönyv';
+
+ @override
+ String get chat_compressOutgoingMessages => 'A küldött üzenetek tömörítése';
+
+ @override
+ String get chat_floodForced => 'Áradás (kényszerített)';
+
+ @override
+ String get chat_directForced => 'Közvetlen (erélyes)';
+
+ @override
+ String chat_hopsForced(int count) {
+ return '$count ánusz (erővel)';
+ }
+
+ @override
+ String get chat_floodAuto => 'Vízosztás (autó)';
+
+ @override
+ String get chat_direct => 'Közvetlen';
+
+ @override
+ String get chat_poiShared => 'Közös erőforrás';
+
+ @override
+ String chat_unread(int count) {
+ return 'Olvasatlan: $count';
+ }
+
+ @override
+ String get chat_openLink => 'Nyisd meg a linket?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Szeretnéd megnyitni ezt a linket a böngésződben?';
+
+ @override
+ String get chat_open => 'Nyitott';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Nem sikerült megnyitni a hivat: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Érvénytelen hivatkozás formátum';
+
+ @override
+ String get map_title => 'Grafikus ábrázás';
+
+ @override
+ String get map_lineOfSight => 'Látási vonal';
+
+ @override
+ String get map_losScreenTitle => 'Látási vonal';
+
+ @override
+ String get map_noNodesWithLocation =>
+ 'Nincs olyan adatpont, amelyhez helyszín-információk tartoznak.';
+
+ @override
+ String get map_nodesNeedGps =>
+ 'A pontoknak meg kell osztaniuk GPS koordinátáikat, hogy megjelenjenek a térképen.';
+
+ @override
+ String map_nodesCount(int count) {
+ return 'Csúcsok: $count';
+ }
+
+ @override
+ String map_pinsCount(int count) {
+ return 'Csapok: $count';
+ }
+
+ @override
+ String get map_chat => 'Csevegés';
+
+ @override
+ String get map_repeater => 'Ismétlő';
+
+ @override
+ String get map_room => 'szoba';
+
+ @override
+ String get map_sensor => 'Érzékelő';
+
+ @override
+ String get map_pinDm => 'Jel (DM)';
+
+ @override
+ String get map_pinPrivate => 'Titkos (privát)';
+
+ @override
+ String get map_pinPublic => 'Jelmez (nyilvános)';
+
+ @override
+ String get map_lastSeen => 'Utoljára látva';
+
+ @override
+ String get map_disconnectConfirm =>
+ 'Biztosan szeretné kiírni ezt a készüléket?';
+
+ @override
+ String get map_from => 'Attól';
+
+ @override
+ String get map_source => 'Forrás';
+
+ @override
+ String get map_flags => 'Zászló';
+
+ @override
+ String get map_shareMarkerHere => 'Osztja ezt a tartalmat itt';
+
+ @override
+ String get map_setAsMyLocation => 'Állítsa be a jelenlegi helyzetemként';
+
+ @override
+ String get map_pinLabel => 'Címkét ragasztani';
+
+ @override
+ String get map_label => 'Címke';
+
+ @override
+ String get map_pointOfInterest => 'Érdekes hely';
+
+ @override
+ String get map_sendToContact => 'Kapcsolatfelvételi űrlap';
+
+ @override
+ String get map_sendToChannel => 'Küldés a csatornán';
+
+ @override
+ String get map_noChannelsAvailable => 'Nincs elérhető csatorna.';
+
+ @override
+ String get map_publicLocationShare => 'Térköz, nyilvános hely';
+
+ @override
+ String map_publicLocationShareConfirm(String channelLabel) {
+ return 'Most egy helyszínt megosztasz a $channelLabel csatornán. Ez a csatorna nyilvános, és bárki, aki rendelkezik a PSK-val, megtekintheti.';
+ }
+
+ @override
+ String get map_connectToShareMarkers =>
+ 'Kapcsolódjon egy eszközhöz, hogy megoszthassa a vonalzókat.';
+
+ @override
+ String get map_filterNodes => 'Szűrési pontok';
+
+ @override
+ String get map_nodeTypes => 'Vonalak típusai';
+
+ @override
+ String get map_chatNodes => 'Csevegési pontok';
+
+ @override
+ String get map_repeaters => 'Újraküldők';
+
+ @override
+ String get map_otherNodes => 'Egyéb csomópontok';
+
+ @override
+ String get map_showOverlaps => 'Az ismétlő kulcsok ütköznek';
+
+ @override
+ String get map_keyPrefix => 'Kulcsfontosságú előtag';
+
+ @override
+ String get map_filterByKeyPrefix => 'Szűrj a kulcsos előtér szerint';
+
+ @override
+ String get map_publicKeyPrefix => 'Névfelhasználó kulc-prefix';
+
+ @override
+ String get map_markers => 'Jelölők';
+
+ @override
+ String get map_showSharedMarkers => 'Mutassa meg a közös jeleket';
+
+ @override
+ String get map_showGuessedLocations =>
+ 'Megjelenítsa a megjósolt csomópontok helyét';
+
+ @override
+ String get map_showDiscoveryContacts =>
+ 'Megjelenítse a Discovery-nál elérhet kontaktokat';
+
+ @override
+ String get map_guessedLocation => 'Tippolt hely';
+
+ @override
+ String get map_lastSeenTime => 'Utoljára megjelent idő';
+
+ @override
+ String get map_sharedPin => 'Gemeinsames PIN-kód';
+
+ @override
+ String get map_joinRoom => 'Csatlakozás a szobához';
+
+ @override
+ String get map_manageRepeater => 'Ellenőriző eszköz kezelése';
+
+ @override
+ String get map_tapToAdd =>
+ 'Nyomj meg a csomópontokhoz, hogy hozzáadd őket az útvonalhoz.';
+
+ @override
+ String get map_runTrace => 'Útvonal követés';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Visszaforduljon az eredeti úton.';
+
+ @override
+ String get map_removeLast => 'Törölj utolsó';
+
+ @override
+ String get map_pathTraceCancelled => 'Az útvonal követés megszakadt.';
+
+ @override
+ String get mapCache_title => 'Offline térkép tárolás';
+
+ @override
+ String get mapCache_selectAreaFirst =>
+ 'Válasszon egy területet, amelyet először cache-oljon.';
+
+ @override
+ String get mapCache_noTilesToDownload =>
+ 'Nincsenek letölthető tile-ok ebben a területben.';
+
+ @override
+ String get mapCache_downloadTilesTitle => 'Letöltsd a tile-okat';
+
+ @override
+ String mapCache_downloadTilesPrompt(int count) {
+ return 'Töltse le $count darab tile-t offline használatra?';
+ }
+
+ @override
+ String get mapCache_downloadAction => 'Letöltés';
+
+ @override
+ String mapCache_cachedTiles(int count) {
+ return 'Tárolt $count darab';
+ }
+
+ @override
+ String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
+ return 'Cached $downloaded tiles ($failed failed)';
+ }
+
+ @override
+ String get mapCache_clearOfflineCacheTitle => 'Tiszta offline tárhely';
+
+ @override
+ String get mapCache_clearOfflineCachePrompt =>
+ 'Távolítsa el az összes tárolt térképmegjelenítőt?';
+
+ @override
+ String get mapCache_offlineCacheCleared => 'A helyi memóriát töröltük.';
+
+ @override
+ String get mapCache_noAreaSelected => 'Nincs kiválasztott terület.';
+
+ @override
+ String get mapCache_cacheArea => 'Tároló terület';
+
+ @override
+ String get mapCache_useCurrentView => 'Használja a jelenlegi nézetet';
+
+ @override
+ String get mapCache_zoomRange => 'Zoom tartomány';
+
+ @override
+ String mapCache_estimatedTiles(int count) {
+ return 'Becsült kerámiák: $count';
+ }
+
+ @override
+ String mapCache_downloadedTiles(int completed, int total) {
+ return 'Letöltve $completed / $total';
+ }
+
+ @override
+ String get mapCache_downloadTilesButton => 'Letöltsd a tile-okat';
+
+ @override
+ String get mapCache_clearCacheButton => 'Ósztótt adatokat';
+
+ @override
+ String mapCache_failedDownloads(int count) {
+ return 'Sikertelen letöltések: $count';
+ }
+
+ @override
+ String mapCache_boundsLabel(
+ String north,
+ String south,
+ String east,
+ String west,
+ ) {
+ return 'N $north, S $south, E $east, W $west';
+ }
+
+ @override
+ String get time_justNow => 'Most';
+
+ @override
+ String time_minutesAgo(int minutes) {
+ return '$minutes perckel ezelőtt';
+ }
+
+ @override
+ String time_hoursAgo(int hours) {
+ return '$hours óva';
+ }
+
+ @override
+ String time_daysAgo(int days) {
+ return '${days}d ago';
+ }
+
+ @override
+ String get time_hour => 'óra';
+
+ @override
+ String get time_hours => 'órák';
+
+ @override
+ String get time_day => 'nap';
+
+ @override
+ String get time_days => 'napok';
+
+ @override
+ String get time_week => 'het';
+
+ @override
+ String get time_weeks => 'het, hetek';
+
+ @override
+ String get time_month => 'hónap';
+
+ @override
+ String get time_months => 'hónapok';
+
+ @override
+ String get time_minutes => 'percek';
+
+ @override
+ String get time_allTime => 'Bármely időpont';
+
+ @override
+ String get dialog_disconnect => 'Csatlakozást megszakasztani';
+
+ @override
+ String get dialog_disconnectConfirm =>
+ 'Biztosan szeretné kiírni ezt a készüléket?';
+
+ @override
+ String get login_repeaterLogin => 'Ismételt bejelentkezés';
+
+ @override
+ String get login_roomLogin => 'Szoba szerverbe való bejelentkezés';
+
+ @override
+ String get login_password => 'Jelszó';
+
+ @override
+ String get login_enterPassword => 'Adja meg a jelszót';
+
+ @override
+ String get login_savePassword => 'Mentse el a jelszót';
+
+ @override
+ String get login_savePasswordSubtitle =>
+ 'A jelszó biztonságosan tárolódik ezen a készüléken.';
+
+ @override
+ String get login_repeaterDescription =>
+ 'Adja meg a repeater (ismétítő) jelszót, hogy hozzáférhessen a beállításokhoz és az állapot információkhoz.';
+
+ @override
+ String get login_roomDescription =>
+ 'Adja meg a belépési kódot, hogy hozzáférhessen a beállításokhoz és az állapot információkhoz.';
+
+ @override
+ String get login_routing => 'Útvonal meghatározás';
+
+ @override
+ String get login_routingMode => 'Útvonal-kezelési mód';
+
+ @override
+ String get login_autoUseSavedPath =>
+ 'Automatikus (az eddigi útvonal használata)';
+
+ @override
+ String get login_forceFloodMode => 'Erőforrás-alapú áramlás mód';
+
+ @override
+ String get login_managePaths => 'Útvonalak kezelése';
+
+ @override
+ String get login_login => 'Bejelentkezés';
+
+ @override
+ String login_attempt(int current, int max) {
+ return 'Megpróbálás $current/$max-adik';
+ }
+
+ @override
+ String login_failed(String error) {
+ return 'Belépés sikertelen: $error';
+ }
+
+ @override
+ String get login_failedMessage =>
+ 'Belépés sikertelen. Vagy a jelszó helytelen, vagy a hálózati kapcsolat nem létesül.';
+
+ @override
+ String get common_reload => 'Újra töltés';
+
+ @override
+ String get common_clear => 'Egyértelmű';
+
+ @override
+ String path_currentPath(String path) {
+ return 'Jelenlegi útvonal: $path';
+ }
+
+ @override
+ String path_usingHopsPath(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'ugrások',
+ one: 'ugrás',
+ );
+ return '$count $_temp0 útvonal használata';
+ }
+
+ @override
+ String get path_enterCustomPath => 'Adja meg a saját elérési útvonalat';
+
+ @override
+ String get path_currentPathLabel => 'Jelenlegi útvonal';
+
+ @override
+ String get path_hexPrefixInstructions =>
+ 'Adja meg a 2 karakteres hexadecimális előtagokat minden lépéshez, tagolva kommával.';
+
+ @override
+ String get path_hexPrefixExample =>
+ 'Példa: A1, F2, 3C (minden csomó az első részét használja a nyilvános kulcsából)';
+
+ @override
+ String get path_labelHexPrefixes => 'Út (hex-prefixek)';
+
+ @override
+ String get path_helperMaxHops =>
+ 'A maximális hossz 64 karakter. Minden előző rész 2 hatos számjegyből áll (1 bájt).';
+
+ @override
+ String get path_selectFromContacts =>
+ 'Válasszon a kontaktlista elembek közül:';
+
+ @override
+ String get path_noRepeatersFound =>
+ 'Nincs megtalálva semmilyen ismétlődő vagy helyiség-szolgáltató szervert.';
+
+ @override
+ String get path_customPathsRequire =>
+ 'Az egyedi útvonalaknak szükségük van átjáró pontokra, amelyek képesek üzeneteket továbbítani.';
+
+ @override
+ String path_invalidHexPrefixes(String prefixes) {
+ return 'Érvénytelen hexadecimális előtagok: $prefixes';
+ }
+
+ @override
+ String get path_tooLong =>
+ 'Az út túl hosszú. A maximális engedélyezett lépések száma 64.';
+
+ @override
+ String get path_setPath => 'Útvonal meghatározása';
+
+ @override
+ String get repeater_management => 'Adatkapcsolás kezelése';
+
+ @override
+ String get room_management => 'Szoba-szerver kezelés';
+
+ @override
+ String get repeater_guest => 'Adatok a repeaterről';
+
+ @override
+ String get room_guest => 'Szoba szerver információk';
+
+ @override
+ String get repeater_managementTools => 'Menedzsmentes eszközök';
+
+ @override
+ String get repeater_guestTools => 'Vendégek számára elérhető eszközök';
+
+ @override
+ String get repeater_status => 'Állapot';
+
+ @override
+ String get repeater_statusSubtitle =>
+ 'Megtekintheted a repeater állapotát, statisztikáit és a környező eszközök adatait.';
+
+ @override
+ String get repeater_telemetry => 'Adatvisszaadás';
+
+ @override
+ String get repeater_telemetrySubtitle =>
+ 'Tekintsük a szenzorok és a rendszer állapotának adatát';
+
+ @override
+ String get repeater_cli => 'Parancssori felület (CLI)';
+
+ @override
+ String get repeater_cliSubtitle => 'Küldj parancsokat a repeaternek.';
+
+ @override
+ String get repeater_neighbors => 'Szomszédok';
+
+ @override
+ String get repeater_neighborsSubtitle =>
+ 'Tekintsük a nullás lépésű szomszédokat.';
+
+ @override
+ String get repeater_settings => 'Beállítások';
+
+ @override
+ String get repeater_settingsSubtitle => 'Állítsa be a repeater paramétereket';
+
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Óra szinkronizálás bejelentkezés után';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatikusan küldje el a \"óra szinkronizálás\" üzenetet a sikeres bejelentkezés után.';
+
+ @override
+ String get repeater_statusTitle => 'Adatkapcsolódás állapot';
+
+ @override
+ String get repeater_routingMode => 'Útvonal-kezelési mód';
+
+ @override
+ String get repeater_autoUseSavedPath =>
+ 'Automatikus (az eddigi útvonal használata)';
+
+ @override
+ String get repeater_forceFloodMode => 'Erőforrás-alapú áramlás mód';
+
+ @override
+ String get repeater_pathManagement => 'Útvonal-kezelés';
+
+ @override
+ String get repeater_refresh => 'Újrafriszol';
+
+ @override
+ String get repeater_statusRequestTimeout => 'Az állapotkérés időtúlt.';
+
+ @override
+ String repeater_errorLoadingStatus(String error) {
+ return 'Hiba a státusz betöltés közben: $error';
+ }
+
+ @override
+ String get repeater_systemInformation => 'Rendszerinformációk';
+
+ @override
+ String get repeater_battery => 'Akku';
+
+ @override
+ String get repeater_clockAtLogin => 'Óra (bejelentkezéskor)';
+
+ @override
+ String get repeater_uptime => 'A rendszer elérhetősége';
+
+ @override
+ String get repeater_queueLength => 'Várakozási sor hossza';
+
+ @override
+ String get repeater_debugFlags => 'Hibakeresési beállítások';
+
+ @override
+ String get repeater_radioStatistics => 'Rádió statisztika';
+
+ @override
+ String get repeater_lastRssi => 'Utolsó RSSI érték';
+
+ @override
+ String get repeater_lastSnr => 'Utolsó SNR';
+
+ @override
+ String get repeater_noiseFloor => 'Háttérzaj szint';
+
+ @override
+ String get repeater_txAirtime => 'TX Airtime';
+
+ @override
+ String get repeater_rxAirtime => 'RX Airtime';
+
+ @override
+ String get repeater_packetStatistics => 'Csomagok statisztikája';
+
+ @override
+ String get repeater_sent => 'Elküldve';
+
+ @override
+ String get repeater_received => 'Megérkezett';
+
+ @override
+ String get repeater_duplicates => 'Duplák';
+
+ @override
+ String repeater_daysHoursMinsSecs(
+ int days,
+ int hours,
+ int minutes,
+ int seconds,
+ ) {
+ return '$days days ${hours}h ${minutes}m ${seconds}s';
+ }
+
+ @override
+ String repeater_packetTxTotal(int total, String flood, String direct) {
+ return 'Összesen: $total, Árvíz: $flood, Közvetlen: $direct';
+ }
+
+ @override
+ String repeater_packetRxTotal(int total, String flood, String direct) {
+ return 'Összesen: $total, Árvíz: $flood, Közvetlen: $direct';
+ }
+
+ @override
+ String repeater_duplicatesFloodDirect(String flood, String direct) {
+ return 'Áradás: $flood, Közvetlen: $direct';
+ }
+
+ @override
+ String repeater_duplicatesTotal(int total) {
+ return 'Összesen: $total';
+ }
+
+ @override
+ String get repeater_settingsTitle => 'Adatátvisszaadási beállítások';
+
+ @override
+ String get repeater_basicSettings => 'Alapbeállítások';
+
+ @override
+ String get repeater_repeaterName => 'Adóállomás neve';
+
+ @override
+ String get repeater_repeaterNameHelper => 'Ez a repeater neve';
+
+ @override
+ String get repeater_adminPassword => 'Adminisztrátori jelszó';
+
+ @override
+ String get repeater_adminPasswordHelper => 'Teljes jogosultságú jelszó';
+
+ @override
+ String get repeater_guestPassword => 'Vendég felhasználói név/jelszó';
+
+ @override
+ String get repeater_guestPasswordHelper =>
+ 'Csak olvasási jogosítást biztosító jelszó';
+
+ @override
+ String get repeater_radioSettings => 'Rádióbeállítások';
+
+ @override
+ String get repeater_frequencyMhz => 'Frekvencia (MHz)';
+
+ @override
+ String get repeater_frequencyHelper => '300–2500 MHz';
+
+ @override
+ String get repeater_txPower => 'TX Power';
+
+ @override
+ String get repeater_txPowerHelper => '1-30 dBm';
+
+ @override
+ String get repeater_bandwidth => 'Adatkapacitás';
+
+ @override
+ String get repeater_spreadingFactor => 'Terjesztési tényező';
+
+ @override
+ String get repeater_codingRate => 'Kódolási sebesség';
+
+ @override
+ String get repeater_locationSettings => 'Helyszínbeállítások';
+
+ @override
+ String get repeater_latitude => 'Nyugat–keleti szélesség';
+
+ @override
+ String get repeater_latitudeHelper => 'Desztes fokok (pl. 37,7749)';
+
+ @override
+ String get repeater_longitude => 'hosszúság';
+
+ @override
+ String get repeater_longitudeHelper => 'Desztes fokok (pl. -122.4194)';
+
+ @override
+ String get repeater_features => 'Jellemzők';
+
+ @override
+ String get repeater_packetForwarding => 'Csomagok továbbítás';
+
+ @override
+ String get repeater_packetForwardingSubtitle =>
+ 'Engedje, hogy a repeater továbbítsa a csomagokat.';
+
+ @override
+ String get repeater_guestAccess => 'Vendégek számára elérhető';
+
+ @override
+ String get repeater_guestAccessSubtitle =>
+ 'Engedje meg a vendégek számára, hogy csak olvassák a tartalmat';
+
+ @override
+ String get repeater_privacyMode => 'Adatvédelem mód';
+
+ @override
+ String get repeater_privacyModeSubtitle =>
+ 'Elrejtse a nevét/a helyszínt az űrlapon';
+
+ @override
+ String get repeater_advertisementSettings => 'Reklámbeállítások';
+
+ @override
+ String get repeater_localAdvertInterval => 'Helyi hirdetés időtartama';
+
+ @override
+ String repeater_localAdvertIntervalMinutes(int minutes) {
+ return '$minutes perc';
+ }
+
+ @override
+ String get repeater_floodAdvertInterval => 'Vízosztály-hirdetés időtartama';
+
+ @override
+ String repeater_floodAdvertIntervalHours(int hours) {
+ return '$hours óra';
+ }
+
+ @override
+ String get repeater_encryptedAdvertInterval => 'Kódolt hirdetés-szünet';
+
+ @override
+ String get repeater_dangerZone => 'Veszélyzóna';
+
+ @override
+ String get repeater_rebootRepeater => 'Újraindítás';
+
+ @override
+ String get repeater_rebootRepeaterSubtitle => 'Indítsa újra a repeater-t.';
+
+ @override
+ String get repeater_rebootRepeaterConfirm =>
+ 'Biztosan szeretné újraindítani ezt a repeatert?';
+
+ @override
+ String get repeater_regenerateIdentityKey =>
+ 'Újra generálja az azonosító kulcsot';
+
+ @override
+ String get repeater_regenerateIdentityKeySubtitle =>
+ 'Új nyilvános/személyes kulcs-párt generáljon';
+
+ @override
+ String get repeater_regenerateIdentityKeyConfirm =>
+ 'Ez új azonosítást fog létrehozni a repeater számára. Folytatni?';
+
+ @override
+ String get repeater_eraseFileSystem => 'Törölje a fájlrendszert';
+
+ @override
+ String get repeater_eraseFileSystemSubtitle =>
+ 'Formázza a duplázó fájlrendszert.';
+
+ @override
+ String get repeater_eraseFileSystemConfirm =>
+ 'FIGYELEM: Ez törli az összes adatot a repeater-en. Ez nem visszafordítható!';
+
+ @override
+ String get repeater_eraseSerialOnly =>
+ 'Az Erase funkció csak a soros konzolon érhető el.';
+
+ @override
+ String repeater_commandSent(String command) {
+ return 'Parancs elküldve: $command';
+ }
+
+ @override
+ String repeater_errorSendingCommand(String error) {
+ return 'Hibás parancs küldés: $error';
+ }
+
+ @override
+ String get repeater_confirm => 'Beküldve';
+
+ @override
+ String get repeater_settingsSaved => 'Beállítások sikeresen mentve';
+
+ @override
+ String repeater_errorSavingSettings(String error) {
+ return 'Hibás beállítások mentése: $error';
+ }
+
+ @override
+ String get repeater_refreshBasicSettings => 'Visszaállítás az alapértékekre';
+
+ @override
+ String get repeater_refreshRadioSettings => 'Frissítse a rádió beállításait';
+
+ @override
+ String get repeater_refreshTxPower => 'Újraindítás TX-támogatással';
+
+ @override
+ String get repeater_refreshLocationSettings =>
+ 'Újraindítás helyszín beállításokkal';
+
+ @override
+ String get repeater_refreshPacketForwarding =>
+ 'Csomagok továbbításának frissítése';
+
+ @override
+ String get repeater_refreshGuestAccess => 'Újraindítás vendégHozzáférés';
+
+ @override
+ String get repeater_refreshPrivacyMode =>
+ 'Visszaállítás a magánéletvédő módra';
+
+ @override
+ String get repeater_refreshAdvertisementSettings =>
+ 'Újraindítás hirdetés beállítások';
+
+ @override
+ String repeater_refreshed(String label) {
+ return '$label frissítve';
+ }
+
+ @override
+ String repeater_errorRefreshing(String label) {
+ return 'Hiba a $label frissítés közben';
+ }
+
+ @override
+ String get repeater_cliTitle => 'CLI (parancssori felület)';
+
+ @override
+ String get repeater_debugNextCommand => 'Hibakeresés, következő parancs';
+
+ @override
+ String get repeater_commandHelp => 'Segítség';
+
+ @override
+ String get repeater_clearHistory => 'Egyértelmű történet';
+
+ @override
+ String get repeater_noCommandsSent => 'Még egyik parancsot sem küldtünk.';
+
+ @override
+ String get repeater_typeCommandOrUseQuick =>
+ 'Írja be a parancsot alább, vagy használja a gyors parancsokat.';
+
+ @override
+ String get repeater_enterCommandHint => 'Írja be a parancsot...';
+
+ @override
+ String get repeater_previousCommand => 'Előző parancs';
+
+ @override
+ String get repeater_nextCommand => 'Következő parancs';
+
+ @override
+ String get repeater_enterCommandFirst => 'Add meg először egy parancsot';
+
+ @override
+ String get repeater_cliCommandFrameTitle => 'CLI parancssor felépítése';
+
+ @override
+ String repeater_cliCommandError(String error) {
+ return 'Hiba: $error';
+ }
+
+ @override
+ String get repeater_cliQuickGetName => 'Kapcsold össze a nevet';
+
+ @override
+ String get repeater_cliQuickGetRadio => 'Szerezd a rádiót';
+
+ @override
+ String get repeater_cliQuickGetTx => 'Szerezd a TX-t';
+
+ @override
+ String get repeater_cliQuickNeighbors => 'Szomszédok';
+
+ @override
+ String get repeater_cliQuickVersion => 'Verzió';
+
+ @override
+ String get repeater_cliQuickAdvertise => 'Hirdetés';
+
+ @override
+ String get repeater_cliQuickClock => 'óra';
+
+ @override
+ String get repeater_cliQuickClockSync => 'Óra szinkronizálás';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Fedezd fel a szomszédokat';
+
+ @override
+ String get repeater_cliHelpAdvert => 'Elküldi egy hirdetési csomagot';
+
+ @override
+ String get repeater_cliHelpReboot =>
+ 'Újraindítja a készüléket. (Kérjük, vegye figyelembe, hogy valószínűleg \"Időhiba\" üzenetet fog kapni, ami normális)';
+
+ @override
+ String get repeater_cliHelpClock =>
+ 'A jelenlegi időt mutatja az egyes eszközök karórája alapján.';
+
+ @override
+ String get repeater_cliHelpPassword =>
+ 'Új adminisztrációs jelszót állít be a eszköz számára.';
+
+ @override
+ String get repeater_cliHelpVersion =>
+ 'Megjeleníti a készülék verzióját és a szoftver verziószámát.';
+
+ @override
+ String get repeater_cliHelpClearStats =>
+ 'Visszaállítja a különböző statisztikai mérőszámokat a nullára.';
+
+ @override
+ String get repeater_cliHelpSetAf => 'Beállítja az idő-szabályozási tényezőt.';
+
+ @override
+ String get repeater_cliHelpSetTx =>
+ 'Beállítja a LoRa átviteli teljesítményt dBm-ben (a rendszer újraindításával alkalmazható).';
+
+ @override
+ String get repeater_cliHelpSetRepeat =>
+ 'Engedélyezi vagy tiltja meg a repeater szerepet ezen a csomón.';
+
+ @override
+ String get repeater_cliHelpSetAllowReadOnly =>
+ '(Szoba szerver) Ha \"igen\", akkor üres jelszóval történő bejelentkezés engedélyezett lesz, de nem lehet üzeneteket küldeni a szobában. (Csak olvasási funkció)';
+
+ @override
+ String get repeater_cliHelpSetFloodMax =>
+ 'Beállítja a bejövő adatcsomagok maximális számát (ha ez a érték nagyobb vagy egyenlő a maximális értékkel, a csomag nem továbbítódik).';
+
+ @override
+ String get repeater_cliHelpSetIntThresh =>
+ 'Beállítja az interferencia határértéket (dB-ben). Az alapérték 14. Ha 0-ra állítja, kiküntheti a csatornák közötti interferencia detektálást.';
+
+ @override
+ String get repeater_cliHelpSetAgcResetInterval =>
+ 'Beállítja az intervallumot, amely a \"Automatikus gain\" szabályozó újraindításához szükséges. Beállítás értéke 0, ha a funkciót le kell tiltani.';
+
+ @override
+ String get repeater_cliHelpSetMultiAcks =>
+ 'Engedélyezi vagy kikapcsolja a „dupla visszaigazolás” funkciót.';
+
+ @override
+ String get repeater_cliHelpSetAdvertInterval =>
+ 'Beállítja az időzítő intervallumot percenként, hogy egy helyi (nincs átjáró) hirdetési csomagot küldjen. Beállítás értéke 0, ha a funkciót le szeretné tiltani.';
+
+ @override
+ String get repeater_cliHelpSetFloodAdvertInterval =>
+ 'Beállítja az időzítő intervallumot órában, hogy egy \"áramló\" hirdetési üzenetet küldjön. Beállítás értéke 0, ha a funkciót kikapcsolni kell.';
+
+ @override
+ String get repeater_cliHelpSetGuestPassword =>
+ 'Beállítja/frissíti a vendég felhasználói fiókot. (Ez lehetővé teszi a visszatérő felhasználók számára, hogy a \"Statistika lekérdezése\" kérést elküldjék)';
+
+ @override
+ String get repeater_cliHelpSetName => 'Megadja az űrlap neve.';
+
+ @override
+ String get repeater_cliHelpSetLat =>
+ 'Beállítja az hirdetés térképen megjelenő pont koordinátájának (tizedes fokokban) a latitude-ját.';
+
+ @override
+ String get repeater_cliHelpSetLon =>
+ 'Beállítja az hirdetés térképen megjelenő hosszúság koordinátát (tizedes fokokban).';
+
+ @override
+ String get repeater_cliHelpSetRadio =>
+ 'Teljesen új rádióparamétereket állít be, és azokat a beállításokba menti. Az alkalmazásához \"újraindítás\" parancs szükséges.';
+
+ @override
+ String get repeater_cliHelpSetRxDelay =>
+ 'Beállítások (kísérleti): Alapérték (legalább 1 értékre kell állítani, hogy hatás legyen), amely alapján a fogadott csomagokhoz enyhe késést alkalmazunk, a jelet ereje/pontszám alapján. 0-ra állítva a funkciót lekapcsoljuk.';
+
+ @override
+ String get repeater_cliHelpSetTxDelay =>
+ 'Beállítja egy tényezőt, amely a légköri idővel szorozva, egy áramlás-üzem módú csomaghoz, valamint egy véletlenszerű slot-rendszerhez, hogy késleltesse a továbbítását. (az ütközések valószínűségének csökkentése érdekében)';
+
+ @override
+ String get repeater_cliHelpSetDirectTxDelay =>
+ 'Hasonló a txdelay-hez, de ebben az esetben egy véletlenszerű késést alkalmazunk a közvetlen módú csomagok továbbításakor.';
+
+ @override
+ String get repeater_cliHelpSetBridgeEnabled =>
+ 'Engedélyez/Tiltás a híd funkciójának.';
+
+ @override
+ String get repeater_cliHelpSetBridgeDelay =>
+ 'Állíts be egy késleztatást a csomagok újbóli továbbításakor.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSource =>
+ 'Döntse el, hogy a híd fogadott vagy elküldött csomagokat fogja-e továbbítani.';
+
+ @override
+ String get repeater_cliHelpSetBridgeBaud =>
+ 'Állítsa be a soros kommunikáció sebességét az RS232 hídok számára.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSecret =>
+ 'Állítsa be a titkos kapcsolatot az ESPNOW hídokhoz.';
+
+ @override
+ String get repeater_cliHelpSetAdcMultiplier =>
+ 'Lehetővé teszi a felhasználónak, hogy egyedi tényezőt állíts be a riportolt akkumulátor feszültségének módosításához (ez csak bizonyos alkatrészeken támogatott).';
+
+ @override
+ String get repeater_cliHelpTempRadio =>
+ 'Időjárás szerinti rádióparamétereket állít be a megadott időtartamra, majd visszaállítja az eredeti beállításokat. (Nem menti a beállításokat a beállítások részben).';
+
+ @override
+ String get repeater_cliHelpSetPerm =>
+ 'A ACL-t módosítja. Ha a \"permissions\" érték 0, akkor eltávolítja a megfelelő bejegyzést (a pubkey előtag alapján). Új bejegyzést hoz létre, ha a pubkey-hex teljes hossza, és jelenleg nem szerepel az ACL-ben. A bejegyzést frissíti a megfelelő pubkey előtag alapján. A engedélyek különbözőek a különböző firmware szerepek között, de az alsó 2 bit a következő értékeket képviseli: 0 (Vendég), 1 (Csak olvasás), 2 (Olvasás és írás), 3 (Adminisztrátor)';
+
+ @override
+ String get repeater_cliHelpGetBridgeType =>
+ 'Kapcsolatok: hid típusú, RS232, ESPNOW';
+
+ @override
+ String get repeater_cliHelpLogStart =>
+ 'Elindítja a csomagok naplózását a fájlrendszerbe.';
+
+ @override
+ String get repeater_cliHelpLogStop =>
+ 'Megállítja a csomagok naplózását a fájlrendszerbe.';
+
+ @override
+ String get repeater_cliHelpLogErase =>
+ 'Törli a fájlrendszerből a csomagok log-fájljait.';
+
+ @override
+ String get repeater_cliHelpNeighbors =>
+ 'Mutat egy listát, amely tartalmazza a más repeater-ek által hallott adatok listáját, amelyek 0-hop hirdetések révén érhetők el. Minden sor az alábbi formát követi: id-prefix-hex:timestamp:snr-times-4';
+
+ @override
+ String get repeater_cliHelpNeighborRemove =>
+ 'Törli az első, a megadott kulcs-prefix (hexadecimális formában) alapján megegyező bejegyzést a szomszédok listájából.';
+
+ @override
+ String get repeater_cliHelpRegion =>
+ '(sorozat) Lista az összes meghatározott területet és a jelenlegi árvízvédelmi engedélyeket.';
+
+ @override
+ String get repeater_cliHelpRegionLoad =>
+ 'FIGYELEM: ez egy speciális, több parancsot tartalmazó futtatás. Minden következő parancs egy területtel kapcsolatos, amely egyenletes szóközökkel (a szülő-gyermek kapcsolatot jelző) megkülönböztethető. A parancs végrehajtása egy üres sor/parancs küldésével történik.';
+
+ @override
+ String get repeater_cliHelpRegionGet =>
+ 'Keresések egy adott név előtérrel (vagy \"*\" globális hatókörre). Válasz: \"-> region-név (szülő-név) \'F\'\"';
+
+ @override
+ String get repeater_cliHelpRegionPut =>
+ 'Hozzáad vagy frissíti egy régió definíciót megadott néven.';
+
+ @override
+ String get repeater_cliHelpRegionRemove =>
+ 'Eltávolítja a megadott nevet használó régió-definíciót. (pontosan meg kell egyeznie, és nem lehet gyermekrégiója)';
+
+ @override
+ String get repeater_cliHelpRegionAllowf =>
+ 'Beállítja a megadott területre vonatkozó \"víz\" jogosultságot. (A globális/régi beállítások esetén a \"*\" jelölő)';
+
+ @override
+ String get repeater_cliHelpRegionDenyf =>
+ 'Eltávolítja a megadott területre vonatkozó \"F\"lood (víz) engedélyt. (FIGYELEM: jelenleg nem javasolt ezt a globális/régi verzióban használni!!)';
+
+ @override
+ String get repeater_cliHelpRegionHome =>
+ 'Visszaállítja a jelenlegi „otthoni” régiót. (Ez a beállítás még nem került alkalmazásra, csak jövőbeli használatra fenyelve)';
+
+ @override
+ String get repeater_cliHelpRegionHomeSet => 'Beállítja a \"házi\" régiót.';
+
+ @override
+ String get repeater_cliHelpRegionSave =>
+ 'Megőrzi a régió listát/térképet a tárolóban.';
+
+ @override
+ String get repeater_cliHelpGps =>
+ 'Megadja a GPS állapotát. Ha a GPS kikapcsolva van, akkor csak \"ki\" választot ad, ha be van, akkor \"be\", \"állapot\", \"pozíció\", \"satellitok száma\" értékeket ad.';
+
+ @override
+ String get repeater_cliHelpGpsOnOff => 'Engedi a GPS működés állapotát.';
+
+ @override
+ String get repeater_cliHelpGpsSync =>
+ 'A hálózati időt az GPS óra időjével szinkronizálja.';
+
+ @override
+ String get repeater_cliHelpGpsSetLoc =>
+ 'Beállítja a węsz pozícióját GPS koordináták alapján, és menti a beállításokat.';
+
+ @override
+ String get repeater_cliHelpGpsAdvert =>
+ 'Adja meg a hirdetés konfigurációjának helyszín-információját:\n- none: ne tartalmazza a helyszínt a hirdetésekben\n- share: megosztja a GPS-helyszínt (SensorManager-ből)\n- prefs: hirdeti a beállításokban tárolt helyszínt';
+
+ @override
+ String get repeater_cliHelpGpsAdvertSet =>
+ 'Beállítja a hirdetés helyszín-specifikus beállításait.';
+
+ @override
+ String get repeater_commandsListTitle => 'Parancsok listája';
+
+ @override
+ String get repeater_commandsListNote =>
+ 'FIGYELEM: a különböző \"set ...\" parancsok mellett létezik egy \"get ...\" parancs is.';
+
+ @override
+ String get repeater_general => 'Általános';
+
+ @override
+ String get repeater_settingsCategory => 'Beállítások';
+
+ @override
+ String get repeater_bridge => 'Híd';
+
+ @override
+ String get repeater_logging => 'Naplózás';
+
+ @override
+ String get repeater_neighborsRepeaterOnly =>
+ 'Szomszédok (Csak ismétlő funkció)';
+
+ @override
+ String get repeater_regionManagementRepeaterOnly =>
+ 'Regionális menedzsment (Csak egyirányú kommunikáció)';
+
+ @override
+ String get repeater_regionNote =>
+ 'Region-specifikus parancsokat vezettek be a régiók definiálására és a hozzájuk tartozó engedélyek kezelésére.';
+
+ @override
+ String get repeater_gpsManagement => 'GPS-vezérlés';
+
+ @override
+ String get repeater_gpsNote =>
+ 'Az GPS-al kapcsolatos funkciók lehetővé teszik a helyszín-személyesítéssel kapcsolatos feladatok kezelését.';
+
+ @override
+ String get telemetry_receivedData => 'Kapott adatokat a szenzorokról';
+
+ @override
+ String get telemetry_requestTimeout => 'Az adatkapcsolati kérés sikertelen.';
+
+ @override
+ String telemetry_errorLoading(String error) {
+ return 'Hiba az adatok begyűjtésében: $error';
+ }
+
+ @override
+ String get telemetry_noData => 'Nincsenek elérhető telemetriadatok.';
+
+ @override
+ String telemetry_channelTitle(int channel) {
+ return '$channel csatorna';
+ }
+
+ @override
+ String get telemetry_batteryLabel => 'Akku';
+
+ @override
+ String get telemetry_voltageLabel => 'Feszültség';
+
+ @override
+ String get telemetry_mcuTemperatureLabel => 'MCU hőmérséklet';
+
+ @override
+ String get telemetry_temperatureLabel => 'Hőmérséklet';
+
+ @override
+ String get telemetry_currentLabel => 'Jelenlegi';
+
+ @override
+ String telemetry_batteryValue(int percent, String volts) {
+ return '$percent% / ${volts}V';
+ }
+
+ @override
+ String telemetry_voltageValue(String volts) {
+ return '${volts}V';
+ }
+
+ @override
+ String telemetry_currentValue(String amps) {
+ return '${amps}A';
+ }
+
+ @override
+ String telemetry_temperatureValue(String celsius, String fahrenheit) {
+ return '$celsius °C / $fahrenheit °F';
+ }
+
+ @override
+ String get neighbors_receivedData => 'Kapott szomszédok adatait';
+
+ @override
+ String get neighbors_requestTimedOut =>
+ 'A szomszédok kérik, hogy tiltsák le a kamerát.';
+
+ @override
+ String neighbors_errorLoading(String error) {
+ return 'Hiba a szomszédok betöltésében: $error';
+ }
+
+ @override
+ String get neighbors_repeatersNeighbors => 'Ismétlő eszközök, szomszédok';
+
+ @override
+ String get neighbors_noData => 'Nincsenek elérhető szomszédokról adatok.';
+
+ @override
+ String neighbors_unknownContact(String pubkey) {
+ return 'Tudatlan $pubkey';
+ }
+
+ @override
+ String neighbors_heardAgo(String time) {
+ return 'Értsd: $time sitten';
+ }
+
+ @override
+ String get channelPath_title => 'Csomagok útvonala';
+
+ @override
+ String get channelPath_viewMap => 'Megtekinthető térkép';
+
+ @override
+ String get channelPath_otherObservedPaths => 'Egyéb megfigyelt utak';
+
+ @override
+ String get channelPath_repeaterHops => 'Adat továbbító lépések';
+
+ @override
+ String get channelPath_noHopDetails =>
+ 'Ez a csomag nem tartalmaz részletes információkat a \"hop\" (vagy más hasonló) szót használó kifejezésekről.';
+
+ @override
+ String get channelPath_messageDetails => 'Üzenet részletei';
+
+ @override
+ String get channelPath_senderLabel => 'Megküldő';
+
+ @override
+ String get channelPath_timeLabel => 'Idő';
+
+ @override
+ String get channelPath_repeatsLabel => 'Ismétli';
+
+ @override
+ String channelPath_pathLabel(int index) {
+ return 'Útvonal $index';
+ }
+
+ @override
+ String get channelPath_observedLabel => 'Megfigyelt';
+
+ @override
+ String channelPath_observedPathTitle(int index, String hops) {
+ return 'Megfigyelt útvonal: $index • $hops';
+ }
+
+ @override
+ String get channelPath_noLocationData => 'Nincs helyszínadat.';
+
+ @override
+ String channelPath_timeWithDate(int day, int month, String time) {
+ return '$day/$month $time';
+ }
+
+ @override
+ String channelPath_timeOnly(String time) {
+ return '$time';
+ }
+
+ @override
+ String get channelPath_unknownPath => 'Megfejt';
+
+ @override
+ String get channelPath_floodPath => 'Árvíz';
+
+ @override
+ String get channelPath_directPath => 'Közvetlen';
+
+ @override
+ String channelPath_observedZeroOf(int total) {
+ return '0-ból $total';
+ }
+
+ @override
+ String channelPath_observedSomeOf(int observed, int total) {
+ return '$observed of $total hops';
+ }
+
+ @override
+ String get channelPath_mapTitle => 'Útvonal térkép';
+
+ @override
+ String get channelPath_noRepeaterLocations =>
+ 'Ez a útvonal nem támogat repeater-t.';
+
+ @override
+ String channelPath_primaryPath(int index) {
+ return 'Útvonal $index (Elsődleges)';
+ }
+
+ @override
+ String get channelPath_pathLabelTitle => 'Út';
+
+ @override
+ String get channelPath_observedPathHeader => 'Megfigyelt útvonal';
+
+ @override
+ String channelPath_selectedPathLabel(String label, String prefixes) {
+ return '$label • $prefixes';
+ }
+
+ @override
+ String get channelPath_noHopDetailsAvailable =>
+ 'Ez a csomag nem tartalmaz részletes információkat a szállításhoz.';
+
+ @override
+ String get channelPath_unknownRepeater => 'Tudatlan erősítő';
+
+ @override
+ String get community_title => 'Helyi közösség';
+
+ @override
+ String get community_create => 'Teremtsd meg a közösséget';
+
+ @override
+ String get community_createDesc =>
+ 'Légyon létre egy új közösséget, és osszák meg QR-kód segítségével.';
+
+ @override
+ String get community_join => 'Csatlakozjon';
+
+ @override
+ String get community_joinTitle => 'Csatlakozzon a közösséghez';
+
+ @override
+ String community_joinConfirmation(String name) {
+ return 'Szeretne csatlakozni a közösséghez, $name?';
+ }
+
+ @override
+ String get community_scanQr => 'QR-kód olvasó a közösség számára';
+
+ @override
+ String get community_scanInstructions =>
+ 'Fordítsa a kamerát egy közösségi QR-kód irányába.';
+
+ @override
+ String get community_showQr => 'Megjelenítse a QR-kódot';
+
+ @override
+ String get community_publicChannel => 'Összetartó, közösségi';
+
+ @override
+ String get community_hashtagChannel => 'Helyi hashtaget';
+
+ @override
+ String get community_name => 'Helyi közösség neve';
+
+ @override
+ String get community_enterName => 'Kérjük, a közösség nevét írja be.';
+
+ @override
+ String community_created(String name) {
+ return 'A \"$name\" nevű közösség létrehozva';
+ }
+
+ @override
+ String community_joined(String name) {
+ return 'Csatlakozott a $name közösséghez';
+ }
+
+ @override
+ String get community_qrTitle => 'Osszpontosítás a közösségben';
+
+ @override
+ String community_qrInstructions(String name) {
+ return 'Scanned this QR-kódot, hogy csatlakozhat a $name csoporthoz.';
+ }
+
+ @override
+ String get community_hashtagPrivacyHint =>
+ 'A közösségi hashtagekhez tartozó csatornák csak a közösség tagjai számára érhetők el.';
+
+ @override
+ String get community_invalidQrCode => 'Érvénytelen közösségi QR-kód';
+
+ @override
+ String get community_alreadyMember => 'Már tag vagy';
+
+ @override
+ String community_alreadyMemberMessage(String name) {
+ return 'Már tagja $name-nek.';
+ }
+
+ @override
+ String get community_addPublicChannel =>
+ 'Hozzon létre egy közösségi nyilvános csatornát';
+
+ @override
+ String get community_addPublicChannelHint =>
+ 'Automatikusan hozzon létre ezt a csatornát a közösség számára.';
+
+ @override
+ String get community_noCommunities => 'Még egyik közösség sem csatlakozott.';
+
+ @override
+ String get community_scanOrCreate =>
+ 'Scelle egy QR-kódot, vagy hozzon létre egy közösséget, hogy elinduljon.';
+
+ @override
+ String get community_manageCommunities => 'Közösségek kezelése';
+
+ @override
+ String get community_delete => 'Hagyományos közösségi élet';
+
+ @override
+ String community_deleteConfirm(String name) {
+ return 'Hagyom $name-et?';
+ }
+
+ @override
+ String community_deleteChannelsWarning(int count) {
+ return 'Ezem törli is $count csatornát és a hozzá tartozó üzeneteket.';
+ }
+
+ @override
+ String community_deleted(String name) {
+ return 'A közösség, amely $name';
+ }
+
+ @override
+ String get community_regenerateSecret => 'Titkos visszaállítás';
+
+ @override
+ String community_regenerateSecretConfirm(String name) {
+ return 'Újra kell generálni a titkos kulcsot $name számára? Minden tagnak be kell szkennelnie az új QR-kódot, hogy továbbra is kommunikálhasson.';
+ }
+
+ @override
+ String get community_regenerate => 'Újraalakítás';
+
+ @override
+ String community_secretRegenerated(String name) {
+ return 'Titkos kulcs megújult $name számára.';
+ }
+
+ @override
+ String get community_updateSecret => 'Frissítési titok';
+
+ @override
+ String community_secretUpdated(String name) {
+ return 'Titkos információ frissítve $name számára';
+ }
+
+ @override
+ String community_scanToUpdateSecret(String name) {
+ return 'Scanned a új QR-kódot, hogy frissítsük a $name számára megőrzött titkos információt.';
+ }
+
+ @override
+ String get community_addHashtagChannel => 'Adjon egy közösségi hashtaget';
+
+ @override
+ String get community_addHashtagChannelDesc =>
+ 'Hozz létre egy hashtage-os csatornát ennek a közösségnek';
+
+ @override
+ String get community_selectCommunity => 'Válasszon közösséget';
+
+ @override
+ String get community_regularHashtag => 'Rendszeres hashtag';
+
+ @override
+ String get community_regularHashtagDesc =>
+ 'Önmagas szintű hashtaget (bárki csatlakozhat)';
+
+ @override
+ String get community_communityHashtag => 'Helyi hashtaget';
+
+ @override
+ String get community_communityHashtagDesc => 'Csak a közösség tagjai számára';
+
+ @override
+ String community_forCommunity(String name) {
+ return '$name számára';
+ }
+
+ @override
+ String get listFilter_tooltip => 'Szűrés és rendezés';
+
+ @override
+ String get listFilter_sortBy => 'Szűrés';
+
+ @override
+ String get listFilter_latestMessages => 'Legfrissebb üzenetek';
+
+ @override
+ String get listFilter_heardRecently => 'Úgy hallottam, hogy...';
+
+ @override
+ String get listFilter_az => 'A-Z';
+
+ @override
+ String get listFilter_filters => 'Szűrők';
+
+ @override
+ String get listFilter_all => 'Mind';
+
+ @override
+ String get listFilter_favorites => 'Kedvencek';
+
+ @override
+ String get listFilter_addToFavorites => 'Megerősítés kívánságlistára';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Törölj a kedvencekből';
+
+ @override
+ String get listFilter_users => 'Felhasználók';
+
+ @override
+ String get listFilter_repeaters => 'Újraküldők';
+
+ @override
+ String get listFilter_roomServers => 'Szoba-szolgálatok';
+
+ @override
+ String get listFilter_unreadOnly => 'Csak olvasatlan';
+
+ @override
+ String get listFilter_newGroup => 'Új csoport';
+
+ @override
+ String get pathTrace_you => 'Te';
+
+ @override
+ String get pathTrace_failed => 'A útvonal követése sikertelen.';
+
+ @override
+ String get pathTrace_notAvailable =>
+ 'Az útvonal követési funkció nem elérhető.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Út mentesség frissítése.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Egy vagy több búzavirág hiányozik a helyszínéről!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Egyértelmű út.';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Válassza ki a kezdő és a végpontokat a LOS-hoz.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'A látószög ellenőrzése sikertelen: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Teljesen tisztázzuk az összes pontot';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Használja a LOS-t, hogy megtekinthesse a magasságkülönbségek diagramját.';
+
+ @override
+ String get losMenuTitle => 'LOS menü';
+
+ @override
+ String get losMenuSubtitle =>
+ 'A térképen található pontok kiválasztására vagy a térképen hosszúra nyomva, hogy egyedi pontokat definiálhassunk.';
+
+ @override
+ String get losShowDisplayNodes => 'Megjelenítsen a megjelenítési egységeket';
+
+ @override
+ String get losCustomPoints => 'Egyedi pontok';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Egyedi $index';
+ }
+
+ @override
+ String get losPointA => 'A pont A';
+
+ @override
+ String get losPointB => 'Pont B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenna A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenna B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Futtass a LOS-on';
+
+ @override
+ String get losNoElevationData => 'Nincsenek emelkedési adatok.';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, clear LOS, min clearance $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, amelyet $obstruction akadályoz meg $heightUnit-ban';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: ellenőrzés...';
+
+ @override
+ String get losStatusNoData => 'LOS: nincs adat';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total tisztított, $blocked blokkolt, $unknown ismeretlen';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Az alábbi minták esetében nem áll rendelkezésre magasságadat.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Hibás vagy hiányos táblázatok a LOS (Loss of Signal) számításához.';
+
+ @override
+ String get losRenameCustomPoint => 'Állítsa meg a saját pont nevét';
+
+ @override
+ String get losPointName => 'Pont neve';
+
+ @override
+ String get losShowPanelTooltip => 'Megjelenítse a LOS paneelt';
+
+ @override
+ String get losHidePanelTooltip => 'Rejtse el a LOS paneelt';
+
+ @override
+ String get losElevationAttribution =>
+ 'Magasságadatok: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Radio Horizont';
+
+ @override
+ String get losLegendLosBeam => 'LOS jelzés';
+
+ @override
+ String get losLegendTerrain => 'Terület';
+
+ @override
+ String get losFrequencyLabel => 'Hatósság';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Lásd a számítás részleteit';
+
+ @override
+ String get losFrequencyDialogTitle =>
+ 'A rádióhullámok hatótávolságának kiszámítása';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'A $baselineK értékből kezdve, $baselineFreq MHz-os frekvencián, a számítás az aktuális $frequencyMHz MHz-os sávhoz igazítja a k-tényezőt, amely meghatározza a görbös rádióhatótávolság határát.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Útvonal követése';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Az útvonal követése a repeaterig';
+
+ @override
+ String get contacts_repeaterPing => 'Ping-szinkronizáló';
+
+ @override
+ String get contacts_roomPathTrace => 'Kapcsolat a szobai szerverrel';
+
+ @override
+ String get contacts_roomPing => 'Ping-szolgáló szerver';
+
+ @override
+ String get contacts_chatTraceRoute => 'Útvonal meghatározása';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Keresse meg a $name címét.';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'A kiválasztott szöveg üres.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Érvénytelen kontaktinformáció';
+
+ @override
+ String get contacts_contactImported => 'Kapcsolat létrejött.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Nem sikerült a kapcsolatot importálni.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Zero Hop reklám';
+
+ @override
+ String get contacts_floodAdvert => 'Árvízre vonatkozó hirdetés';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Másolja a hirdetést a kiválasztási ablakba';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Adjon hozzá egy kapcsolatot a kiválasztott listából';
+
+ @override
+ String get contacts_ShareContact => 'Másolja a kapcsolatot a kiválasztóba';
+
+ @override
+ String get contacts_ShareContactZeroHop =>
+ 'Ossza meg a kapcsolatot hirdetés segítségével';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Kapcsolatot a hirdetésen keresztül.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Nem sikerült a kapcsolatot elküldeni.';
+
+ @override
+ String get contacts_contactAdvertCopied => 'A hirdetés másolva a vágólapra.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Az hirdetés másolása a vágólapra sikertelen.';
+
+ @override
+ String get notification_activityTitle => 'MeshCore tevékenységek';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'üzenetek',
+ one: 'üzenet',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'csatornaüzenetek',
+ one: 'csatornaüzenet',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'új csomópontok',
+ one: 'új csomópont',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Új $contactType megtalálva';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Új üzenetet kaptam';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Külső eszközök / helyi szerver a GPX formátumba';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exportálható repeater/szobaterm-szerver, amely egy GPX fájlban tárolja a helyzetet.';
+
+ @override
+ String get settings_gpxExportContacts => 'GPX export funkciók';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Az export funkció lehetővé teszi, hogy a GPS fájlban megadott helyszínen is megőrizzük az útvonalat.';
+
+ @override
+ String get settings_gpxExportAll =>
+ 'Exportálja az összes kapcsolatot GPX formátumban.';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Az összes elérhetőséget, amelyekhez egy helyszín tartozik, egy GPX fájlba exportálja.';
+
+ @override
+ String get settings_gpxExportSuccess =>
+ 'A GPX fájl sikeresen exportálva lett.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Nincs exportálható kapcsolatok.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Nem támogatott a jelenlegi eszközön/rendszeren.';
+
+ @override
+ String get settings_gpxExportError => 'Hiba történt az export során.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Adatátvisszaadó eszközök és helyiségi szerverek helyei';
+
+ @override
+ String get settings_gpxExportChat => 'Kapcsolódó helyszínek';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Az összes kapcsolat helyszíne';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'A meshcore-open-ból exportált térkéadatumok';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open GPX formátumú térképi adatok export';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Helyszíni erősítők';
+
+ @override
+ String get snrIndicator_lastSeen => 'Utoljára, amikor látták';
+
+ @override
+ String get contactsSettings_title => 'Kapcsolatok beállításai';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatikus felfedezés';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Egyéb kapcsolattal kapcsolatos beállítások';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Automatikus felhasználói hozzáadás';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Engedje, hogy a segítő automatikusan hozzáadja az új felhasználókat.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Automatikus visszatöltés';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Engedje, hogy a segítő eszköz automatikusan hozzáadja az új, megtalált jelzőállomásokat.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Automatikus szobák szerverek hozzáadása';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Engedje, hogy a segítő automatikusan hozzáadja az új, megtalált hálózati szervereket.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Automatikus érzékelők hozzáadása';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Engedje, hogy a kísérő automatikusan hozzáadja az új, megtalált szenzorokat.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Felülírja a legrégebbet';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Amikor a névsor telítődik, a legidősebb, de még nem kedvencként jelölt személyt helyettesíti egy újabb.';
+
+ @override
+ String get discoveredContacts_Title => 'Megtalált kapcsolatok';
+
+ @override
+ String get discoveredContacts_noMatching => 'Nincs megegyező kapcsolat.';
+
+ @override
+ String get discoveredContacts_searchHint => 'Keress új kapcsolatokat';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kapcsolat hozzáadva';
+
+ @override
+ String get discoveredContacts_addContact => 'Adjon személyhez';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Másolja a kapcsolatot a vágólapra';
+
+ @override
+ String get discoveredContacts_deleteContact =>
+ 'Törölj a feltalált kapcsolatot';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Törölj minden megtalált kapcsolatot';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Biztos, hogy szeretné törölni az összes eddig megtalált kapcsolatot?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Kérjük, várjon egy pillanatot, mielőtt újra elküldené.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Jelentkezzen az legörebb, olvasatlan üzenetre';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Amikor egy új csevet indítunk, amelyben vannak olvashatatlan üzenetek, görgessük a listát, hogy a legelső, olvashatatlan üzenet megjelenjen, nem pedig az utolsó.';
+
+ @override
+ String get appSettings_languageHu => 'Magyar';
+
+ @override
+ String get appSettings_languageJa => 'Japán';
+
+ @override
+ String get appSettings_languageKo => 'Koreai';
+
+ @override
+ String get radioStats_tooltip => 'Rádió és hálózati statisztikák';
+
+ @override
+ String get radioStats_screenTitle => 'Rádió statisztikák';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Csatlakozzon egy eszközhöz, hogy megtekinthesse a rádió adatok statisztikáit.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'A rádió statisztikákhoz v8 vagy újabb verziójú szoftver szükséges.';
+
+ @override
+ String get radioStats_waiting => 'Adatokra vár…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Háttérzaj szint: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Utolsó RSSI érték: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Utolsó SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX-es idő (összesen): $seconds másodperc';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX használat időtartama (összesen): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Háttérzaj szint (dBm) a legutóbbi minták alapján.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Háttérzaj szint: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Rádió adatok begyűjtése…';
+
+ @override
+ String get radioStats_settingsTile => 'Rádió statisztikák';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Háttérzaj, RSSI, zaj-sűrűség, és a használat időtartama';
+
+ @override
+ String get translation_title => 'Fordítás';
+
+ @override
+ String get translation_enableTitle => 'Engedje meg a fordítást';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Fordítsa az érkező üzeneteket, és lehetővé tegye a küldés előtti fordítást.';
+
+ @override
+ String get translation_composerTitle => 'Fordítsa el, mielőtt elküldi';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Ellenőrzi a zeneszerző fordítási ikon alapértékét.';
+
+ @override
+ String get translation_targetLanguage => 'Célnyelv';
+
+ @override
+ String get translation_useAppLanguage =>
+ 'Használja az alkalmazás nyelvének beállítását.';
+
+ @override
+ String get translation_downloadedModelLabel => 'Letöltött modell';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Előre definiált Hugging Face-modell';
+
+ @override
+ String get translation_manualUrlLabel => 'Manuális modell URL';
+
+ @override
+ String get translation_downloadModel => 'Letöltés';
+
+ @override
+ String get translation_downloading => 'Letöltés...';
+
+ @override
+ String get translation_working => 'Munkában vagyok...';
+
+ @override
+ String get translation_stop => 'Halt';
+
+ @override
+ String get translation_mergingChunks =>
+ 'A letöltött részek összeállítása a végleges fájlba...';
+
+ @override
+ String get translation_downloadedModels => 'Letöltött modelok';
+
+ @override
+ String get translation_deleteModel => 'Törölje a modellt';
+
+ @override
+ String get translation_modelDownloaded => 'Fordítási modell letöltve.';
+
+ @override
+ String get translation_downloadStopped => 'A letöltés leállt.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Letöltés sikertelen: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Addon először egy modell URL-t.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Megjelenítse a PIN-kódot';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Rejtse el a PIN-kódot';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth párosítási PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Adja meg a(z) $deviceName PIN-kódját (hagyja üresen, ha nincs).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Üzenet fordítása';
+
+ @override
+ String get translation_translateBeforeSending =>
+ 'Fordítsa el, mielőtt elküldi';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'A üzenetek fordítását a küldés előtt elvégezzük.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Küldj üzeneteket az eredeti, nyomtatott nyelven.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Fordítás $language-ra';
+ }
+
+ @override
+ String get translation_translationOptions => 'Fordítási lehetőségek';
+
+ @override
+ String get translation_systemLanguage => 'Rendszer nyelvé';
+}
diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart
index 20df619..3a55559 100644
--- a/lib/l10n/app_localizations_it.dart
+++ b/lib/l10n/app_localizations_it.dart
@@ -38,6 +38,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get common_delete => 'Elimina';
+ @override
+ String get common_deleteAll => 'Elimina tutto';
+
@override
String get common_close => 'Chiudi';
@@ -108,6 +111,137 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Stabilire una connessione tramite TCP';
+
+ @override
+ String get tcpHostLabel => 'Indirizzo IP';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Porta';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Inserisci l\'endpoint e connettiti.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Connessione a $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'È necessario fornire un indirizzo IP.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'La dimensione della porta deve essere compresa tra 1 e 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Il protocollo TCP non è supportato su questa piattaforma.';
+
+ @override
+ String get tcpErrorTimedOut => 'La connessione TCP è scaduta.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Impossibile stabilire la connessione TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Connessione tramite USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Seleziona il dispositivo seriale rilevato e connettilo direttamente al tuo nodo MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Seleziona un dispositivo USB';
+
+ @override
+ String get usbScreenNote =>
+ 'La comunicazione seriale USB è attiva sui dispositivi Android supportati e sulle piattaforme desktop.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Nessun dispositivo USB rilevato. Collegare uno e aggiornare.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'È stato negato l\'accesso tramite USB.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Il dispositivo USB selezionato non è più disponibile.';
+
+ @override
+ String get usbErrorInvalidPort => 'Seleziona un dispositivo USB valido.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Un\'altra richiesta di connessione tramite USB è già in corso.';
+
+ @override
+ String get usbErrorNotConnected => 'Non è collegato alcun dispositivo USB.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Impossibile aprire il dispositivo USB selezionato.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Impossibile connettersi al dispositivo USB selezionato.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'La comunicazione seriale tramite USB non è supportata su questa piattaforma.';
+
+ @override
+ String get usbErrorAlreadyActive => 'La connessione USB è già attiva.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Non è stato selezionato alcun dispositivo USB.';
+
+ @override
+ String get usbErrorPortClosed => 'La connessione USB non è attiva.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'La connessione è scaduta. Assicurarsi che il dispositivo abbia il firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Dispositivo per comunicazione seriale su rete';
+
+ @override
+ String get usbStatus_notConnected => 'Seleziona un dispositivo USB';
+
+ @override
+ String get usbStatus_connecting => 'Connessione al dispositivo USB...';
+
+ @override
+ String get usbStatus_searching => 'Ricerca di dispositivi USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Errore nella connessione USB: $error';
+ }
+
@override
String get scanner_scanning => 'Scansione in corso per i dispositivi...';
@@ -143,6 +277,23 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get scanner_scan => 'Scansiona';
+ @override
+ String get scanner_bluetoothOff => 'Il Bluetooth è disattivato.';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Si prega di attivare il Bluetooth per effettuare la scansione dei dispositivi.';
+
+ @override
+ String get scanner_chromeRequired => 'Browser Chrome richiesto';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Questa applicazione web richiede Google Chrome o un browser basato su Chromium per il supporto Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Abilita il Bluetooth';
+
@override
String get device_quickSwitch => 'Passa velocemente';
@@ -223,6 +374,13 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get settings_longitude => 'Longitudine';
+ @override
+ String get settings_contactSettings => 'Impostazioni di contatto';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Impostazioni per l\'aggiunta dei contatti';
+
@override
String get settings_privacyMode => 'Modalità Privacy';
@@ -240,6 +398,52 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Modalità privacy disabilitata';
+ @override
+ String get settings_privacy => 'Impostazioni sulla privacy';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Controlla le informazioni che vengono condivise.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Scegli le informazioni che il tuo dispositivo condivide con gli altri.';
+
+ @override
+ String get settings_denyAll => 'Negare tutto';
+
+ @override
+ String get settings_allowByContact => 'Consenti in base ai flag di contatto';
+
+ @override
+ String get settings_allowAll => 'Consenti tutto';
+
+ @override
+ String get settings_telemetryBaseMode => 'Modalità di base di telemetria';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Modalità di posizionamento telemetrico';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Modalità di ambiente di telemetria';
+
+ @override
+ String get settings_advertLocation => 'Posizione dell\'annuncio';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Includi la posizione nell\'annuncio';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Modalità telemetria aggiornata';
+
@override
String get settings_actions => 'Azioni';
@@ -312,6 +516,10 @@ class AppLocalizationsIt extends AppLocalizations {
String get settings_aboutDescription =>
'Un client Flutter open-source per i dispositivi di rete mesh LoRa Core di MeshCore.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Dati di elevazione LOS: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Nome';
@@ -336,15 +544,6 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get settings_presets => 'Preset';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frequenza (MHz)';
@@ -373,10 +572,15 @@ class AppLocalizationsIt extends AppLocalizations {
String get settings_txPowerInvalid => 'Potere TX non valido (0-22 dBm)';
@override
- String get settings_longRange => 'Lungo Raggio';
+ String get settings_clientRepeat => 'Ripetizione \"fuori dalla rete\"';
@override
- String get settings_fastSpeed => 'Velocità Rapida';
+ String get settings_clientRepeatSubtitle =>
+ 'Permetti a questo dispositivo di ripetere i pacchetti di rete per gli altri.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Per la comunicazione fuori rete, è necessario utilizzare frequenze di 433, 869 o 918 MHz.';
@override
String settings_error(String message) {
@@ -446,6 +650,20 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Russo';
+
+ @override
+ String get appSettings_languageUk => 'Ucraino';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Abilita tracciamento messaggi';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Mostra metadati dettagliati su instradamento e tempi per i messaggi';
+
@override
String get appSettings_notifications => 'Notifiche';
@@ -523,6 +741,50 @@ class AppLocalizationsIt extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Rotazione del percorso automatico disabilitata';
+ @override
+ String get appSettings_maxRouteWeight =>
+ 'Massimo peso consentito per il percorso';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Il peso massimo che un percorso può accumulare grazie a consegne di successo.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Peso iniziale del percorso';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Peso di partenza per nuovi percorsi';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Aumento del peso del successo';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Peso aggiunto a un percorso dopo una consegna riuscita.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Riduzione del peso associato al fallimento';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Peso rimosso da un percorso dopo un tentativo di consegna fallito.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Numero massimo di tentativi di invio del messaggio';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Numero di tentativi di riprova prima di considerare un messaggio come fallito.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batteria';
@@ -607,6 +869,15 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Cache Mappa Offline';
+ @override
+ String get appSettings_unitsTitle => 'Unità';
+
+ @override
+ String get appSettings_unitsMetric => 'Metrico (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperiale (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'Nessun\'area selezionata';
@@ -644,7 +915,35 @@ class AppLocalizationsIt extends AppLocalizations {
'I contatti appariranno quando i dispositivi pubblicizzano.';
@override
- String get contacts_searchContacts => 'Cerca contatti...';
+ String get contacts_unread => 'Non letti';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Cerca Contatti...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Cerca contatti...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Cerca $number$str Preferiti...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Cerca $number$str Utenti...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Cerca $number$str Ripetitori...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Cerca $number$str server Room...';
+ }
@override
String get contacts_noUnreadContacts => 'Nessun contatto non letto';
@@ -692,6 +991,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Il nome del gruppo è obbligatorio.';
+ @override
+ String get contacts_groupNameReserved => 'Questo nome del gruppo è riservato';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Il gruppo \"$name\" esiste già.';
@@ -731,6 +1033,42 @@ class AppLocalizationsIt extends AppLocalizations {
return 'Ultimo visto $days giorni fa';
}
+ @override
+ String get contact_info => 'Informazioni di Contatto';
+
+ @override
+ String get contact_settings => 'Impostazioni di contatto';
+
+ @override
+ String get contact_telemetry => 'Telemetria';
+
+ @override
+ String get contact_lastSeen => 'Ultimo accesso';
+
+ @override
+ String get contact_clearChat => 'Cancella chat';
+
+ @override
+ String get contact_teleBase => 'Base di telemetria';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Consenti la condivisione del livello della batteria e della telemetria di base';
+
+ @override
+ String get contact_teleLoc => 'Posizione telemetria';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Consenti la condivisione dei dati di posizione';
+
+ @override
+ String get contact_teleEnv => 'Ambiente di telemetria';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Consenti la condivisione dei dati del sensore ambientale';
+
@override
String get channels_title => 'Canali';
@@ -769,6 +1107,12 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get channels_editChannel => 'Modifica canale';
+ @override
+ String get channels_muteChannel => 'Silenzia canale';
+
+ @override
+ String get channels_unmuteChannel => 'Attiva notifiche canale';
+
@override
String get channels_deleteChannel => 'Elimina canale';
@@ -777,6 +1121,11 @@ class AppLocalizationsIt extends AppLocalizations {
return 'Eliminare \"$name\"? Non può essere annullato.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Impossibile eliminare il canale \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Canale \"$name\" eliminato';
@@ -890,6 +1239,14 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get chat_noMessages => 'Nessun messaggio ancora';
+ @override
+ String get chat_sendMessage => 'Invia messaggio';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Invia un messaggio a $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Invia un messaggio per iniziare';
@@ -909,11 +1266,6 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get chat_location => 'Posizione';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Invia un messaggio a $contactName';
- }
-
@override
String get chat_typeMessage => 'Digita un messaggio...';
@@ -1065,6 +1417,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get chat_pathManagement => 'Gestione Percorsi';
+ @override
+ String get chat_ShowAllPaths => 'Mostra tutti i percorsi';
+
@override
String get chat_routingMode => 'Modalità di routing';
@@ -1203,9 +1558,33 @@ class AppLocalizationsIt extends AppLocalizations {
return 'Non letti: $count';
}
+ @override
+ String get chat_openLink => 'Aprire il link?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Vuoi aprire questo link nel tuo browser?';
+
+ @override
+ String get chat_open => 'Apri';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Impossibile aprire il link: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Formato di link non valido';
+
@override
String get map_title => 'Mappa Nodi';
+ @override
+ String get map_lineOfSight => 'Linea di vista';
+
+ @override
+ String get map_losScreenTitle => 'Linea di vista';
+
@override
String get map_noNodesWithLocation => 'Nessun nodo con dati di posizione';
@@ -1263,6 +1642,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Condividi marcatore qui';
+ @override
+ String get map_setAsMyLocation => 'Imposta come la mia posizione';
+
@override
String get map_pinLabel => 'Etichetta PIN';
@@ -1308,6 +1690,9 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get map_otherNodes => 'Altri Nodi';
+ @override
+ String get map_showOverlaps => 'Sovrapposizioni della chiave ripetitore';
+
@override
String get map_keyPrefix => 'Prefisso Chiave';
@@ -1323,6 +1708,15 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Mostra i segnaposto condivisi';
+ @override
+ String get map_showGuessedLocations => 'Mostra le posizioni stimate dei nodi';
+
+ @override
+ String get map_showDiscoveryContacts => 'Mostra Contatti di Discovery';
+
+ @override
+ String get map_guessedLocation => 'Località indovinata';
+
@override
String get map_lastSeenTime => 'Ultimo Tempo di Visualizzazione';
@@ -1335,6 +1729,22 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get map_manageRepeater => 'Gestisci Ripetitore';
+ @override
+ String get map_tapToAdd => 'Tocca i nodi per aggiungerli al percorso.';
+
+ @override
+ String get map_runTrace => 'Esegui Path Trace';
+
+ @override
+ String get map_runTraceWithReturnPath =>
+ 'Tornare indietro sullo stesso percorso';
+
+ @override
+ String get map_removeLast => 'Rimuovi ultimo';
+
+ @override
+ String get map_pathTraceCancelled => 'Tracciamento del percorso annullato.';
+
@override
String get mapCache_title => 'Cache Mappa Offline';
@@ -1606,9 +2016,18 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get room_management => 'Gestione del Server di Camera';
+ @override
+ String get repeater_guest => 'Informazioni sul ripetitore';
+
+ @override
+ String get room_guest => 'Informazioni sul server';
+
@override
String get repeater_managementTools => 'Strumenti di Gestione';
+ @override
+ String get repeater_guestTools => 'Strumenti per gli ospiti';
+
@override
String get repeater_status => 'Stato';
@@ -1630,10 +2049,10 @@ class AppLocalizationsIt extends AppLocalizations {
String get repeater_cliSubtitle => 'Invia comandi al ripetitore';
@override
- String get repeater_neighbours => 'Vicini';
+ String get repeater_neighbors => 'Vicini';
@override
- String get repeater_neighboursSubtitle =>
+ String get repeater_neighborsSubtitle =>
'Visualizza vicini di salto pari a zero.';
@override
@@ -1643,6 +2062,14 @@ class AppLocalizationsIt extends AppLocalizations {
String get repeater_settingsSubtitle =>
'Configura i parametri del ripetitore';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Sincronizzazione dell\'orologio dopo il login';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Invia automaticamente il comando \"sincronizzazione dell\'orologio\" dopo un login riuscito.';
+
@override
String get repeater_statusTitle => 'Stato del Ripetitore';
@@ -2019,6 +2446,12 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Orologio';
+ @override
+ String get repeater_cliQuickClockSync => 'Sincronizzazione dell\'orologio';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Scopri i Vicini';
+
@override
String get repeater_cliHelpAdvert => 'Invia un pacchetto pubblicitario';
@@ -2334,7 +2767,7 @@ class AppLocalizationsIt extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Ripetitori Vicini';
+ String get neighbors_repeatersNeighbors => 'Ripetitori Vicini';
@override
String get neighbors_noData => 'Nessun dato sugli vicini disponibile.';
@@ -2565,32 +2998,32 @@ class AppLocalizationsIt extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Ri genera la chiave segreta';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regenera la chiave segreta per \"$name\"? Tutti i membri dovranno scansionare il nuovo codice QR per continuare a comunicare.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Rigenera';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Codice segreto rigenerato per \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Aggiorna Segreto';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Segreto aggiornato per \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Scansiona il nuovo codice QR per aggiornare il segreto di \"$name\"';
}
@override
@@ -2643,6 +3076,15 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get listFilter_all => 'Tutti';
+ @override
+ String get listFilter_favorites => 'Preferiti';
+
+ @override
+ String get listFilter_addToFavorites => 'Aggiungi ai preferiti';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Rimuovi dai preferiti';
+
@override
String get listFilter_users => 'Utenti';
@@ -2657,4 +3099,614 @@ class AppLocalizationsIt extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nuovo gruppo';
+
+ @override
+ String get pathTrace_you => 'Tu';
+
+ @override
+ String get pathTrace_failed => 'Tracciamento del percorso fallito.';
+
+ @override
+ String get pathTrace_notAvailable =>
+ 'Tracciamento del percorso non disponibile.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Aggiorna Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Uno o più dei luppoli mancano di una posizione!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Pulisci percorso';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Seleziona i nodi iniziali e finali per la LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Controllo della linea di vista fallito: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Cancella tutti i punti';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Eseguire LOS per visualizzare il profilo altimetrico';
+
+ @override
+ String get losMenuTitle => 'Menù LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Tocca i nodi o premi a lungo la mappa per punti personalizzati';
+
+ @override
+ String get losShowDisplayNodes => 'Mostra i nodi di visualizzazione';
+
+ @override
+ String get losCustomPoints => 'Punti personalizzati';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Personalizzato $index';
+ }
+
+ @override
+ String get losPointA => 'Punto A';
+
+ @override
+ String get losPointB => 'Punto B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenna A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenna B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Esegui LOS';
+
+ @override
+ String get losNoElevationData => 'Nessun dato di elevazione';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, libera LOS, distanza minima $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, bloccato da $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: controllo...';
+
+ @override
+ String get losStatusNoData => 'LOS: nessun dato';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total libera, $blocked bloccato, $unknown sconosciuto';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Dati di elevazione non disponibili per uno o più campioni.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Dati punti/elevazione non validi per il calcolo della LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Rinomina punto personalizzato';
+
+ @override
+ String get losPointName => 'Nome del punto';
+
+ @override
+ String get losShowPanelTooltip => 'Mostra il pannello LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Nascondi il pannello LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Dati di elevazione: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Orizzonte radio';
+
+ @override
+ String get losLegendLosBeam => 'Linea di vista';
+
+ @override
+ String get losLegendTerrain => 'Terreno';
+
+ @override
+ String get losFrequencyLabel => 'Frequenza';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Visualizza i dettagli del calcolo';
+
+ @override
+ String get losFrequencyDialogTitle => 'Calcolo dell’orizzonte radio';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Partendo da k=$baselineK a $baselineFreq MHz, il calcolo regola il fattore k per l\'attuale banda $frequencyMHz MHz, che definisce il limite curvo dell\'orizzonte radio.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Traccia Percorso';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Traccia percorso al ripetitore';
+
+ @override
+ String get contacts_repeaterPing => 'Ripetitore ping';
+
+ @override
+ String get contacts_roomPathTrace =>
+ 'Traccia del percorso al server della stanza';
+
+ @override
+ String get contacts_roomPing => 'Ping al server della stanza';
+
+ @override
+ String get contacts_chatTraceRoute => 'Traccia percorso path';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Traccia percorso verso $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'La clipboard è vuota.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Dati di contatto non validi';
+
+ @override
+ String get contacts_contactImported => 'Il contatto è stato importato.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Contatto non importato con successo.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Annuncio Zero Hop';
+
+ @override
+ String get contacts_floodAdvert => 'Annuncio alluvionale';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Copia Annuncio negli Appunti';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Aggiungere contatto dalla clipboard';
+
+ @override
+ String get contacts_ShareContact => 'Copia contatto negli Appunti';
+
+ @override
+ String get contacts_ShareContactZeroHop =>
+ 'Condividi contatto tramite annuncio';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Inviato contatto tramite annuncio.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Invio del contatto non riuscito.';
+
+ @override
+ String get contacts_contactAdvertCopied => 'Annuncio copiato negli Appunti.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Copia dell\'annuncio nella Clipboard non riuscita.';
+
+ @override
+ String get notification_activityTitle => 'Attività MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messaggi',
+ one: 'messaggio',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messaggi del canale',
+ one: 'messaggio del canale',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nuovi nodi',
+ one: 'nuovo nodo',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nuovo $contactType scoperto';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nuovo messaggio ricevuto';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Esporta ripetitori / server di stanza in GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Esporta ripetitori / roomserver con una posizione in un file GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Esporta compagni in GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Esporta i compagni con una posizione in un file GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Esporta tutti i contatti in GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Esporta tutti i contatti con una posizione in un file GPX.';
+
+ @override
+ String get settings_gpxExportSuccess =>
+ 'Esportazione del file GPX completata con successo.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Nessun contatto da esportare.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Non supportato sul tuo dispositivo/Sistema Operativo';
+
+ @override
+ String get settings_gpxExportError =>
+ 'Si è verificato un errore durante l\'esportazione.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Posizioni del server ripetitore e della stanza';
+
+ @override
+ String get settings_gpxExportChat => 'Posizioni dei compagni';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Tutte le posizioni dei contatti';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Dati mappa esportati da meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open esportazione dati mappa GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Ripetitori vicini';
+
+ @override
+ String get snrIndicator_lastSeen => 'Ultimo accesso';
+
+ @override
+ String get contactsSettings_title => 'Impostazioni dei contatti';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Scoperta automatica';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Altre impostazioni relative ai contatti';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Aggiungere utenti automaticamente';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Consenti al compagno di aggiungere automaticamente gli utenti scoperti.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Aggiungere ripetitori automaticamente';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Consenti al compagno di aggiungere automaticamente i ripetitori scoperti.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Aggiungere automaticamente i server delle stanze';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Consenti al compagno di aggiungere automaticamente i server delle stanze scoperte.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Aggiungere automaticamente i sensori';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Consenti al compagno di aggiungere automaticamente i sensori scoperti';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle =>
+ 'Sostituisci il più vecchio';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Quando l\'elenco dei contatti è pieno, il contatto più vecchio non tra i preferiti verrà sostituito.';
+
+ @override
+ String get discoveredContacts_Title => 'Contatti scoperti';
+
+ @override
+ String get discoveredContacts_noMatching => 'Nessun contatto corrispondente';
+
+ @override
+ String get discoveredContacts_searchHint => 'Cerca contatti scoperti';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contatto aggiunto';
+
+ @override
+ String get discoveredContacts_addContact => 'Aggiungi contatto';
+
+ @override
+ String get discoveredContacts_copyContact => 'Copia contatto negli appunti';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Elimina Contatto';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Eliminare tutti i contatti scoperti';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Sei sicuro di voler eliminare tutti i contatti scoperti?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Si prega di attendere un momento prima di inviare nuovamente.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Vai al messaggio più vecchio non letto';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Quando si apre una chat con messaggi non letti, scorrete verso l\'alto fino al primo messaggio non letto, invece che al più recente.';
+
+ @override
+ String get appSettings_languageHu => 'Ungherese';
+
+ @override
+ String get appSettings_languageJa => 'Giapponese';
+
+ @override
+ String get appSettings_languageKo => 'Coreano';
+
+ @override
+ String get radioStats_tooltip => 'Statistiche per radio e reti';
+
+ @override
+ String get radioStats_screenTitle => 'Statistiche radio';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Connettiti a un dispositivo per visualizzare le statistiche radio.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Le statistiche radio richiedono il firmware versione 8 o successiva.';
+
+ @override
+ String get radioStats_waiting => 'In attesa dei dati…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Livello di rumore: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Ultimo valore RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Ultimo SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Tempo di trasmissione in diretta (totale): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Tempo di trasmissione RX (totale): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Livello di rumore (dBm) misurato su campioni recenti.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Livello di rumore: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Recupero delle statistiche radio…';
+
+ @override
+ String get radioStats_settingsTile => 'Statistiche radio';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Livello di rumore, RSSI, rapporto segnale/rumore (SNR) e tempo di trasmissione';
+
+ @override
+ String get translation_title => 'Traduzione';
+
+ @override
+ String get translation_enableTitle => 'Abilitare la traduzione';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Tradurre i messaggi in arrivo e consentire la traduzione preventiva prima dell\'invio.';
+
+ @override
+ String get translation_composerTitle => 'Tradurre prima di inviare';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Controlla lo stato predefinito dell\'icona di traduzione del compositore.';
+
+ @override
+ String get translation_targetLanguage => 'Lingua di destinazione';
+
+ @override
+ String get translation_useAppLanguage => 'Utilizza la lingua dell\'app';
+
+ @override
+ String get translation_downloadedModelLabel => 'Modello scaricato';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Modello predefinito di Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'URL del modello manuale';
+
+ @override
+ String get translation_downloadModel => 'Scarica il modello';
+
+ @override
+ String get translation_downloading => 'Inizio download...';
+
+ @override
+ String get translation_working => 'Lavoro...';
+
+ @override
+ String get translation_stop => 'Smetta';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Unione dei frammenti scaricati in un unico file...';
+
+ @override
+ String get translation_downloadedModels => 'Modelli scaricati';
+
+ @override
+ String get translation_deleteModel => 'Elimina modello';
+
+ @override
+ String get translation_modelDownloaded => 'Modello di traduzione scaricato.';
+
+ @override
+ String get translation_downloadStopped => 'Il download è stato interrotto.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Download fallito: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Inserite innanzitutto l\'URL del modello.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Mostra PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Nascondi il PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle =>
+ 'PIN per l\'accoppiamento Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Inserire il codice PIN per $deviceName (lasciare vuoto se non presente).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Traduzione del messaggio';
+
+ @override
+ String get translation_translateBeforeSending => 'Tradurre prima di inviare';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'I messaggi verranno tradotti prima di essere inviati.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Invia messaggi utilizzando la lingua originale, scritta.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Tradurre in $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Opzioni di traduzione';
+
+ @override
+ String get translation_systemLanguage => 'Lingua del sistema';
}
diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart
new file mode 100644
index 0000000..afb8c29
--- /dev/null
+++ b/lib/l10n/app_localizations_ja.dart
@@ -0,0 +1,3531 @@
+// ignore: unused_import
+import 'package:intl/intl.dart' as intl;
+import 'app_localizations.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Japanese (`ja`).
+class AppLocalizationsJa extends AppLocalizations {
+ AppLocalizationsJa([String locale = 'ja']) : super(locale);
+
+ @override
+ String get appTitle => 'MeshCore Open';
+
+ @override
+ String get nav_contacts => '連絡先';
+
+ @override
+ String get nav_channels => 'チャンネル';
+
+ @override
+ String get nav_map => '地図';
+
+ @override
+ String get common_cancel => 'キャンセル';
+
+ @override
+ String get common_ok => '了解';
+
+ @override
+ String get common_connect => '接続する';
+
+ @override
+ String get common_unknownDevice => '不明なデバイス';
+
+ @override
+ String get common_save => '保存';
+
+ @override
+ String get common_delete => '削除';
+
+ @override
+ String get common_deleteAll => 'すべて削除';
+
+ @override
+ String get common_close => '閉じる';
+
+ @override
+ String get common_edit => '編集';
+
+ @override
+ String get common_add => '追加';
+
+ @override
+ String get common_settings => '設定';
+
+ @override
+ String get common_disconnect => '切断する';
+
+ @override
+ String get common_connected => '接続されている';
+
+ @override
+ String get common_disconnected => '切断';
+
+ @override
+ String get common_create => '作成する';
+
+ @override
+ String get common_continue => '続き';
+
+ @override
+ String get common_share => '共有する';
+
+ @override
+ String get common_copy => 'コピー';
+
+ @override
+ String get common_retry => '再試';
+
+ @override
+ String get common_hide => '隠す';
+
+ @override
+ String get common_remove => '削除';
+
+ @override
+ String get common_enable => '有効化する';
+
+ @override
+ String get common_disable => '無効化する';
+
+ @override
+ String get common_reboot => '再起動';
+
+ @override
+ String get common_loading => '読み込み中...';
+
+ @override
+ String get common_notAvailable => '—';
+
+ @override
+ String common_voltageValue(String volts) {
+ return '$volts V';
+ }
+
+ @override
+ String common_percentValue(int percent) {
+ return '$percent%';
+ }
+
+ @override
+ String get scanner_title => 'MeshCore オープン';
+
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'ブルートゥース';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'TCP を使用して接続';
+
+ @override
+ String get tcpHostLabel => 'IPアドレス';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => '港';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'エンドポイントを入力し、接続する';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return '$endpoint への接続中...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'IPアドレスが必要です。';
+
+ @override
+ String get tcpErrorPortInvalid => 'ポート番号は1から65535の範囲で指定してください。';
+
+ @override
+ String get tcpErrorUnsupported => 'このプラットフォームでは、TCP 転送はサポートされていません。';
+
+ @override
+ String get tcpErrorTimedOut => 'TCP 接続がタイムアウトしました。';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'TCP接続に失敗しました:$error';
+ }
+
+ @override
+ String get usbScreenTitle => 'USB経由で接続';
+
+ @override
+ String get usbScreenSubtitle => '検出されたシリアルデバイスを選択し、MeshCoreノードに直接接続してください。';
+
+ @override
+ String get usbScreenStatus => 'USBデバイスを選択する';
+
+ @override
+ String get usbScreenNote =>
+ 'USBシリアルポートは、サポートされているAndroidデバイスおよびデスクトッププラットフォームで利用可能です。';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'USBデバイスが見つかりませんでした。「別のUSBデバイスを接続して、再度確認してください。」';
+
+ @override
+ String get usbErrorPermissionDenied => 'USBへのアクセス許可が拒否されました。';
+
+ @override
+ String get usbErrorDeviceMissing => '選択されたUSBデバイスは、もう利用できません。';
+
+ @override
+ String get usbErrorInvalidPort => '有効なUSBデバイスを選択してください。';
+
+ @override
+ String get usbErrorBusy => '別のUSB接続の要求がすでに処理中です。';
+
+ @override
+ String get usbErrorNotConnected => 'USBデバイスは接続されていません。';
+
+ @override
+ String get usbErrorOpenFailed => '選択したUSBデバイスを開くことができません。';
+
+ @override
+ String get usbErrorConnectFailed => '選択したUSBデバイスへの接続に失敗しました。';
+
+ @override
+ String get usbErrorUnsupported => 'このプラットフォームでは、USBシリアル通信はサポートされていません。';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB接続はすでに確立されています。';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'USBデバイスは選択されていません。';
+
+ @override
+ String get usbErrorPortClosed => 'USB接続は確立されていません。';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ '接続がタイムアウトしました。デバイスにUSBコンパニオンファームウェアがインストールされていることを確認してください。';
+
+ @override
+ String get usbFallbackDeviceName => 'ウェブシリアルデバイス';
+
+ @override
+ String get usbStatus_notConnected => 'USBデバイスを選択する';
+
+ @override
+ String get usbStatus_connecting => 'USBデバイスへの接続中...';
+
+ @override
+ String get usbStatus_searching => 'USBデバイスを検索中...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'USB接続に失敗しました:$error';
+ }
+
+ @override
+ String get scanner_scanning => 'デバイスをスキャン中...';
+
+ @override
+ String get scanner_connecting => '接続中...';
+
+ @override
+ String get scanner_disconnecting => '切断...';
+
+ @override
+ String get scanner_notConnected => '接続されていない';
+
+ @override
+ String scanner_connectedTo(String deviceName) {
+ return '$deviceName に接続';
+ }
+
+ @override
+ String get scanner_searchingDevices => 'MeshCoreデバイスの検索';
+
+ @override
+ String get scanner_tapToScan => 'MeshCore デバイスを検索するには、「スキャン」ボタンをタップしてください。';
+
+ @override
+ String scanner_connectionFailed(String error) {
+ return '接続に失敗しました:$error';
+ }
+
+ @override
+ String get scanner_stop => '停止';
+
+ @override
+ String get scanner_scan => 'スキャン';
+
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth はオフになっています';
+
+ @override
+ String get scanner_bluetoothOffMessage => 'Bluetoothを有効にして、デバイスを検索してください。';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome ブラウザが必須です';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'このWebアプリケーションは、Bluetooth機能を利用するために、Google ChromeまたはChromiumベースのブラウザが必要です。';
+
+ @override
+ String get scanner_enableBluetooth => 'Bluetoothを有効にする';
+
+ @override
+ String get device_quickSwitch => '素早い切り替え';
+
+ @override
+ String get device_meshcore => 'メッシュコア';
+
+ @override
+ String get settings_title => '設定';
+
+ @override
+ String get settings_deviceInfo => 'デバイス情報';
+
+ @override
+ String get settings_appSettings => 'アプリ設定';
+
+ @override
+ String get settings_appSettingsSubtitle => '通知、メッセージング、および地図の表示設定';
+
+ @override
+ String get settings_nodeSettings => 'ノード設定';
+
+ @override
+ String get settings_nodeName => 'ノード名';
+
+ @override
+ String get settings_nodeNameNotSet => '設定されていない';
+
+ @override
+ String get settings_nodeNameHint => 'ノード名を入力してください';
+
+ @override
+ String get settings_nodeNameUpdated => '氏名変更';
+
+ @override
+ String get settings_radioSettings => 'ラジオ設定';
+
+ @override
+ String get settings_radioSettingsSubtitle => '周波数、電力、スプレッドファクター';
+
+ @override
+ String get settings_radioSettingsUpdated => 'ラジオの設定が更新されました';
+
+ @override
+ String get settings_location => '場所';
+
+ @override
+ String get settings_locationSubtitle => 'GPS 座標';
+
+ @override
+ String get settings_locationUpdated => '場所とGPS設定が更新されました';
+
+ @override
+ String get settings_locationBothRequired => '緯度と経度をそれぞれ入力してください。';
+
+ @override
+ String get settings_locationInvalid => '無効な緯度または経度。';
+
+ @override
+ String get settings_locationGPSEnable => 'GPS機能有効';
+
+ @override
+ String get settings_locationGPSEnableSubtitle => 'GPSが自動的に位置情報を更新できるようにする。';
+
+ @override
+ String get settings_locationIntervalSec => 'GPS データの取得間隔(秒)';
+
+ @override
+ String get settings_locationIntervalInvalid =>
+ '間隔は少なくとも60秒で、86400秒未満でなければなりません。';
+
+ @override
+ String get settings_latitude => '緯度';
+
+ @override
+ String get settings_longitude => '経度';
+
+ @override
+ String get settings_contactSettings => '連絡設定';
+
+ @override
+ String get settings_contactSettingsSubtitle => '連絡先を追加する設定';
+
+ @override
+ String get settings_privacyMode => 'プライバシーモード';
+
+ @override
+ String get settings_privacyModeSubtitle => '広告に名前/場所を記載しない';
+
+ @override
+ String get settings_privacyModeToggle =>
+ 'プライバシーモードをオンにして、広告に表示される名前や場所を非表示にします。';
+
+ @override
+ String get settings_privacyModeEnabled => 'プライバシーモードが有効になっています';
+
+ @override
+ String get settings_privacyModeDisabled => 'プライバシーモードは無効化されています';
+
+ @override
+ String get settings_privacy => 'プライバシー設定';
+
+ @override
+ String get settings_privacySubtitle => '共有する情報の内容を管理する。';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ '自分のデバイスが他の人に共有する情報を選択してください。';
+
+ @override
+ String get settings_denyAll => 'すべてを否定';
+
+ @override
+ String get settings_allowByContact => '連絡先を明示するオプション';
+
+ @override
+ String get settings_allowAll => 'すべて許可';
+
+ @override
+ String get settings_telemetryBaseMode => 'テレメトリ基本モード';
+
+ @override
+ String get settings_telemetryLocationMode => 'テレメトリ位置特定モード';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'テレメトリ環境モード';
+
+ @override
+ String get settings_advertLocation => '広告掲載場所';
+
+ @override
+ String get settings_advertLocationSubtitle => '広告に場所を記載してください。';
+
+ @override
+ String settings_multiAck(String value) {
+ return '複数のACK:$value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'テレメトリモードが更新されました';
+
+ @override
+ String get settings_actions => '行動';
+
+ @override
+ String get settings_sendAdvertisement => '広告を送信する';
+
+ @override
+ String get settings_sendAdvertisementSubtitle => '現在、放送での活動';
+
+ @override
+ String get settings_advertisementSent => '広告が送信されました';
+
+ @override
+ String get settings_syncTime => '同期時間';
+
+ @override
+ String get settings_syncTimeSubtitle => 'デバイスの時刻を、携帯電話の時刻に合わせる';
+
+ @override
+ String get settings_timeSynchronized => '時間同期';
+
+ @override
+ String get settings_refreshContacts => '連絡先を更新する';
+
+ @override
+ String get settings_refreshContactsSubtitle => 'デバイスから連絡先リストを再読み込みする';
+
+ @override
+ String get settings_rebootDevice => 'デバイスを再起動する';
+
+ @override
+ String get settings_rebootDeviceSubtitle => 'MeshCore デバイスを再起動する';
+
+ @override
+ String get settings_rebootDeviceConfirm =>
+ '本当にデバイスを再起動したいですか? その場合、接続が切断されます。';
+
+ @override
+ String get settings_debug => 'デバッグ';
+
+ @override
+ String get settings_bleDebugLog => 'BLE デバッグログ';
+
+ @override
+ String get settings_bleDebugLogSubtitle => 'BLEコマンド、応答、および生のデータ';
+
+ @override
+ String get settings_appDebugLog => 'アプリケーションのデバッグログ';
+
+ @override
+ String get settings_appDebugLogSubtitle => 'アプリケーションのデバッグメッセージ';
+
+ @override
+ String get settings_about => '概要';
+
+ @override
+ String settings_aboutVersion(String version) {
+ return 'MeshCore Open $version版';
+ }
+
+ @override
+ String get settings_aboutLegalese => '2026年のMeshCoreオープンソースプロジェクト';
+
+ @override
+ String get settings_aboutDescription =>
+ 'MeshCore LoRaメッシュネットワークデバイス用の、オープンソースのFlutterクライアント。';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS 標高データ:Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => '名前';
+
+ @override
+ String get settings_infoId => 'ID';
+
+ @override
+ String get settings_infoStatus => 'ステータス';
+
+ @override
+ String get settings_infoBattery => 'バッテリー';
+
+ @override
+ String get settings_infoPublicKey => '公開鍵';
+
+ @override
+ String get settings_infoContactsCount => '連絡先数';
+
+ @override
+ String get settings_infoChannelCount => 'チャンネル数';
+
+ @override
+ String get settings_presets => 'プリセット';
+
+ @override
+ String get settings_frequency => '周波数 (MHz)';
+
+ @override
+ String get settings_frequencyHelper => '300.0 - 2500.0';
+
+ @override
+ String get settings_frequencyInvalid => '無効な周波数 (300-2500 MHz)';
+
+ @override
+ String get settings_bandwidth => '帯域幅';
+
+ @override
+ String get settings_spreadingFactor => '伝播係数';
+
+ @override
+ String get settings_codingRate => 'コーディング速度';
+
+ @override
+ String get settings_txPower => 'TX 信号電力 (dBm)';
+
+ @override
+ String get settings_txPowerHelper => '0 - 22';
+
+ @override
+ String get settings_txPowerInvalid => '無効な送信電力 (0-22 dBm)';
+
+ @override
+ String get settings_clientRepeat => 'オフグリッド(電力網から孤立した状態)の繰り返し';
+
+ @override
+ String get settings_clientRepeatSubtitle =>
+ 'このデバイスが、他のデバイスに対してメッシュパケットを繰り返し送信できるようにする。';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'オフグリッドでの再送には、433MHz、869MHz、または918MHzの周波数が必要です。';
+
+ @override
+ String settings_error(String message) {
+ return 'エラー:$message';
+ }
+
+ @override
+ String get appSettings_title => 'アプリ設定';
+
+ @override
+ String get appSettings_appearance => '外観';
+
+ @override
+ String get appSettings_theme => 'テーマ';
+
+ @override
+ String get appSettings_themeSystem => 'システムデフォルト';
+
+ @override
+ String get appSettings_themeLight => '光';
+
+ @override
+ String get appSettings_themeDark => '暗い';
+
+ @override
+ String get appSettings_language => '言語';
+
+ @override
+ String get appSettings_languageSystem => 'システムデフォルト';
+
+ @override
+ String get appSettings_languageEn => '英語';
+
+ @override
+ String get appSettings_languageFr => 'フランス語';
+
+ @override
+ String get appSettings_languageEs => 'スペイン語';
+
+ @override
+ String get appSettings_languageDe => 'ドイツ語';
+
+ @override
+ String get appSettings_languagePl => 'ポーランド語';
+
+ @override
+ String get appSettings_languageSl => 'スロベニア語';
+
+ @override
+ String get appSettings_languagePt => 'ポルトガル語';
+
+ @override
+ String get appSettings_languageIt => 'イタリア語';
+
+ @override
+ String get appSettings_languageZh => '中国語';
+
+ @override
+ String get appSettings_languageSv => 'スウェーデン語';
+
+ @override
+ String get appSettings_languageNl => 'オランダ語';
+
+ @override
+ String get appSettings_languageSk => 'スロベニア語';
+
+ @override
+ String get appSettings_languageBg => 'ブルガリア語';
+
+ @override
+ String get appSettings_languageRu => 'ロシア語';
+
+ @override
+ String get appSettings_languageUk => 'ウクライナ語';
+
+ @override
+ String get appSettings_enableMessageTracing => 'メッセージ追跡機能を有効にする';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'メッセージに関する詳細な経路およびタイミングに関するメタデータを表示する';
+
+ @override
+ String get appSettings_notifications => '通知';
+
+ @override
+ String get appSettings_enableNotifications => '通知を有効にする';
+
+ @override
+ String get appSettings_enableNotificationsSubtitle => 'メッセージや広告に関する通知を受け取る';
+
+ @override
+ String get appSettings_notificationPermissionDenied => '通知の許可が拒否されました';
+
+ @override
+ String get appSettings_notificationsEnabled => '通知機能が有効になっています';
+
+ @override
+ String get appSettings_notificationsDisabled => '通知が無効化されています';
+
+ @override
+ String get appSettings_messageNotifications => 'メッセージ通知';
+
+ @override
+ String get appSettings_messageNotificationsSubtitle =>
+ '新しいメッセージを受信した際に、通知を表示する';
+
+ @override
+ String get appSettings_channelMessageNotifications => 'チャネルメッセージの通知';
+
+ @override
+ String get appSettings_channelMessageNotificationsSubtitle =>
+ 'チャンネルからのメッセージを受信した際に、通知を表示する';
+
+ @override
+ String get appSettings_advertisementNotifications => '広告通知';
+
+ @override
+ String get appSettings_advertisementNotificationsSubtitle =>
+ '新しいノードが発見された場合に通知を表示する';
+
+ @override
+ String get appSettings_messaging => 'メッセージング';
+
+ @override
+ String get appSettings_clearPathOnMaxRetry => 'マックスリトライでの明確な手順';
+
+ @override
+ String get appSettings_clearPathOnMaxRetrySubtitle =>
+ '5回送信に失敗した場合、連絡経路をリセットする';
+
+ @override
+ String get appSettings_pathsWillBeCleared => '5回失敗した後、経路が再開されます。';
+
+ @override
+ String get appSettings_pathsWillNotBeCleared => 'パスは自動で削除されません。';
+
+ @override
+ String get appSettings_autoRouteRotation => '自動ルートの切り替え';
+
+ @override
+ String get appSettings_autoRouteRotationSubtitle => '最適なルートと、洪水モードを切り替える';
+
+ @override
+ String get appSettings_autoRouteRotationEnabled => '自動ルートの切り替え機能が有効になっています';
+
+ @override
+ String get appSettings_autoRouteRotationDisabled => '自動ルートの変更機能が無効になっています。';
+
+ @override
+ String get appSettings_maxRouteWeight => '最大ルート重量';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'ある経路が、成功裏に配送された場合に、積み上げられる最大重量';
+
+ @override
+ String get appSettings_initialRouteWeight => '初期ルートの重み';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle => '新たに発見された経路の初期重量';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement => '成功時の重み増加';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ '配送が成功した場合に、経路に追加される重量';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement => '失敗時の重み減少';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ '配送に失敗した際に、経路から取り除かれた重量';
+
+ @override
+ String get appSettings_maxMessageRetries => '最大メッセージ再試行回数';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'メッセージを「失敗」とマークするまでの、再試行回数';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
+ @override
+ String get appSettings_battery => 'バッテリー';
+
+ @override
+ String get appSettings_batteryChemistry => '電池の化学';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return '$deviceName 単位';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst => 'デバイスを選択するために接続する';
+
+ @override
+ String get appSettings_batteryNmc => '18650型 NMC (3.0-4.2V)';
+
+ @override
+ String get appSettings_batteryLifepo4 => 'LiFePO4 (2.6-3.65V)';
+
+ @override
+ String get appSettings_batteryLipo => 'LiPo (3.0-4.2V)';
+
+ @override
+ String get appSettings_mapDisplay => '地図の表示';
+
+ @override
+ String get appSettings_showRepeaters => '繰り返し再生機能';
+
+ @override
+ String get appSettings_showRepeatersSubtitle => '地図上にリピーターノードを表示する';
+
+ @override
+ String get appSettings_showChatNodes => 'チャットノードの表示';
+
+ @override
+ String get appSettings_showChatNodesSubtitle => '地図上にチャットノードを表示する';
+
+ @override
+ String get appSettings_showOtherNodes => '他のノードを表示する';
+
+ @override
+ String get appSettings_showOtherNodesSubtitle => '地図上に、他のノードの種類を表示する';
+
+ @override
+ String get appSettings_timeFilter => '時間フィルター';
+
+ @override
+ String get appSettings_timeFilterShowAll => 'すべてのノードを表示する';
+
+ @override
+ String appSettings_timeFilterShowLast(int hours) {
+ return '過去 $hours 時間のノードを表示する';
+ }
+
+ @override
+ String get appSettings_mapTimeFilter => '地図の表示期間を絞り込む';
+
+ @override
+ String get appSettings_showNodesDiscoveredWithin => '以下の範囲内で発見されたノードを表示する:';
+
+ @override
+ String get appSettings_allTime => 'すべての期間';
+
+ @override
+ String get appSettings_lastHour => '直前の';
+
+ @override
+ String get appSettings_last6Hours => '過去6時間';
+
+ @override
+ String get appSettings_last24Hours => '過去24時間';
+
+ @override
+ String get appSettings_lastWeek => '先週';
+
+ @override
+ String get appSettings_offlineMapCache => 'オフライン用地図キャッシュ';
+
+ @override
+ String get appSettings_unitsTitle => '単位';
+
+ @override
+ String get appSettings_unitsMetric => 'メートル (m) / キロメートル (km)';
+
+ @override
+ String get appSettings_unitsImperial => '帝国 (フィート / マイル)';
+
+ @override
+ String get appSettings_noAreaSelected => '選択されたエリアはありません';
+
+ @override
+ String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
+ return '選択された範囲(ズームレベル:$minZoom~$maxZoom)';
+ }
+
+ @override
+ String get appSettings_debugCard => 'デバッグ';
+
+ @override
+ String get appSettings_appDebugLogging => 'アプリケーションのデバッグ用ログ';
+
+ @override
+ String get appSettings_appDebugLoggingSubtitle =>
+ 'ログアプリのデバッグメッセージ(トラブルシューティング用)';
+
+ @override
+ String get appSettings_appDebugLoggingEnabled =>
+ 'アプリケーションのデバッグ用ログ機能が有効になっています。';
+
+ @override
+ String get appSettings_appDebugLoggingDisabled =>
+ 'アプリケーションのデバッグログが無効化されています。';
+
+ @override
+ String get contacts_title => '連絡先';
+
+ @override
+ String get contacts_noContacts => '現時点では、連絡先はまだありません。';
+
+ @override
+ String get contacts_contactsWillAppear => 'デバイスが広告を行う際に、連絡先が表示されます。';
+
+ @override
+ String get contacts_unread => '未読';
+
+ @override
+ String get contacts_searchContactsNoNumber => '連絡先を検索...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return '$number件の$strに関する連絡先を検索...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return '$number件の$strを検索...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return '$number件の$strに関するユーザーを検索する...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return '$number $str までの検索...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return '$number $str 部屋のサーバーを検索する...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => '未読の連絡先はありません';
+
+ @override
+ String get contacts_noContactsFound => '連絡先またはグループは見つかりませんでした。';
+
+ @override
+ String get contacts_deleteContact => '連絡先を削除';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return '$contactName を連絡先から削除しますか?';
+ }
+
+ @override
+ String get contacts_manageRepeater => 'リピーターの管理';
+
+ @override
+ String get contacts_manageRoom => 'ルームサーバーの管理';
+
+ @override
+ String get contacts_roomLogin => 'ルームサーバーへのログイン';
+
+ @override
+ String get contacts_openChat => '自由な会話';
+
+ @override
+ String get contacts_editGroup => '編集グループ';
+
+ @override
+ String get contacts_deleteGroup => 'グループを削除';
+
+ @override
+ String contacts_deleteGroupConfirm(String groupName) {
+ return '$groupName を削除しますか?';
+ }
+
+ @override
+ String get contacts_newGroup => '新しいグループ';
+
+ @override
+ String get contacts_groupName => 'グループ名';
+
+ @override
+ String get contacts_groupNameRequired => 'グループ名が必須です';
+
+ @override
+ String get contacts_groupNameReserved => 'このグループ名はすでに使用されています。';
+
+ @override
+ String contacts_groupAlreadyExists(String name) {
+ return 'グループ「$name」はすでに存在しています';
+ }
+
+ @override
+ String get contacts_filterContacts => '連絡先をフィルタリングする…';
+
+ @override
+ String get contacts_noContactsMatchFilter => '指定された条件に合致する連絡先は見つかりませんでした。';
+
+ @override
+ String get contacts_noMembers => 'メンバーはいない';
+
+ @override
+ String get contacts_lastSeenNow => '最近';
+
+ @override
+ String contacts_lastSeenMinsAgo(int minutes) {
+ return '~$minutes 分';
+ }
+
+ @override
+ String get contacts_lastSeenHourAgo => '約1時間';
+
+ @override
+ String contacts_lastSeenHoursAgo(int hours) {
+ return '~ $hours 時間';
+ }
+
+ @override
+ String get contacts_lastSeenDayAgo => '~1日';
+
+ @override
+ String contacts_lastSeenDaysAgo(int days) {
+ return '~$days日間';
+ }
+
+ @override
+ String get contact_info => '連絡先';
+
+ @override
+ String get contact_settings => '連絡設定';
+
+ @override
+ String get contact_telemetry => 'テレメトリー';
+
+ @override
+ String get contact_lastSeen => '最後に確認された場所';
+
+ @override
+ String get contact_clearChat => 'チャットのクリア';
+
+ @override
+ String get contact_teleBase => 'テレメトリ基地';
+
+ @override
+ String get contact_teleBaseSubtitle => 'バッテリー残量と基本的なテレメトリーの共有を許可する';
+
+ @override
+ String get contact_teleLoc => 'テレメトリの場所';
+
+ @override
+ String get contact_teleLocSubtitle => '位置情報共有を許可する';
+
+ @override
+ String get contact_teleEnv => 'テレメトリ環境';
+
+ @override
+ String get contact_teleEnvSubtitle => '環境センサーのデータを共有することを許可する';
+
+ @override
+ String get channels_title => 'チャンネル';
+
+ @override
+ String get channels_noChannelsConfigured => '設定されたチャンネルがありません';
+
+ @override
+ String get channels_addPublicChannel => 'パブリックチャンネルを追加する';
+
+ @override
+ String get channels_searchChannels => '検索オプション...';
+
+ @override
+ String get channels_noChannelsFound => 'チャンネルが見つかりませんでした';
+
+ @override
+ String channels_channelIndex(int index) {
+ return 'チャンネル $index';
+ }
+
+ @override
+ String get channels_hashtagChannel => 'ハッシュタグチャンネル';
+
+ @override
+ String get channels_public => '一般の人々';
+
+ @override
+ String get channels_private => '個人の';
+
+ @override
+ String get channels_publicChannel => '一般チャンネル';
+
+ @override
+ String get channels_privateChannel => 'プライベートチャンネル';
+
+ @override
+ String get channels_editChannel => 'チャンネルを編集する';
+
+ @override
+ String get channels_muteChannel => 'ミュート機能';
+
+ @override
+ String get channels_unmuteChannel => 'ミュートを解除する';
+
+ @override
+ String get channels_deleteChannel => 'チャンネルを削除する';
+
+ @override
+ String channels_deleteChannelConfirm(String name) {
+ return '$name を削除しますか? これは取り消すことができません。';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'チャンネル「$name」の削除に失敗しました。';
+ }
+
+ @override
+ String channels_channelDeleted(String name) {
+ return 'チャンネル「$name」が削除されました';
+ }
+
+ @override
+ String get channels_addChannel => 'チャンネルを追加';
+
+ @override
+ String get channels_channelIndexLabel => 'チャンネルインデックス';
+
+ @override
+ String get channels_channelName => 'チャンネル名';
+
+ @override
+ String get channels_usePublicChannel => 'パブリックチャンネルを使用する';
+
+ @override
+ String get channels_standardPublicPsk => '標準的な公用 PSK';
+
+ @override
+ String get channels_pskHex => 'PSK (ヘックス)';
+
+ @override
+ String get channels_generateRandomPsk => 'ランダムなPSK(正交符号分割変調)を生成する';
+
+ @override
+ String get channels_enterChannelName => 'チャンネル名を入力してください';
+
+ @override
+ String get channels_pskMustBe32Hex => 'PSKは32桁の16進数で構成されている必要があります。';
+
+ @override
+ String channels_channelAdded(String name) {
+ return 'チャンネル「$name」を追加';
+ }
+
+ @override
+ String channels_editChannelTitle(int index) {
+ return 'チャンネル $index の編集';
+ }
+
+ @override
+ String get channels_smazCompression => 'SMAZ 圧縮';
+
+ @override
+ String channels_channelUpdated(String name) {
+ return 'チャンネル「$name」が更新されました';
+ }
+
+ @override
+ String get channels_publicChannelAdded => 'パブリックチャンネルが追加されました';
+
+ @override
+ String get channels_sortBy => '並び替え';
+
+ @override
+ String get channels_sortManual => 'マニュアル';
+
+ @override
+ String get channels_sortAZ => 'AからZ';
+
+ @override
+ String get channels_sortLatestMessages => '最新のメッセージ';
+
+ @override
+ String get channels_sortUnread => '未読';
+
+ @override
+ String get channels_createPrivateChannel => 'プライベートチャンネルを作成する';
+
+ @override
+ String get channels_createPrivateChannelDesc => '秘密鍵を使用して保護されています。';
+
+ @override
+ String get channels_joinPrivateChannel => 'プライベートチャンネルに参加する';
+
+ @override
+ String get channels_joinPrivateChannelDesc => '手動で秘密のキーを入力する。';
+
+ @override
+ String get channels_joinPublicChannel => '公開チャンネルに参加する';
+
+ @override
+ String get channels_joinPublicChannelDesc => 'このチャンネルには、誰でも参加できます。';
+
+ @override
+ String get channels_joinHashtagChannel => 'ハッシュタグチャンネルに参加する';
+
+ @override
+ String get channels_joinHashtagChannelDesc => '誰でもハッシュタグチャンネルに参加できます。';
+
+ @override
+ String get channels_scanQrCode => 'QRコードをスキャンする';
+
+ @override
+ String get channels_scanQrCodeComingSoon => '近日公開';
+
+ @override
+ String get channels_enterHashtag => 'ハッシュタグを入力してください';
+
+ @override
+ String get channels_hashtagHint => '例:#チーム';
+
+ @override
+ String get chat_noMessages => 'まだメッセージは届いていません';
+
+ @override
+ String get chat_sendMessage => 'メッセージを送信する';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return '$contactName へのメッセージを送信する';
+ }
+
+ @override
+ String get chat_sendMessageToStart => '開始するためにメッセージを送信してください';
+
+ @override
+ String get chat_originalMessageNotFound => '元のメッセージが見つかりませんでした';
+
+ @override
+ String chat_replyingTo(String name) {
+ return '$name への返信';
+ }
+
+ @override
+ String chat_replyTo(String name) {
+ return '$nameへの返信';
+ }
+
+ @override
+ String get chat_location => '場所';
+
+ @override
+ String get chat_typeMessage => 'メッセージを入力してください…';
+
+ @override
+ String chat_messageTooLong(int maxBytes) {
+ return 'メッセージが長すぎる($maxBytes バイトを超える)。';
+ }
+
+ @override
+ String get chat_messageCopied => 'メッセージがコピーされました';
+
+ @override
+ String get chat_messageDeleted => 'メッセージは削除されました';
+
+ @override
+ String get chat_retryingMessage => '再試行メッセージ';
+
+ @override
+ String chat_retryCount(int current, int max) {
+ return '$current / $max 回目';
+ }
+
+ @override
+ String get chat_sendGif => 'GIFを送信する';
+
+ @override
+ String get chat_reply => '返信';
+
+ @override
+ String get chat_addReaction => '反応を追加';
+
+ @override
+ String get chat_me => '私';
+
+ @override
+ String get emojiCategorySmileys => '笑顔の絵文字';
+
+ @override
+ String get emojiCategoryGestures => '身振り、動作';
+
+ @override
+ String get emojiCategoryHearts => '心';
+
+ @override
+ String get emojiCategoryObjects => '対象物';
+
+ @override
+ String get gifPicker_title => 'GIF を選択してください';
+
+ @override
+ String get gifPicker_searchHint => 'GIFの検索...';
+
+ @override
+ String get gifPicker_poweredBy => 'GIPHYによる提供';
+
+ @override
+ String get gifPicker_noGifsFound => 'GIF形式のファイルは見つかりませんでした';
+
+ @override
+ String get gifPicker_failedLoad => 'GIFファイルの読み込みに失敗しました';
+
+ @override
+ String get gifPicker_failedSearch => 'GIFファイルの検索に失敗しました';
+
+ @override
+ String get gifPicker_noInternet => 'インターネット接続なし';
+
+ @override
+ String get debugLog_appTitle => 'アプリケーションのデバッグログ';
+
+ @override
+ String get debugLog_bleTitle => 'BLE デバッグログ';
+
+ @override
+ String get debugLog_copyLog => '記録';
+
+ @override
+ String get debugLog_clearLog => '詳細なログ';
+
+ @override
+ String get debugLog_copied => 'デバッグログをコピー';
+
+ @override
+ String get debugLog_bleCopied => 'BLEログのコピー';
+
+ @override
+ String get debugLog_noEntries => 'デバッグログはまだ生成されていません';
+
+ @override
+ String get debugLog_enableInSettings => 'アプリのデバッグログを有効にするには、設定から操作してください。';
+
+ @override
+ String get debugLog_frames => 'フレーム';
+
+ @override
+ String get debugLog_rawLogRx => '生のログ-RX';
+
+ @override
+ String get debugLog_noBleActivity => '現時点では、BLE関連の活動は行われていません。';
+
+ @override
+ String debugFrame_length(int count) {
+ return 'フレーム長: $count バイト';
+ }
+
+ @override
+ String debugFrame_command(String value) {
+ return 'コマンド: 0x$value';
+ }
+
+ @override
+ String get debugFrame_textMessageHeader => 'テキストメッセージ用フレーム:';
+
+ @override
+ String debugFrame_destinationPubKey(String pubKey) {
+ return '- 宛先公開鍵: $pubKey';
+ }
+
+ @override
+ String debugFrame_timestamp(int timestamp) {
+ return '- タイムスタンプ: $timestamp';
+ }
+
+ @override
+ String debugFrame_flags(String value) {
+ return '- フラグ: 0x$value';
+ }
+
+ @override
+ String debugFrame_textType(int type, String label) {
+ return '- テキストの種類: $type ($label)';
+ }
+
+ @override
+ String get debugFrame_textTypeCli => 'CLI(コマンドラインインターフェース)';
+
+ @override
+ String get debugFrame_textTypePlain => 'シンプルな';
+
+ @override
+ String debugFrame_text(String text) {
+ return '- テキスト:「$text」';
+ }
+
+ @override
+ String get debugFrame_hexDump => 'ヘックスダンプ:';
+
+ @override
+ String get chat_pathManagement => '経路管理';
+
+ @override
+ String get chat_ShowAllPaths => 'すべての経路を表示';
+
+ @override
+ String get chat_routingMode => 'ルーティングモード';
+
+ @override
+ String get chat_autoUseSavedPath => '自動 (保存されたパスを使用)';
+
+ @override
+ String get chat_forceFloodMode => '強制的に洪水モードを起動';
+
+ @override
+ String get chat_recentAckPaths => '最近使用したACKパス(タップして使用):';
+
+ @override
+ String get chat_pathHistoryFull => 'パスの履歴は完全です。エントリを削除して、新しいものを追加できます。';
+
+ @override
+ String get chat_hopSingular => 'ジャンプ';
+
+ @override
+ String get chat_hopPlural => 'ホップ';
+
+ @override
+ String chat_hopsCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'ホップ',
+ one: 'ホップ',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String get chat_successes => '成功事例';
+
+ @override
+ String get chat_removePath => 'パスを削除する';
+
+ @override
+ String get chat_noPathHistoryYet => 'まだ履歴はありません。\nパスを特定するためにメッセージを送信してください。';
+
+ @override
+ String get chat_pathActions => 'パスの操作:';
+
+ @override
+ String get chat_setCustomPath => 'カスタムパスを設定';
+
+ @override
+ String get chat_setCustomPathSubtitle => '手動で経路を指定する';
+
+ @override
+ String get chat_clearPath => '明確な道';
+
+ @override
+ String get chat_clearPathSubtitle => '次回送信時に、以前の情報を再取得する';
+
+ @override
+ String get chat_pathCleared => '経路が確保されました。次のメッセージでルートを再確認します。';
+
+ @override
+ String get chat_floodModeSubtitle => 'アプリのバーにあるルーティング切り替え機能を使用する';
+
+ @override
+ String get chat_floodModeEnabled =>
+ '洪水モードが有効になっています。アプリのメニューバーにあるルートアイコンを使用して、モードを切り替えることができます。';
+
+ @override
+ String get chat_fullPath => 'フルパス';
+
+ @override
+ String get chat_pathDetailsNotAvailable =>
+ '経路の詳細については、まだ情報がありません。「リフレッシュ」ボタンを押して、再度お試しください。';
+
+ @override
+ String chat_pathSetHops(int hopCount, String status) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hopCount,
+ locale: localeName,
+ other: 'hops',
+ one: 'hop',
+ );
+ return 'Path set: $hopCount $_temp0 - $status';
+ }
+
+ @override
+ String get chat_pathSavedLocally => 'ローカルで保存。同期のために接続する。';
+
+ @override
+ String get chat_pathDeviceConfirmed => 'デバイスの確認済み。';
+
+ @override
+ String get chat_pathDeviceNotConfirmed => 'デバイスの確認はまだできていません。';
+
+ @override
+ String get chat_type => '種類';
+
+ @override
+ String get chat_path => '道';
+
+ @override
+ String get chat_publicKey => '公開鍵';
+
+ @override
+ String get chat_compressOutgoingMessages => '送信されるメッセージを圧縮する';
+
+ @override
+ String get chat_floodForced => '洪水(強制的な)';
+
+ @override
+ String get chat_directForced => '直接的な(強制的な)';
+
+ @override
+ String chat_hopsForced(int count) {
+ return '$count 本のホップ(強制的に採取)';
+ }
+
+ @override
+ String get chat_floodAuto => '洪水 (自動)';
+
+ @override
+ String get chat_direct => '直接';
+
+ @override
+ String get chat_poiShared => '共有されたPOI';
+
+ @override
+ String chat_unread(int count) {
+ return '未読: $count';
+ }
+
+ @override
+ String get chat_openLink => 'リンクを開く?';
+
+ @override
+ String get chat_openLinkConfirmation => 'このリンクをブラウザで開くことはご希望ですか?';
+
+ @override
+ String get chat_open => '開く';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'リンクを開けられませんでした: $url';
+ }
+
+ @override
+ String get chat_invalidLink => '無効なリンク形式';
+
+ @override
+ String get map_title => 'ノードマップ';
+
+ @override
+ String get map_lineOfSight => '視界';
+
+ @override
+ String get map_losScreenTitle => '視界';
+
+ @override
+ String get map_noNodesWithLocation => '位置情報データを持つノードは存在しません';
+
+ @override
+ String get map_nodesNeedGps => 'ノードは、地図上に表示されるために、GPS座標を共有する必要があります。';
+
+ @override
+ String map_nodesCount(int count) {
+ return 'ノード:$count';
+ }
+
+ @override
+ String map_pinsCount(int count) {
+ return 'ピン:$count個';
+ }
+
+ @override
+ String get map_chat => 'チャット';
+
+ @override
+ String get map_repeater => '繰り返し送信装置';
+
+ @override
+ String get map_room => '部屋';
+
+ @override
+ String get map_sensor => 'センサー';
+
+ @override
+ String get map_pinDm => 'ピン(DM)';
+
+ @override
+ String get map_pinPrivate => 'プライベート(非公開)';
+
+ @override
+ String get map_pinPublic => '公開 (一般公開)';
+
+ @override
+ String get map_lastSeen => '最後に確認された場所';
+
+ @override
+ String get map_disconnectConfirm => '本当にこのデバイスとの接続を解除したいですか?';
+
+ @override
+ String get map_from => '~から';
+
+ @override
+ String get map_source => '出典';
+
+ @override
+ String get map_flags => '旗';
+
+ @override
+ String get map_shareMarkerHere => 'この場所でシェア';
+
+ @override
+ String get map_setAsMyLocation => '現在地として設定';
+
+ @override
+ String get map_pinLabel => 'ピンラベル';
+
+ @override
+ String get map_label => 'ラベル';
+
+ @override
+ String get map_pointOfInterest => '注目すべき点';
+
+ @override
+ String get map_sendToContact => '連絡先へ送信';
+
+ @override
+ String get map_sendToChannel => '特定のチャンネルに送信する';
+
+ @override
+ String get map_noChannelsAvailable => '利用可能なチャンネルはありません';
+
+ @override
+ String get map_publicLocationShare => '公共スペースの共有';
+
+ @override
+ String map_publicLocationShareConfirm(String channelLabel) {
+ return '現在、$channelLabel で位置情報を共有する準備をしています。このチャンネルは公開されており、PSK を持つ誰でも閲覧できます。';
+ }
+
+ @override
+ String get map_connectToShareMarkers => '他のデバイスと接続して、マーカーを共有する';
+
+ @override
+ String get map_filterNodes => 'フィルタノード';
+
+ @override
+ String get map_nodeTypes => 'ノードの種類';
+
+ @override
+ String get map_chatNodes => 'チャットノード';
+
+ @override
+ String get map_repeaters => '繰り返し送信装置';
+
+ @override
+ String get map_otherNodes => 'その他のノード';
+
+ @override
+ String get map_showOverlaps => 'リピーターキーの重複';
+
+ @override
+ String get map_keyPrefix => '主要なプレフィックス';
+
+ @override
+ String get map_filterByKeyPrefix => '主要なプレフィックスでフィルタリングする';
+
+ @override
+ String get map_publicKeyPrefix => '公開鍵のプレフィックス';
+
+ @override
+ String get map_markers => 'マーカー';
+
+ @override
+ String get map_showSharedMarkers => '共有のマーカーを表示する';
+
+ @override
+ String get map_showGuessedLocations => '推測されたノードの位置を表示する';
+
+ @override
+ String get map_showDiscoveryContacts => 'Discovery社の連絡先を表示する';
+
+ @override
+ String get map_guessedLocation => '推測された場所';
+
+ @override
+ String get map_lastSeenTime => '最後に確認された時間';
+
+ @override
+ String get map_sharedPin => '共有パスワード';
+
+ @override
+ String get map_joinRoom => '部屋に参加する';
+
+ @override
+ String get map_manageRepeater => 'リピーターの管理';
+
+ @override
+ String get map_tapToAdd => 'ノードをクリックして、パスに追加します。';
+
+ @override
+ String get map_runTrace => 'パスの追跡を実行';
+
+ @override
+ String get map_runTraceWithReturnPath => '元の経路に戻る。';
+
+ @override
+ String get map_removeLast => '最後のものを削除';
+
+ @override
+ String get map_pathTraceCancelled => 'パスの追跡は中止。';
+
+ @override
+ String get mapCache_title => 'オフライン用地図キャッシュ';
+
+ @override
+ String get mapCache_selectAreaFirst => '最初にキャッシュする領域を選択してください';
+
+ @override
+ String get mapCache_noTilesToDownload => 'この地域にはダウンロードできるタイルは存在しません。';
+
+ @override
+ String get mapCache_downloadTilesTitle => 'タイルをダウンロードする';
+
+ @override
+ String mapCache_downloadTilesPrompt(int count) {
+ return 'オフラインでの使用のために、$count個のタイルをダウンロードしますか?';
+ }
+
+ @override
+ String get mapCache_downloadAction => 'ダウンロード';
+
+ @override
+ String mapCache_cachedTiles(int count) {
+ return '$count 個のタイルをキャッシュ';
+ }
+
+ @override
+ String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
+ return 'Cached $downloaded tiles ($failed failed)';
+ }
+
+ @override
+ String get mapCache_clearOfflineCacheTitle => 'オフラインキャッシュをクリアする';
+
+ @override
+ String get mapCache_clearOfflineCachePrompt => 'キャッシュされた地図のタイルをすべて削除しますか?';
+
+ @override
+ String get mapCache_offlineCacheCleared => 'オフラインキャッシュをクリア';
+
+ @override
+ String get mapCache_noAreaSelected => '選択されたエリアはありません';
+
+ @override
+ String get mapCache_cacheArea => 'キャッシュエリア';
+
+ @override
+ String get mapCache_useCurrentView => '現在表示されている内容を保持する';
+
+ @override
+ String get mapCache_zoomRange => 'ズーム範囲';
+
+ @override
+ String mapCache_estimatedTiles(int count) {
+ return '推定されるタイル数: $count';
+ }
+
+ @override
+ String mapCache_downloadedTiles(int completed, int total) {
+ return 'Downloaded $completed / $total';
+ }
+
+ @override
+ String get mapCache_downloadTilesButton => 'タイルをダウンロードする';
+
+ @override
+ String get mapCache_clearCacheButton => 'キャッシュをクリアする';
+
+ @override
+ String mapCache_failedDownloads(int count) {
+ return '失敗したダウンロード: $count';
+ }
+
+ @override
+ String mapCache_boundsLabel(
+ String north,
+ String south,
+ String east,
+ String west,
+ ) {
+ return 'N $north, S $south, E $east, W $west';
+ }
+
+ @override
+ String get time_justNow => 'まさに今';
+
+ @override
+ String time_minutesAgo(int minutes) {
+ return '$minutes分前';
+ }
+
+ @override
+ String time_hoursAgo(int hours) {
+ return '$hours時間前';
+ }
+
+ @override
+ String time_daysAgo(int days) {
+ return '$days日前';
+ }
+
+ @override
+ String get time_hour => '1時間';
+
+ @override
+ String get time_hours => '時間';
+
+ @override
+ String get time_day => '一日';
+
+ @override
+ String get time_days => '日';
+
+ @override
+ String get time_week => '1週間';
+
+ @override
+ String get time_weeks => '週';
+
+ @override
+ String get time_month => '月';
+
+ @override
+ String get time_months => '月';
+
+ @override
+ String get time_minutes => '分';
+
+ @override
+ String get time_allTime => '全期間';
+
+ @override
+ String get dialog_disconnect => '切断する';
+
+ @override
+ String get dialog_disconnectConfirm => '本当にこのデバイスとの接続を解除したいですか?';
+
+ @override
+ String get login_repeaterLogin => '再ログイン';
+
+ @override
+ String get login_roomLogin => 'ルームサーバーへのログイン';
+
+ @override
+ String get login_password => 'パスワード';
+
+ @override
+ String get login_enterPassword => 'パスワードを入力してください';
+
+ @override
+ String get login_savePassword => 'パスワードを保存する';
+
+ @override
+ String get login_savePasswordSubtitle => 'パスワードは、このデバイスに安全に保存されます。';
+
+ @override
+ String get login_repeaterDescription =>
+ '設定やステータスにアクセスするために、リピーターのパスワードを入力してください。';
+
+ @override
+ String get login_roomDescription => '設定やステータスへのアクセスには、部屋のパスワードを入力してください。';
+
+ @override
+ String get login_routing => '経路設定';
+
+ @override
+ String get login_routingMode => 'ルーティングモード';
+
+ @override
+ String get login_autoUseSavedPath => '自動 (保存されたパスを使用)';
+
+ @override
+ String get login_forceFloodMode => '強制的に洪水モードを起動';
+
+ @override
+ String get login_managePaths => 'パスの管理';
+
+ @override
+ String get login_login => 'ログイン';
+
+ @override
+ String login_attempt(int current, int max) {
+ return '試行回数:$current/$max';
+ }
+
+ @override
+ String login_failed(String error) {
+ return 'ログインに失敗しました:$error';
+ }
+
+ @override
+ String get login_failedMessage =>
+ 'ログインに失敗しました。パスワードが間違っているか、または接続が確立されていません。';
+
+ @override
+ String get common_reload => '再読み込み';
+
+ @override
+ String get common_clear => '明確';
+
+ @override
+ String path_currentPath(String path) {
+ return '現在のパス: $path';
+ }
+
+ @override
+ String path_usingHopsPath(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'ホップ',
+ one: 'ホップ',
+ );
+ return '$count $_temp0のパスを使用';
+ }
+
+ @override
+ String get path_enterCustomPath => 'カスタムパスを入力';
+
+ @override
+ String get path_currentPathLabel => '現在の経路';
+
+ @override
+ String get path_hexPrefixInstructions =>
+ '各ホップに対して、2文字の16進数プレフィックスをカンマで区切って入力してください。';
+
+ @override
+ String get path_hexPrefixExample => '例:A1, F2, 3C (各ノードは、自身の公開鍵の最初のバイトを使用)';
+
+ @override
+ String get path_labelHexPrefixes => 'パス (ヘックスプレフィックス)';
+
+ @override
+ String get path_helperMaxHops =>
+ '最大64個のホップ。各プレフィックスは2つの16進数文字(1バイト)で構成されています。';
+
+ @override
+ String get path_selectFromContacts => 'または、連絡先リストから選択してください:';
+
+ @override
+ String get path_noRepeatersFound => '繰り返し機能やルームサーバーは見つかりませんでした。';
+
+ @override
+ String get path_customPathsRequire => 'カスタムパスには、メッセージを中継できる中間地点が必要です。';
+
+ @override
+ String path_invalidHexPrefixes(String prefixes) {
+ return '無効な16進数プレフィックス: $prefixes';
+ }
+
+ @override
+ String get path_tooLong => '経路が長すぎる。最大64回のジャンプのみ許可。';
+
+ @override
+ String get path_setPath => 'パスを設定';
+
+ @override
+ String get repeater_management => 'リピーター管理';
+
+ @override
+ String get room_management => 'ルームサーバーの管理';
+
+ @override
+ String get repeater_guest => '繰り返し送信に関する情報';
+
+ @override
+ String get room_guest => 'ルームサーバーに関する情報';
+
+ @override
+ String get repeater_managementTools => '管理ツール';
+
+ @override
+ String get repeater_guestTools => 'ゲスト向けツール';
+
+ @override
+ String get repeater_status => 'ステータス';
+
+ @override
+ String get repeater_statusSubtitle => 'リピーターの状態、統計情報、および隣接するネットワークの情報を表示する';
+
+ @override
+ String get repeater_telemetry => 'テレメトリー';
+
+ @override
+ String get repeater_telemetrySubtitle => 'センサーおよびシステムの状態に関するテレメトリの表示';
+
+ @override
+ String get repeater_cli => 'CLI(コマンドラインインターフェース)';
+
+ @override
+ String get repeater_cliSubtitle => 'リピーターへのコマンドを送信する';
+
+ @override
+ String get repeater_neighbors => '近隣住民';
+
+ @override
+ String get repeater_neighborsSubtitle => 'ゼロホップの隣接ノードを表示する。';
+
+ @override
+ String get repeater_settings => '設定';
+
+ @override
+ String get repeater_settingsSubtitle => 'リピーターのパラメータを設定する';
+
+ @override
+ String get repeater_clockSyncAfterLogin => 'ログイン後、時計の時刻を同期する';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'ログインが成功した場合、自動的に「時刻同期」を送信する。';
+
+ @override
+ String get repeater_statusTitle => '再送ステータス';
+
+ @override
+ String get repeater_routingMode => 'ルーティングモード';
+
+ @override
+ String get repeater_autoUseSavedPath => '自動 (保存されたパスを使用)';
+
+ @override
+ String get repeater_forceFloodMode => '強制的に洪水モードを起動';
+
+ @override
+ String get repeater_pathManagement => '経路管理';
+
+ @override
+ String get repeater_refresh => 'リフレッシュ';
+
+ @override
+ String get repeater_statusRequestTimeout => 'ステータス情報の取得に失敗しました。';
+
+ @override
+ String repeater_errorLoadingStatus(String error) {
+ return 'ステータス読み込みエラー: $error';
+ }
+
+ @override
+ String get repeater_systemInformation => 'システム情報';
+
+ @override
+ String get repeater_battery => 'バッテリー';
+
+ @override
+ String get repeater_clockAtLogin => 'ログイン時の時刻表示';
+
+ @override
+ String get repeater_uptime => '稼働率';
+
+ @override
+ String get repeater_queueLength => '待ち行列の長さ';
+
+ @override
+ String get repeater_debugFlags => 'デバッグフラグ';
+
+ @override
+ String get repeater_radioStatistics => 'ラジオに関する統計';
+
+ @override
+ String get repeater_lastRssi => '最後のRSSI';
+
+ @override
+ String get repeater_lastSnr => '最後のSNR';
+
+ @override
+ String get repeater_noiseFloor => 'ノイズレベル';
+
+ @override
+ String get repeater_txAirtime => 'TXの放送時間';
+
+ @override
+ String get repeater_rxAirtime => 'RX 空き時間';
+
+ @override
+ String get repeater_packetStatistics => 'パケット統計';
+
+ @override
+ String get repeater_sent => '送信';
+
+ @override
+ String get repeater_received => '受領';
+
+ @override
+ String get repeater_duplicates => '重複';
+
+ @override
+ String repeater_daysHoursMinsSecs(
+ int days,
+ int hours,
+ int minutes,
+ int seconds,
+ ) {
+ return '$days日 $hours時間 $minutes分 $seconds秒';
+ }
+
+ @override
+ String repeater_packetTxTotal(int total, String flood, String direct) {
+ return '合計: $total, 洪水: $flood, 直接: $direct';
+ }
+
+ @override
+ String repeater_packetRxTotal(int total, String flood, String direct) {
+ return '合計: $total, 洪水: $flood, 直接: $direct';
+ }
+
+ @override
+ String repeater_duplicatesFloodDirect(String flood, String direct) {
+ return '$flood: $flood, 直接: $direct';
+ }
+
+ @override
+ String repeater_duplicatesTotal(int total) {
+ return '合計: $total';
+ }
+
+ @override
+ String get repeater_settingsTitle => 'リピーター設定';
+
+ @override
+ String get repeater_basicSettings => '基本設定';
+
+ @override
+ String get repeater_repeaterName => '送信装置名';
+
+ @override
+ String get repeater_repeaterNameHelper => 'このリピーターの名前';
+
+ @override
+ String get repeater_adminPassword => '管理者パスワード';
+
+ @override
+ String get repeater_adminPasswordHelper => '完全アクセス権のパスワード';
+
+ @override
+ String get repeater_guestPassword => 'ゲスト用のパスワード';
+
+ @override
+ String get repeater_guestPasswordHelper => '読み取り専用アクセス用のパスワード';
+
+ @override
+ String get repeater_radioSettings => 'ラジオ設定';
+
+ @override
+ String get repeater_frequencyMhz => '周波数 (MHz)';
+
+ @override
+ String get repeater_frequencyHelper => '300~2500 MHz';
+
+ @override
+ String get repeater_txPower => 'TXパワー';
+
+ @override
+ String get repeater_txPowerHelper => '-30~-10 dBm';
+
+ @override
+ String get repeater_bandwidth => '帯域幅';
+
+ @override
+ String get repeater_spreadingFactor => '伝播係数';
+
+ @override
+ String get repeater_codingRate => 'コーディング速度';
+
+ @override
+ String get repeater_locationSettings => '場所設定';
+
+ @override
+ String get repeater_latitude => '緯度';
+
+ @override
+ String get repeater_latitudeHelper => '度分表記(例:37.7749)';
+
+ @override
+ String get repeater_longitude => '経度';
+
+ @override
+ String get repeater_longitudeHelper => '度分表記(例:-122.4194)';
+
+ @override
+ String get repeater_features => '特徴';
+
+ @override
+ String get repeater_packetForwarding => 'パケット転送';
+
+ @override
+ String get repeater_packetForwardingSubtitle => 'リピーターがパケットを転送できるように設定する';
+
+ @override
+ String get repeater_guestAccess => 'ゲストへのアクセス';
+
+ @override
+ String get repeater_guestAccessSubtitle => 'ゲストへの読み取り専用アクセスを許可する';
+
+ @override
+ String get repeater_privacyMode => 'プライバシーモード';
+
+ @override
+ String get repeater_privacyModeSubtitle => '広告に名前/場所を記載しない';
+
+ @override
+ String get repeater_advertisementSettings => '広告設定';
+
+ @override
+ String get repeater_localAdvertInterval => '地域広告掲載期間';
+
+ @override
+ String repeater_localAdvertIntervalMinutes(int minutes) {
+ return '$minutes 分';
+ }
+
+ @override
+ String get repeater_floodAdvertInterval => '洪水に関する広告の表示間隔';
+
+ @override
+ String repeater_floodAdvertIntervalHours(int hours) {
+ return '$hours 時間';
+ }
+
+ @override
+ String get repeater_encryptedAdvertInterval => '暗号化された広告表示間';
+
+ @override
+ String get repeater_dangerZone => '危険区域';
+
+ @override
+ String get repeater_rebootRepeater => 'リピーターを再起動する';
+
+ @override
+ String get repeater_rebootRepeaterSubtitle => 'リピーターデバイスを再起動する';
+
+ @override
+ String get repeater_rebootRepeaterConfirm => '本当にこのリピーターを再起動したいですか?';
+
+ @override
+ String get repeater_regenerateIdentityKey => 'IDキーの再生成';
+
+ @override
+ String get repeater_regenerateIdentityKeySubtitle => '新しい公開鍵/秘密鍵のペアを生成する';
+
+ @override
+ String get repeater_regenerateIdentityKeyConfirm =>
+ 'これにより、リピーターには新しい識別情報が割り当てられます。続行しますか?';
+
+ @override
+ String get repeater_eraseFileSystem => 'ファイルシステムを削除する';
+
+ @override
+ String get repeater_eraseFileSystemSubtitle => 'リピーターファイルシステムをフォーマットする';
+
+ @override
+ String get repeater_eraseFileSystemConfirm =>
+ '警告:この操作により、リピーター内のすべてのデータが消去されます。この操作は元に戻すことができません!';
+
+ @override
+ String get repeater_eraseSerialOnly => 'Erase機能は、シリアルコンソール経由でのみ利用可能です。';
+
+ @override
+ String repeater_commandSent(String command) {
+ return '送信されたコマンド: $command';
+ }
+
+ @override
+ String repeater_errorSendingCommand(String error) {
+ return 'コマンド送信エラー:$error';
+ }
+
+ @override
+ String get repeater_confirm => '確認';
+
+ @override
+ String get repeater_settingsSaved => '設定が正常に保存されました';
+
+ @override
+ String repeater_errorSavingSettings(String error) {
+ return '設定の保存に失敗しました:$error';
+ }
+
+ @override
+ String get repeater_refreshBasicSettings => '基本設定をリセットする';
+
+ @override
+ String get repeater_refreshRadioSettings => 'ラジオ設定をリセットする';
+
+ @override
+ String get repeater_refreshTxPower => 'TX の電力レベルをリセットする';
+
+ @override
+ String get repeater_refreshLocationSettings => '場所設定をリセットする';
+
+ @override
+ String get repeater_refreshPacketForwarding => 'パケット転送の刷新';
+
+ @override
+ String get repeater_refreshGuestAccess => 'ゲストへのアクセスをリフレッシュする';
+
+ @override
+ String get repeater_refreshPrivacyMode => 'プライバシーモードをリセットする';
+
+ @override
+ String get repeater_refreshAdvertisementSettings => '広告設定のリセット';
+
+ @override
+ String repeater_refreshed(String label) {
+ return '$label が更新されました';
+ }
+
+ @override
+ String repeater_errorRefreshing(String label) {
+ return '$label の更新に失敗しました';
+ }
+
+ @override
+ String get repeater_cliTitle => 'リピーターのコマンドラインインターフェース';
+
+ @override
+ String get repeater_debugNextCommand => '次のコマンドのデバッグ';
+
+ @override
+ String get repeater_commandHelp => 'コマンドヘルプ';
+
+ @override
+ String get repeater_clearHistory => '明確な歴史';
+
+ @override
+ String get repeater_noCommandsSent => 'まだコマンドは送信されていません';
+
+ @override
+ String get repeater_typeCommandOrUseQuick =>
+ '以下のコマンドを入力するか、クイックコマンドを使用してください。';
+
+ @override
+ String get repeater_enterCommandHint => 'コマンドを入力してください...';
+
+ @override
+ String get repeater_previousCommand => '直前の指示';
+
+ @override
+ String get repeater_nextCommand => '次の指示';
+
+ @override
+ String get repeater_enterCommandFirst => 'まず、コマンドを入力してください。';
+
+ @override
+ String get repeater_cliCommandFrameTitle => 'CLI コマンドフレーム';
+
+ @override
+ String repeater_cliCommandError(String error) {
+ return 'エラー:$error';
+ }
+
+ @override
+ String get repeater_cliQuickGetName => '名前を取得する';
+
+ @override
+ String get repeater_cliQuickGetRadio => 'ラジオを聴く';
+
+ @override
+ String get repeater_cliQuickGetTx => 'TXを入手する';
+
+ @override
+ String get repeater_cliQuickNeighbors => '近隣住民';
+
+ @override
+ String get repeater_cliQuickVersion => 'バージョン';
+
+ @override
+ String get repeater_cliQuickAdvertise => '広告';
+
+ @override
+ String get repeater_cliQuickClock => '時計';
+
+ @override
+ String get repeater_cliQuickClockSync => 'クロック同期';
+
+ @override
+ String get repeater_cliQuickDiscovery => '近隣を発見する';
+
+ @override
+ String get repeater_cliHelpAdvert => '広告用資料を送る';
+
+ @override
+ String get repeater_cliHelpReboot =>
+ 'デバイスを再起動します。(注:通常は「タイムアウト」が表示されますが、これは正常です)';
+
+ @override
+ String get repeater_cliHelpClock => '各デバイスの時計で現在の時刻を表示します。';
+
+ @override
+ String get repeater_cliHelpPassword => 'デバイス用の新しい管理者パスワードを設定します。';
+
+ @override
+ String get repeater_cliHelpVersion => 'デバイスのバージョンとファームウェアのビルド日を表示します。';
+
+ @override
+ String get repeater_cliHelpClearStats => 'さまざまな統計カウンターをゼロにリセットする。';
+
+ @override
+ String get repeater_cliHelpSetAf => '空き時間係数を設定します。';
+
+ @override
+ String get repeater_cliHelpSetTx => 'LoRaの送信電力をdBmで設定します。(設定変更後、再起動が必要です)';
+
+ @override
+ String get repeater_cliHelpSetRepeat => 'このノードに対するリピーターの役割を有効化または無効化します。';
+
+ @override
+ String get repeater_cliHelpSetAllowReadOnly =>
+ '(ルームサーバー設定)「オン」に設定した場合、空白のパスワードでのログインは可能ですが、ルームへの投稿はできません。(閲覧のみ)';
+
+ @override
+ String get repeater_cliHelpSetFloodMax =>
+ 'インバウンドフラッパケットの最大ホップ数を設定します(最大値を超えた場合、パケットは転送されません)。';
+
+ @override
+ String get repeater_cliHelpSetIntThresh =>
+ '干渉閾値を設定します(dB単位)。デフォルト値は14です。0に設定すると、チャンネル間の干渉を検出する機能を無効にします。';
+
+ @override
+ String get repeater_cliHelpSetAgcResetInterval =>
+ 'オートゲインコントローラーのリセット間隔を設定します。 0 に設定すると無効化されます。';
+
+ @override
+ String get repeater_cliHelpSetMultiAcks => '「ダブルACK」機能の有効化または無効化を可能にします。';
+
+ @override
+ String get repeater_cliHelpSetAdvertInterval =>
+ 'ローカル(ホップなし)の広告パケットを送信する間隔を分単位で設定します。 0 に設定すると、機能を無効にします。';
+
+ @override
+ String get repeater_cliHelpSetFloodAdvertInterval =>
+ '洪水広告の送信間隔を時間単位で設定します。0に設定すると、送信を停止します。';
+
+ @override
+ String get repeater_cliHelpSetGuestPassword =>
+ 'ゲストのパスワードを設定/更新します。(繰り返し利用の場合、ゲストのログインは「統計情報を取得」のリクエストを送信できます)';
+
+ @override
+ String get repeater_cliHelpSetName => '広告の名前を設定します。';
+
+ @override
+ String get repeater_cliHelpSetLat => '広告表示の地図の緯度を設定します。(度分秒表記)';
+
+ @override
+ String get repeater_cliHelpSetLon => '広告表示の地図の経度を設定します。(度数、分)';
+
+ @override
+ String get repeater_cliHelpSetRadio =>
+ '完全に新しいラジオパラメータを設定し、設定として保存します。適用するには、「再起動」コマンドが必要です。';
+
+ @override
+ String get repeater_cliHelpSetRxDelay =>
+ '(実験用)遅延時間を設定するためのベース(1以上の値に設定する必要)\n受信パケットに対して、信号強度/スコアに基づいてわずかな遅延を適用します。 0に設定すると無効化されます。';
+
+ @override
+ String get repeater_cliHelpSetTxDelay =>
+ '時間経過に応じた「フラッシュモード」パケットの送信遅延を設定します。この遅延は、ランダムなスロットシステムと組み合わせて使用され、パケットの衝突を減らすことを目的としています。';
+
+ @override
+ String get repeater_cliHelpSetDirectTxDelay =>
+ 'txdelayと同様ですが、ダイレクトモードのパケット転送にランダムな遅延を適用する場合に使用します。';
+
+ @override
+ String get repeater_cliHelpSetBridgeEnabled => 'ブリッジを有効化/無効化';
+
+ @override
+ String get repeater_cliHelpSetBridgeDelay => 'パケットを再送信する前に、遅延を設定する。';
+
+ @override
+ String get repeater_cliHelpSetBridgeSource =>
+ '橋が受信したパケットを再送信するか、送信したパケットを再送信するかどうかを選択してください。';
+
+ @override
+ String get repeater_cliHelpSetBridgeBaud =>
+ 'RS232 橋渡しに使用するシリアルリンクのボーレートを設定する。';
+
+ @override
+ String get repeater_cliHelpSetBridgeSecret => 'ESPNow 橋の秘密設定';
+
+ @override
+ String get repeater_cliHelpSetAdcMultiplier =>
+ '特定のボードでのみサポートされている、報告されるバッテリー電圧を調整するためのカスタムファクタを設定できます。';
+
+ @override
+ String get repeater_cliHelpTempRadio =>
+ '指定された時間(分単位)に対して、一時的にラジオパラメータを設定し、その後元のラジオパラメータに戻します。(設定を保存しません)。';
+
+ @override
+ String get repeater_cliHelpSetPerm =>
+ 'ACL を変更します。「permissions」が 0 の場合、対応するエントリ(pubkey のプレフィックスで識別)を削除します。pubkey-hex が有効な長さで、かつ ACL に現在存在しない場合に、新しいエントリを追加します。pubkey のプレフィックスと一致するエントリを更新します。権限ビットはファームウェアの役割によって異なり、下位 2 ビットは以下のとおりです:0 (ゲスト)、1 (読み取り専用)、2 (読み書き)、3 (管理者)';
+
+ @override
+ String get repeater_cliHelpGetBridgeType => 'ブリッジ機能なし、RS232、ESPNow';
+
+ @override
+ String get repeater_cliHelpLogStart => 'パケットのログ記録を開始し、ファイルシステムに保存する。';
+
+ @override
+ String get repeater_cliHelpLogStop => 'ファイルシステムへのパケットログの記録を停止する。';
+
+ @override
+ String get repeater_cliHelpLogErase => 'ファイルシステムからパケットログを削除する。';
+
+ @override
+ String get repeater_cliHelpNeighbors =>
+ 'ゼロホップ広告を通じて受信した他のリピーターノードの一覧を表示します。各行は、IDプレフィックス(16進数)、タイムスタンプ、SNR(シグナル強度)の情報を4つ含みます。';
+
+ @override
+ String get repeater_cliHelpNeighborRemove =>
+ '隣接リストから、最初に一致するエントリ(pubkeyプレフィックス(16進数)で特定)を削除します。';
+
+ @override
+ String get repeater_cliHelpRegion =>
+ '(特定のシリーズのみ)定義されたすべての地域と、現在の洪水許可状況を一覧表示します。';
+
+ @override
+ String get repeater_cliHelpRegionLoad =>
+ '注:これは特殊な複数コマンドの呼び出しです。その後の各コマンドは、地域名であり(スペースを使用して親階層を示し、少なくとも1つのスペースが必要です)、空行/コマンドで終了します。';
+
+ @override
+ String get repeater_cliHelpRegionGet =>
+ '指定された名前のプレフィックスを持つ地域を検索します(または、グローバルな範囲の場合は「*」)。結果として、「region-name (parent-name) \'F\'」と返答します。';
+
+ @override
+ String get repeater_cliHelpRegionPut => '指定された名前で、領域の定義を追加または更新します。';
+
+ @override
+ String get repeater_cliHelpRegionRemove =>
+ '指定された名前を持つ領域の定義を削除します。(正確に一致している必要があり、子領域は存在してはなりません)';
+
+ @override
+ String get repeater_cliHelpRegionAllowf =>
+ '指定された領域に対して、「洪水」アクセス許可を設定します。 (グローバル/従来のスコープには「*」を使用)';
+
+ @override
+ String get repeater_cliHelpRegionDenyf =>
+ '指定された領域における「FLOOD」権限を削除します。(注:現時点では、グローバル/従来の範囲での使用は推奨されません!)';
+
+ @override
+ String get repeater_cliHelpRegionHome =>
+ '現在の「ホーム」地域に返信します。(まだ適用されていない、将来利用を予定)';
+
+ @override
+ String get repeater_cliHelpRegionHomeSet => '「ホーム」地域を設定します。';
+
+ @override
+ String get repeater_cliHelpRegionSave => '領域リスト/マップをストレージに保存する。';
+
+ @override
+ String get repeater_cliHelpGps =>
+ 'GPSの状態を表示します。GPSがオフの場合、「オフ」と表示します。オンの場合、「オン」、「ステータス」、「位置情報」、「衛星数」と表示します。';
+
+ @override
+ String get repeater_cliHelpGpsOnOff => 'GPS の電源状態を切り替えます。';
+
+ @override
+ String get repeater_cliHelpGpsSync => 'ノードの時刻をGPSクロックと同期する。';
+
+ @override
+ String get repeater_cliHelpGpsSetLoc => 'ノードの位置をGPS座標に設定し、設定を保存する。';
+
+ @override
+ String get repeater_cliHelpGpsAdvert =>
+ 'ノードの広告設定における場所情報の指定:\n- none: 広告に場所情報を含まない\n- share: GPS位置情報を共有 (SensorManagerから取得)\n- prefs: プリファレンスに保存された場所情報を広告';
+
+ @override
+ String get repeater_cliHelpGpsAdvertSet => '場所に関する広告設定を行います。';
+
+ @override
+ String get repeater_commandsListTitle => 'コマンド一覧';
+
+ @override
+ String get repeater_commandsListNote =>
+ '注:さまざまな「set ...」コマンドには、「get ...」コマンドも存在します。';
+
+ @override
+ String get repeater_general => '一般的な';
+
+ @override
+ String get repeater_settingsCategory => '設定';
+
+ @override
+ String get repeater_bridge => '橋';
+
+ @override
+ String get repeater_logging => 'ログ記録';
+
+ @override
+ String get repeater_neighborsRepeaterOnly => '近隣住民(リピーターのみ)';
+
+ @override
+ String get repeater_regionManagementRepeaterOnly => '地域管理(ブロードキャスト用のみ)';
+
+ @override
+ String get repeater_regionNote => '地域レベルでの管理のため、地域定義と権限の管理を行うための機能が導入されました。';
+
+ @override
+ String get repeater_gpsManagement => 'GPS管理';
+
+ @override
+ String get repeater_gpsNote => 'GPSコマンドは、位置情報に関連するタスクを管理するために導入されました。';
+
+ @override
+ String get telemetry_receivedData => '受信したテレメトリーデータ';
+
+ @override
+ String get telemetry_requestTimeout => 'テレメトリの要求タイムアウトしました。';
+
+ @override
+ String telemetry_errorLoading(String error) {
+ return 'テレメトリの読み込みに失敗しました: $error';
+ }
+
+ @override
+ String get telemetry_noData => 'テレメトリデータは利用できません。';
+
+ @override
+ String telemetry_channelTitle(int channel) {
+ return 'チャンネル $channel';
+ }
+
+ @override
+ String get telemetry_batteryLabel => 'バッテリー';
+
+ @override
+ String get telemetry_voltageLabel => '電圧';
+
+ @override
+ String get telemetry_mcuTemperatureLabel => 'MCU の温度';
+
+ @override
+ String get telemetry_temperatureLabel => '温度';
+
+ @override
+ String get telemetry_currentLabel => '現在';
+
+ @override
+ String telemetry_batteryValue(int percent, String volts) {
+ return '$percent% / ${volts}V';
+ }
+
+ @override
+ String telemetry_voltageValue(String volts) {
+ return '${volts}V';
+ }
+
+ @override
+ String telemetry_currentValue(String amps) {
+ return '${amps}A';
+ }
+
+ @override
+ String telemetry_temperatureValue(String celsius, String fahrenheit) {
+ return '$celsius℃ / $fahrenheit°F';
+ }
+
+ @override
+ String get neighbors_receivedData => '近隣住民のデータを受信';
+
+ @override
+ String get neighbors_requestTimedOut => '近隣住民からの要望:時間制限を設けてください。';
+
+ @override
+ String neighbors_errorLoading(String error) {
+ return '近隣情報の読み込みに失敗: $error';
+ }
+
+ @override
+ String get neighbors_repeatersNeighbors => '繰り返し送信する、近隣';
+
+ @override
+ String get neighbors_noData => '近隣のデータは利用できません。';
+
+ @override
+ String neighbors_unknownContact(String pubkey) {
+ return '不明な $pubkey';
+ }
+
+ @override
+ String neighbors_heardAgo(String time) {
+ return '聞いたのは、$time くらい前です';
+ }
+
+ @override
+ String get channelPath_title => 'パケットパス';
+
+ @override
+ String get channelPath_viewMap => '地図を表示する';
+
+ @override
+ String get channelPath_otherObservedPaths => '観察されたその他の経路';
+
+ @override
+ String get channelPath_repeaterHops => 'ホップの繰り返し';
+
+ @override
+ String get channelPath_noHopDetails => 'このパッケージに関する詳細な情報は提供されていません。';
+
+ @override
+ String get channelPath_messageDetails => 'メッセージの詳細';
+
+ @override
+ String get channelPath_senderLabel => '送信者';
+
+ @override
+ String get channelPath_timeLabel => '時間';
+
+ @override
+ String get channelPath_repeatsLabel => '繰り返し';
+
+ @override
+ String channelPath_pathLabel(int index) {
+ return '$index 番目の経路';
+ }
+
+ @override
+ String get channelPath_observedLabel => '観察';
+
+ @override
+ String channelPath_observedPathTitle(int index, String hops) {
+ return '観察された経路 $index • $hops';
+ }
+
+ @override
+ String get channelPath_noLocationData => '場所に関するデータはありません';
+
+ @override
+ String channelPath_timeWithDate(int day, int month, String time) {
+ return '$day/$month $time';
+ }
+
+ @override
+ String channelPath_timeOnly(String time) {
+ return '$time';
+ }
+
+ @override
+ String get channelPath_unknownPath => '不明';
+
+ @override
+ String get channelPath_floodPath => '洪水';
+
+ @override
+ String get channelPath_directPath => '直接';
+
+ @override
+ String channelPath_observedZeroOf(int total) {
+ return '$total個のホップ';
+ }
+
+ @override
+ String channelPath_observedSomeOf(int observed, int total) {
+ return '$observed of $total hops';
+ }
+
+ @override
+ String get channelPath_mapTitle => '経路図';
+
+ @override
+ String get channelPath_noRepeaterLocations => 'この経路には、中継装置の設置場所がありません。';
+
+ @override
+ String channelPath_primaryPath(int index) {
+ return '$index番目の経路(主要経路)';
+ }
+
+ @override
+ String get channelPath_pathLabelTitle => '道';
+
+ @override
+ String get channelPath_observedPathHeader => '観察された経路';
+
+ @override
+ String channelPath_selectedPathLabel(String label, String prefixes) {
+ return '$label • $prefixes';
+ }
+
+ @override
+ String get channelPath_noHopDetailsAvailable => 'このパッケージに関する詳細な配送情報は利用できません。';
+
+ @override
+ String get channelPath_unknownRepeater => '不明な増幅機';
+
+ @override
+ String get community_title => '地域';
+
+ @override
+ String get community_create => 'コミュニティを構築する';
+
+ @override
+ String get community_createDesc => '新しいコミュニティを作成し、QRコードで共有する。';
+
+ @override
+ String get community_join => '参加する';
+
+ @override
+ String get community_joinTitle => 'コミュニティに参加する';
+
+ @override
+ String community_joinConfirmation(String name) {
+ return '$nameさんのようなコミュニティに参加したいですか?';
+ }
+
+ @override
+ String get community_scanQr => 'コミュニティのQRコードをスキャン';
+
+ @override
+ String get community_scanInstructions => 'カメラを、地域のQRコードを向けて';
+
+ @override
+ String get community_showQr => 'QRコードを表示する';
+
+ @override
+ String get community_publicChannel => '地域住民向け';
+
+ @override
+ String get community_hashtagChannel => 'コミュニティ用ハッシュタグ';
+
+ @override
+ String get community_name => 'コミュニティ名';
+
+ @override
+ String get community_enterName => 'コミュニティ名を入力してください';
+
+ @override
+ String community_created(String name) {
+ return 'コミュニティ「$name」が作成されました';
+ }
+
+ @override
+ String community_joined(String name) {
+ return '$name のコミュニティに参加';
+ }
+
+ @override
+ String get community_qrTitle => 'コミュニティ共有';
+
+ @override
+ String community_qrInstructions(String name) {
+ return 'このQRコードをスキャンして、$nameに参加してください。';
+ }
+
+ @override
+ String get community_hashtagPrivacyHint =>
+ 'コミュニティハッシュタグのチャンネルは、コミュニティのメンバーのみが参加できます。';
+
+ @override
+ String get community_invalidQrCode => '無効なコミュニティQRコード';
+
+ @override
+ String get community_alreadyMember => 'すでに会員である';
+
+ @override
+ String community_alreadyMemberMessage(String name) {
+ return 'あなたはすでに $name の会員です。';
+ }
+
+ @override
+ String get community_addPublicChannel => 'コミュニティ用の公開チャンネルを追加';
+
+ @override
+ String get community_addPublicChannelHint => 'このコミュニティの公開チャンネルを自動的に追加する';
+
+ @override
+ String get community_noCommunities => 'まだコミュニティは形成されていません。';
+
+ @override
+ String get community_scanOrCreate => 'QRコードをスキャンするか、コミュニティを作成して開始してください。';
+
+ @override
+ String get community_manageCommunities => 'コミュニティの管理';
+
+ @override
+ String get community_delete => 'コミュニティからの離脱';
+
+ @override
+ String community_deleteConfirm(String name) {
+ return '$nameを辞める?';
+ }
+
+ @override
+ String community_deleteChannelsWarning(int count) {
+ return 'これにより、$count のチャンネルとそのメッセージも削除されます。';
+ }
+
+ @override
+ String community_deleted(String name) {
+ return 'コミュニティ「$name」を離れる';
+ }
+
+ @override
+ String get community_regenerateSecret => '秘密の復元';
+
+ @override
+ String community_regenerateSecretConfirm(String name) {
+ return '$name の秘密鍵を再生成しますか? 継続的に通信するため、すべてのメンバーは新しいQRコードをスキャンする必要があります。';
+ }
+
+ @override
+ String get community_regenerate => '再生';
+
+ @override
+ String community_secretRegenerated(String name) {
+ return '$name への秘密が再設定されました';
+ }
+
+ @override
+ String get community_updateSecret => '秘密情報の更新';
+
+ @override
+ String community_secretUpdated(String name) {
+ return '$name 向けの秘密設定を更新';
+ }
+
+ @override
+ String community_scanToUpdateSecret(String name) {
+ return '新しいQRコードをスキャンして、$nameの秘密情報を更新してください。';
+ }
+
+ @override
+ String get community_addHashtagChannel => 'コミュニティのハッシュタグを追加';
+
+ @override
+ String get community_addHashtagChannelDesc => 'このコミュニティ用のハッシュタグチャンネルを追加する';
+
+ @override
+ String get community_selectCommunity => 'コミュニティを選択';
+
+ @override
+ String get community_regularHashtag => '定期的なハッシュタグ';
+
+ @override
+ String get community_regularHashtagDesc => '一般のハッシュタグ(誰でも参加可能)';
+
+ @override
+ String get community_communityHashtag => 'コミュニティ用ハッシュタグ';
+
+ @override
+ String get community_communityHashtagDesc => 'コミュニティメンバーのみへの限定';
+
+ @override
+ String community_forCommunity(String name) {
+ return '$name 様';
+ }
+
+ @override
+ String get listFilter_tooltip => 'フィルタリングと並べ替え';
+
+ @override
+ String get listFilter_sortBy => '並び替え';
+
+ @override
+ String get listFilter_latestMessages => '最新のメッセージ';
+
+ @override
+ String get listFilter_heardRecently => '最近、聞いた';
+
+ @override
+ String get listFilter_az => 'AからZ';
+
+ @override
+ String get listFilter_filters => 'フィルター';
+
+ @override
+ String get listFilter_all => 'すべて';
+
+ @override
+ String get listFilter_favorites => 'お気に入り';
+
+ @override
+ String get listFilter_addToFavorites => 'お気に入りに追加';
+
+ @override
+ String get listFilter_removeFromFavorites => 'お気に入りから削除';
+
+ @override
+ String get listFilter_users => '利用者';
+
+ @override
+ String get listFilter_repeaters => '繰り返し送信装置';
+
+ @override
+ String get listFilter_roomServers => 'ルーム用サーバー';
+
+ @override
+ String get listFilter_unreadOnly => '未読のみ';
+
+ @override
+ String get listFilter_newGroup => '新しいグループ';
+
+ @override
+ String get pathTrace_you => 'あなた';
+
+ @override
+ String get pathTrace_failed => 'パスの追跡に失敗しました。';
+
+ @override
+ String get pathTrace_notAvailable => 'パスの追跡機能は利用できません。';
+
+ @override
+ String get pathTrace_refreshTooltip => 'パスの追跡をリフレッシュする。';
+
+ @override
+ String get pathTrace_someHopsNoLocation => 'ホップの1つまたは複数について、場所が特定されていません。';
+
+ @override
+ String get pathTrace_clearTooltip => '明確な道筋。';
+
+ @override
+ String get losSelectStartEnd => 'LOS の開始ノードと終了ノードを選択してください。';
+
+ @override
+ String losRunFailed(String error) {
+ return '視界確認に失敗: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'すべての項目をクリア';
+
+ @override
+ String get losRunToViewElevationProfile => 'LOS(レーザー測距)を使用して、標高プロファイルを表示する';
+
+ @override
+ String get losMenuTitle => 'LOS メニュー';
+
+ @override
+ String get losMenuSubtitle => '特定の場所をタップするか、地図を長押ししてカスタムポイントを作成する。';
+
+ @override
+ String get losShowDisplayNodes => '表示ノードを表示する';
+
+ @override
+ String get losCustomPoints => 'カスタマイズ可能なポイント';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'カスタマイズ $index';
+ }
+
+ @override
+ String get losPointA => 'ポイントA';
+
+ @override
+ String get losPointB => 'ポイントB';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'アンテナ A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'アンテナ B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'LOS(レーティングシステム)を使用する';
+
+ @override
+ String get losNoElevationData => '標高データは含まれていません';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, clear LOS, min clearance $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blocked by $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS:確認中…';
+
+ @override
+ String get losStatusNoData => 'LOS: データの欠如';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total clear, $blocked blocked, $unknown unknown';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'あるサンプルまたは複数のサンプルについて、標高データが利用できません。';
+
+ @override
+ String get losErrorInvalidInput => 'LOS(レーダー)計算に必要な、無効な点/標高データ。';
+
+ @override
+ String get losRenameCustomPoint => 'カスタムポイントの名前を変更する';
+
+ @override
+ String get losPointName => '項目名';
+
+ @override
+ String get losShowPanelTooltip => 'LOSパネルを表示する';
+
+ @override
+ String get losHidePanelTooltip => 'LOSパネルを隠す';
+
+ @override
+ String get losElevationAttribution => '標高データ:Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'ラジオ・ホライゾン';
+
+ @override
+ String get losLegendLosBeam => 'LOS ビーミング';
+
+ @override
+ String get losLegendTerrain => '地形';
+
+ @override
+ String get losFrequencyLabel => '周波数';
+
+ @override
+ String get losFrequencyInfoTooltip => '計算の詳細を見る';
+
+ @override
+ String get losFrequencyDialogTitle => 'ラジオによる水平線計算';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'k=$baselineK ( $baselineFreq MHz) から開始し、現在の $frequencyMHz MHz の帯域に対して k の値を調整します。これにより、曲面状の無線通信範囲の限界が定義されます。';
+ }
+
+ @override
+ String get contacts_pathTrace => '経路追跡';
+
+ @override
+ String get contacts_ping => 'パング';
+
+ @override
+ String get contacts_repeaterPathTrace => 'リピーターまでの経路を追跡する';
+
+ @override
+ String get contacts_repeaterPing => 'PING 繰り返し';
+
+ @override
+ String get contacts_roomPathTrace => '部屋のサーバーへの経路を追跡する';
+
+ @override
+ String get contacts_roomPing => 'ピンルーム用サーバー';
+
+ @override
+ String get contacts_chatTraceRoute => '経路の追跡ルート';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return '$name への経路を追跡する';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'クリップボードは空です。';
+
+ @override
+ String get contacts_invalidAdvertFormat => '無効な連絡先情報';
+
+ @override
+ String get contacts_contactImported => '連絡先が登録されました。';
+
+ @override
+ String get contacts_contactImportFailed => '連絡先のインポートに失敗しました。';
+
+ @override
+ String get contacts_zeroHopAdvert => 'ゼロホップ広告';
+
+ @override
+ String get contacts_floodAdvert => '洪水に関する広告';
+
+ @override
+ String get contacts_copyAdvertToClipboard => '広告をクリップボードにコピー';
+
+ @override
+ String get contacts_addContactFromClipboard => 'クリップボードから連絡先を追加する';
+
+ @override
+ String get contacts_ShareContact => '連絡先をクリップボードにコピー';
+
+ @override
+ String get contacts_ShareContactZeroHop => '広告を通じて連絡先を共有する';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => '広告を通じて連絡先を得た。';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed => '連絡を送信できませんでした。';
+
+ @override
+ String get contacts_contactAdvertCopied => '広告がクリップボードにコピーされました。';
+
+ @override
+ String get contacts_contactAdvertCopyFailed => '広告のコピーがクリップボードにコピーできませんでした。';
+
+ @override
+ String get notification_activityTitle => 'メッシュコアの活動';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'messages',
+ one: 'message',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'チャンネルメッセージ',
+ one: 'チャンネルメッセージ',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '新しいノード',
+ one: '新しいノード',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return '新たに $contactType が発見されました';
+ }
+
+ @override
+ String get notification_receivedNewMessage => '新しいメッセージを受信';
+
+ @override
+ String get settings_gpxExportRepeaters => 'GPX へのエクスポート用リピーター/ルームサーバー';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'GPXファイルに場所情報を付加した、レピーター/ルームサーバーのエクスポート';
+
+ @override
+ String get settings_gpxExportContacts => 'GPX 形式へのエクスポート';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'GPXファイルに位置情報を保存して、他の人と共有する。';
+
+ @override
+ String get settings_gpxExportAll => 'すべての連絡先をGPX形式でエクスポートする';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'すべての連絡先を、場所情報付きのGPXファイルにエクスポートする。';
+
+ @override
+ String get settings_gpxExportSuccess => 'GPXファイルの正常なエクスポートが完了しました。';
+
+ @override
+ String get settings_gpxExportNoContacts => 'エクスポートする連絡先は存在しません。';
+
+ @override
+ String get settings_gpxExportNotAvailable => 'このデバイス/OSではサポートされていません';
+
+ @override
+ String get settings_gpxExportError => 'エクスポート時にエラーが発生しました。';
+
+ @override
+ String get settings_gpxExportRepeatersRoom => '中継装置およびルームサーバーの設置場所';
+
+ @override
+ String get settings_gpxExportChat => '関連施設';
+
+ @override
+ String get settings_gpxExportAllContacts => 'すべての連絡先場所';
+
+ @override
+ String get settings_gpxExportShareText => 'meshcore-openからエクスポートされた地図データ';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open GPX形式の地図データのエクスポート';
+
+ @override
+ String get snrIndicator_nearByRepeaters => '近くの電波中継局';
+
+ @override
+ String get snrIndicator_lastSeen => '最後に確認された場所';
+
+ @override
+ String get contactsSettings_title => '連絡先設定';
+
+ @override
+ String get contactsSettings_autoAddTitle => '自動検出';
+
+ @override
+ String get contactsSettings_otherTitle => 'その他の連絡に関する設定';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle => '自動でユーザーを追加する';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ '利用者が自動的に発見したユーザーを追加できるようにする。';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle => '自動で繰り返し設定';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ '発見した中継局を、自動的に追加できるようにする。';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle => '自動でルームサーバーを追加';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ '利用者が、発見した部屋のサーバーを自動的に追加できるようにする。';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle => '自動でセンサーを追加';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ '利用者が、発見したセンサーを自動的に追加できるようにする。';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => '最も古いものを上書きする';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ '連絡先リストが満杯になった場合、最も古いかつ「お気に入り」ではない連絡先が削除されます。';
+
+ @override
+ String get discoveredContacts_Title => '連絡先が見つかった';
+
+ @override
+ String get discoveredContacts_noMatching => '一致する連絡先が見つかりませんでした';
+
+ @override
+ String get discoveredContacts_searchHint => '発見された連絡先を検索する';
+
+ @override
+ String get discoveredContacts_contactAdded => '連絡先を追加';
+
+ @override
+ String get discoveredContacts_addContact => '連絡先を追加';
+
+ @override
+ String get discoveredContacts_copyContact => '連絡先をクリップボードにコピー';
+
+ @override
+ String get discoveredContacts_deleteContact => '発見された連絡先を削除';
+
+ @override
+ String get discoveredContacts_deleteContactAll => '発見されたすべての連絡先を削除';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ '本当に、見つけたすべての連絡先を削除してもよろしいですか?';
+
+ @override
+ String get chat_sendCooldown => '再度送信する前に、しばらくお待ちください。';
+
+ @override
+ String get appSettings_jumpToOldestUnread => '最も古い未読のメッセージへ移動';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ '未読メッセージがあるチャットを開く際、「最新のメッセージ」ではなく、最初に未読のメッセージまでスクロールしてください。';
+
+ @override
+ String get appSettings_languageHu => 'ハンガリー語';
+
+ @override
+ String get appSettings_languageJa => '日本語';
+
+ @override
+ String get appSettings_languageKo => '韓国語';
+
+ @override
+ String get radioStats_tooltip => 'ラジオおよびメッシュに関する統計';
+
+ @override
+ String get radioStats_screenTitle => 'ラジオの統計';
+
+ @override
+ String get radioStats_notConnected => 'ラジオの統計情報を表示するために、デバイスに接続してください。';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'ラジオの統計機能を使用するには、v8またはそれ以降のファームウェアが必要です。';
+
+ @override
+ String get radioStats_waiting => 'データ待ち…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'ノイズレベル: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return '最後のRSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return '最終SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX 放送時間(合計):$seconds 秒';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX 放送時間(合計):$seconds 秒';
+ }
+
+ @override
+ String get radioStats_chartCaption => '最近のサンプルのノイズレベル(dBm)。';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'ノイズレベル: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'ラジオの統計情報を取得中…';
+
+ @override
+ String get radioStats_settingsTile => 'ラジオの統計';
+
+ @override
+ String get radioStats_settingsSubtitle => 'ノイズレベル、RSSI、SNR、および通信時間';
+
+ @override
+ String get translation_title => '翻訳';
+
+ @override
+ String get translation_enableTitle => '翻訳機能を有効にする';
+
+ @override
+ String get translation_enableSubtitle => '受信メッセージを翻訳し、送信前に翻訳を適用できるようにする。';
+
+ @override
+ String get translation_composerTitle => '送信する前に翻訳する';
+
+ @override
+ String get translation_composerSubtitle => '作曲家翻訳アイコンのデフォルト状態を制御する。';
+
+ @override
+ String get translation_targetLanguage => '翻訳対象言語';
+
+ @override
+ String get translation_useAppLanguage => 'アプリの言語設定';
+
+ @override
+ String get translation_downloadedModelLabel => 'ダウンロードしたモデル';
+
+ @override
+ String get translation_presetModelLabel => 'あらかじめ設定されたHugging Faceモデル';
+
+ @override
+ String get translation_manualUrlLabel => 'マニュアルモデルのURL';
+
+ @override
+ String get translation_downloadModel => 'モデルのダウンロード';
+
+ @override
+ String get translation_downloading => 'ダウンロード中...';
+
+ @override
+ String get translation_working => '業務中…';
+
+ @override
+ String get translation_stop => '停止';
+
+ @override
+ String get translation_mergingChunks => 'ダウンロードしたファイルを最終ファイルに結合中...';
+
+ @override
+ String get translation_downloadedModels => 'ダウンロードされたモデル';
+
+ @override
+ String get translation_deleteModel => 'モデルを削除';
+
+ @override
+ String get translation_modelDownloaded => '翻訳モデルのダウンロードが完了しました。';
+
+ @override
+ String get translation_downloadStopped => 'ダウンロードが中断されました。';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'ダウンロードに失敗しました:$error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'まず、モデルのURLを入力してください。';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'PINを表示';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'PINを非表示';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth ペアリング PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return '$deviceNameのPINを入力してください(なしの場合は空欄のまま)。';
+ }
+
+ @override
+ String get translation_messageTranslation => 'メッセージの翻訳';
+
+ @override
+ String get translation_translateBeforeSending => '送信する前に翻訳する';
+
+ @override
+ String get translation_composerEnabledHint => 'メッセージは送信前に翻訳されます。';
+
+ @override
+ String get translation_composerDisabledHint => '元のタイプされた言語でメッセージを送信してください。';
+
+ @override
+ String translation_translateTo(String language) {
+ return '$language への翻訳';
+ }
+
+ @override
+ String get translation_translationOptions => '翻訳の選択肢';
+
+ @override
+ String get translation_systemLanguage => 'システム言語';
+}
diff --git a/lib/l10n/app_localizations_ko.dart b/lib/l10n/app_localizations_ko.dart
new file mode 100644
index 0000000..ff4bd26
--- /dev/null
+++ b/lib/l10n/app_localizations_ko.dart
@@ -0,0 +1,3530 @@
+// ignore: unused_import
+import 'package:intl/intl.dart' as intl;
+import 'app_localizations.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Korean (`ko`).
+class AppLocalizationsKo extends AppLocalizations {
+ AppLocalizationsKo([String locale = 'ko']) : super(locale);
+
+ @override
+ String get appTitle => 'MeshCore Open';
+
+ @override
+ String get nav_contacts => '연락처';
+
+ @override
+ String get nav_channels => '채널';
+
+ @override
+ String get nav_map => '지도';
+
+ @override
+ String get common_cancel => '취소';
+
+ @override
+ String get common_ok => '알겠습니다';
+
+ @override
+ String get common_connect => '연결';
+
+ @override
+ String get common_unknownDevice => '알 수 없는 장치';
+
+ @override
+ String get common_save => '저장';
+
+ @override
+ String get common_delete => '삭제';
+
+ @override
+ String get common_deleteAll => '모두 삭제';
+
+ @override
+ String get common_close => '닫기';
+
+ @override
+ String get common_edit => '수정';
+
+ @override
+ String get common_add => '추가';
+
+ @override
+ String get common_settings => '설정';
+
+ @override
+ String get common_disconnect => '연결 해제';
+
+ @override
+ String get common_connected => '연결된';
+
+ @override
+ String get common_disconnected => '단절';
+
+ @override
+ String get common_create => '만들다';
+
+ @override
+ String get common_continue => '계속';
+
+ @override
+ String get common_share => '공유';
+
+ @override
+ String get common_copy => '복사';
+
+ @override
+ String get common_retry => '다시 시도';
+
+ @override
+ String get common_hide => '숨기다';
+
+ @override
+ String get common_remove => '제거';
+
+ @override
+ String get common_enable => '활성화';
+
+ @override
+ String get common_disable => '비활성화';
+
+ @override
+ String get common_reboot => '재부팅';
+
+ @override
+ String get common_loading => '로딩 중...';
+
+ @override
+ String get common_notAvailable => '—';
+
+ @override
+ String common_voltageValue(String volts) {
+ return '$volts V';
+ }
+
+ @override
+ String common_percentValue(int percent) {
+ return '$percent%';
+ }
+
+ @override
+ String get scanner_title => 'MeshCore 공개';
+
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => '블루투스';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'TCP를 통해 연결';
+
+ @override
+ String get tcpHostLabel => 'IP 주소';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => '항';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => '목적지 주소 입력 후 연결';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return '$endpoint에 연결 중...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'IP 주소가 필요합니다.';
+
+ @override
+ String get tcpErrorPortInvalid => '포트 번호는 1에서 65535 사이여야 합니다.';
+
+ @override
+ String get tcpErrorUnsupported => '이 플랫폼에서는 TCP 트랜스포트를 지원하지 않습니다.';
+
+ @override
+ String get tcpErrorTimedOut => 'TCP 연결이 시간 초과되었습니다.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'TCP 연결 실패: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'USB를 통해 연결';
+
+ @override
+ String get usbScreenSubtitle => '감지된 시리얼 장치를 선택하고 MeshCore 노드에 직접 연결하십시오.';
+
+ @override
+ String get usbScreenStatus => 'USB 장치를 선택합니다.';
+
+ @override
+ String get usbScreenNote => 'USB 직렬 통신은 지원되는 안드로이드 장치 및 데스크톱 플랫폼에서 활성화됩니다.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'USB 장치가 탐지되지 않았습니다. USB 장치를 연결하고 다시 시도해 보세요.';
+
+ @override
+ String get usbErrorPermissionDenied => 'USB 접근 권한이 거부되었습니다.';
+
+ @override
+ String get usbErrorDeviceMissing => '선택한 USB 장치는 더 이상 사용 불가능합니다.';
+
+ @override
+ String get usbErrorInvalidPort => '유효한 USB 장치를 선택하세요.';
+
+ @override
+ String get usbErrorBusy => '또 다른 USB 연결 요청이 이미 진행 중입니다.';
+
+ @override
+ String get usbErrorNotConnected => 'USB 장치가 연결되지 않았습니다.';
+
+ @override
+ String get usbErrorOpenFailed => '선택한 USB 장치를 열 수 없습니다.';
+
+ @override
+ String get usbErrorConnectFailed => '선택한 USB 장치에 연결에 실패했습니다.';
+
+ @override
+ String get usbErrorUnsupported => '이 플랫폼에서는 USB 직렬 통신을 지원하지 않습니다.';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB 연결이 이미 활성화되어 있습니다.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'USB 장치가 선택되지 않았습니다.';
+
+ @override
+ String get usbErrorPortClosed => 'USB 연결이 활성화되지 않았습니다.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ '연결이 시간 초과되었습니다. 장치가 USB Companion 펌웨어를 가지고 있는지 확인해 주세요.';
+
+ @override
+ String get usbFallbackDeviceName => '웹 시리얼 장치';
+
+ @override
+ String get usbStatus_notConnected => 'USB 장치를 선택합니다.';
+
+ @override
+ String get usbStatus_connecting => 'USB 장치에 연결 중...';
+
+ @override
+ String get usbStatus_searching => 'USB 장치 검색 중...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'USB 연결 실패: $error';
+ }
+
+ @override
+ String get scanner_scanning => '장치 검색 중...';
+
+ @override
+ String get scanner_connecting => '연결 중...';
+
+ @override
+ String get scanner_disconnecting => '연결 해제 중...';
+
+ @override
+ String get scanner_notConnected => '연결되지 않음';
+
+ @override
+ String scanner_connectedTo(String deviceName) {
+ return '$deviceName에 연결됨';
+ }
+
+ @override
+ String get scanner_searchingDevices => 'MeshCore 장치를 검색 중...';
+
+ @override
+ String get scanner_tapToScan => 'MeshCore 장치를 찾기 위해 스캔 버튼을 누르세요.';
+
+ @override
+ String scanner_connectionFailed(String error) {
+ return '연결 실패: $error';
+ }
+
+ @override
+ String get scanner_stop => '멈춰';
+
+ @override
+ String get scanner_scan => '스캔';
+
+ @override
+ String get scanner_bluetoothOff => '블루투스는 꺼져 있습니다.';
+
+ @override
+ String get scanner_bluetoothOffMessage => '블루투스를 켜서 장치를 검색해주세요.';
+
+ @override
+ String get scanner_chromeRequired => '크롬 브라우저 필요';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ '이 웹 애플리케이션은 블루투드 지원을 위해 Google Chrome 또는 Chromium 기반 브라우저가 필요합니다.';
+
+ @override
+ String get scanner_enableBluetooth => '블루투스 활성화';
+
+ @override
+ String get device_quickSwitch => '빠른 전환';
+
+ @override
+ String get device_meshcore => '메쉬코어';
+
+ @override
+ String get settings_title => '설정';
+
+ @override
+ String get settings_deviceInfo => '장치 정보';
+
+ @override
+ String get settings_appSettings => '앱 설정';
+
+ @override
+ String get settings_appSettingsSubtitle => '알림, 메시징, 지도 설정';
+
+ @override
+ String get settings_nodeSettings => '노드 설정';
+
+ @override
+ String get settings_nodeName => '노드 이름';
+
+ @override
+ String get settings_nodeNameNotSet => '설정되지 않음';
+
+ @override
+ String get settings_nodeNameHint => '노드 이름을 입력하세요';
+
+ @override
+ String get settings_nodeNameUpdated => '이름 변경';
+
+ @override
+ String get settings_radioSettings => '라디오 설정';
+
+ @override
+ String get settings_radioSettingsSubtitle => '주파수, 전력, 스펙트럼';
+
+ @override
+ String get settings_radioSettingsUpdated => '라디오 설정이 업데이트되었습니다.';
+
+ @override
+ String get settings_location => '위치';
+
+ @override
+ String get settings_locationSubtitle => 'GPS 좌표';
+
+ @override
+ String get settings_locationUpdated => '위치 및 GPS 설정이 업데이트되었습니다.';
+
+ @override
+ String get settings_locationBothRequired => '위도와 경도를 모두 입력하세요.';
+
+ @override
+ String get settings_locationInvalid => '유효하지 않은 위도 또는 경도.';
+
+ @override
+ String get settings_locationGPSEnable => 'GPS 활성화';
+
+ @override
+ String get settings_locationGPSEnableSubtitle =>
+ 'GPS를 사용하여 위치 정보를 자동으로 업데이트할 수 있도록 합니다.';
+
+ @override
+ String get settings_locationIntervalSec => 'GPS 간격 (초)';
+
+ @override
+ String get settings_locationIntervalInvalid =>
+ '간격은 최소 60초 이상, 86400초 미만이어야 합니다.';
+
+ @override
+ String get settings_latitude => '위도';
+
+ @override
+ String get settings_longitude => '경도';
+
+ @override
+ String get settings_contactSettings => '연락처 설정';
+
+ @override
+ String get settings_contactSettingsSubtitle => '연락처 추가 방식 설정';
+
+ @override
+ String get settings_privacyMode => '개인 정보 보호 모드';
+
+ @override
+ String get settings_privacyModeSubtitle => '광고에 이름/위치 정보 숨기기';
+
+ @override
+ String get settings_privacyModeToggle =>
+ '광고에 자신의 이름과 위치를 숨기기 위해 개인 정보 보호 모드를 켜거나 끄십시오.';
+
+ @override
+ String get settings_privacyModeEnabled => '개인 정보 보호 모드 활성화';
+
+ @override
+ String get settings_privacyModeDisabled => '개인 정보 보호 모드 비활성화';
+
+ @override
+ String get settings_privacy => '개인 정보 설정';
+
+ @override
+ String get settings_privacySubtitle => '어떤 정보를 공유할지 통제하세요.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ '어떤 정보를 기기가 다른 사람들과 공유할지 선택하세요.';
+
+ @override
+ String get settings_denyAll => '모든 것을 부정';
+
+ @override
+ String get settings_allowByContact => '연락처 표시 기능 활성화';
+
+ @override
+ String get settings_allowAll => '모든 것을 허용';
+
+ @override
+ String get settings_telemetryBaseMode => '원격 모니터링 기본 설정';
+
+ @override
+ String get settings_telemetryLocationMode => '텔레메트리 위치 모드';
+
+ @override
+ String get settings_telemetryEnvironmentMode => '텔레메트리 환경 모드';
+
+ @override
+ String get settings_advertLocation => '광고 위치';
+
+ @override
+ String get settings_advertLocationSubtitle => '광고에 위치 정보를 포함하세요.';
+
+ @override
+ String settings_multiAck(String value) {
+ return '다중 ACK: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => '텔레메트리 모드 업데이트 완료';
+
+ @override
+ String get settings_actions => '행동';
+
+ @override
+ String get settings_sendAdvertisement => '광고 전송';
+
+ @override
+ String get settings_sendAdvertisementSubtitle => '방송 활동';
+
+ @override
+ String get settings_advertisementSent => '광고 전송';
+
+ @override
+ String get settings_syncTime => '동기화 시간';
+
+ @override
+ String get settings_syncTimeSubtitle => '장치 시계를 휴대폰 시간으로 설정';
+
+ @override
+ String get settings_timeSynchronized => '시간 동기화';
+
+ @override
+ String get settings_refreshContacts => '연락처 갱신';
+
+ @override
+ String get settings_refreshContactsSubtitle => '장치에서 연락처 목록을 다시 불러오기';
+
+ @override
+ String get settings_rebootDevice => '장치 재부팅';
+
+ @override
+ String get settings_rebootDeviceSubtitle => 'MeshCore 장치를 재부팅하세요.';
+
+ @override
+ String get settings_rebootDeviceConfirm =>
+ '정말로 장치를 재부팅하시겠습니까? 이 경우 연결이 끊어집니다.';
+
+ @override
+ String get settings_debug => '디버깅';
+
+ @override
+ String get settings_bleDebugLog => 'BLE 디버그 로그';
+
+ @override
+ String get settings_bleDebugLogSubtitle => 'BLE 명령어, 응답 및 원시 데이터';
+
+ @override
+ String get settings_appDebugLog => '앱 디버깅 로그';
+
+ @override
+ String get settings_appDebugLogSubtitle => '애플리케이션 디버깅 메시지';
+
+ @override
+ String get settings_about => '소개';
+
+ @override
+ String settings_aboutVersion(String version) {
+ return 'MeshCore Open $version 버전';
+ }
+
+ @override
+ String get settings_aboutLegalese => '2026년 MeshCore 오픈 소스 프로젝트';
+
+ @override
+ String get settings_aboutDescription =>
+ 'MeshCore LoRa 메시 네트워크 장치를 위한 오픈 소스 Flutter 클라이언트.';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS 고도 데이터: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => '이름';
+
+ @override
+ String get settings_infoId => 'ID';
+
+ @override
+ String get settings_infoStatus => '상태';
+
+ @override
+ String get settings_infoBattery => '배터리';
+
+ @override
+ String get settings_infoPublicKey => '공개 키';
+
+ @override
+ String get settings_infoContactsCount => '연락처 수';
+
+ @override
+ String get settings_infoChannelCount => '채널 수';
+
+ @override
+ String get settings_presets => '기본 설정';
+
+ @override
+ String get settings_frequency => '주파수 (MHz)';
+
+ @override
+ String get settings_frequencyHelper => '300.0 - 2500.0';
+
+ @override
+ String get settings_frequencyInvalid => '유효하지 않은 주파수 (300-2500 MHz)';
+
+ @override
+ String get settings_bandwidth => '대역폭';
+
+ @override
+ String get settings_spreadingFactor => '분산 계수';
+
+ @override
+ String get settings_codingRate => '코딩 속도';
+
+ @override
+ String get settings_txPower => 'TX 전력 (dBm)';
+
+ @override
+ String get settings_txPowerHelper => '0 - 22';
+
+ @override
+ String get settings_txPowerInvalid => '유효하지 않은 TX 전력 (0-22 dBm)';
+
+ @override
+ String get settings_clientRepeat => '오프그리드 반복';
+
+ @override
+ String get settings_clientRepeatSubtitle =>
+ '이 장치가 다른 사람들을 위해 메시 패킷을 반복하도록 허용합니다.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ '오프그리드(무전력) 시스템 재연결에는 433MHz, 869MHz, 또는 918MHz 주파수가 필요합니다.';
+
+ @override
+ String settings_error(String message) {
+ return '오류: $message';
+ }
+
+ @override
+ String get appSettings_title => '앱 설정';
+
+ @override
+ String get appSettings_appearance => '외관';
+
+ @override
+ String get appSettings_theme => '주제';
+
+ @override
+ String get appSettings_themeSystem => '기본 설정';
+
+ @override
+ String get appSettings_themeLight => '빛';
+
+ @override
+ String get appSettings_themeDark => '어둡다';
+
+ @override
+ String get appSettings_language => '언어';
+
+ @override
+ String get appSettings_languageSystem => '기본 설정';
+
+ @override
+ String get appSettings_languageEn => '영어';
+
+ @override
+ String get appSettings_languageFr => '프랑스어';
+
+ @override
+ String get appSettings_languageEs => '스페인어';
+
+ @override
+ String get appSettings_languageDe => '독일어';
+
+ @override
+ String get appSettings_languagePl => '폴란드';
+
+ @override
+ String get appSettings_languageSl => '슬로베니아어';
+
+ @override
+ String get appSettings_languagePt => '포르투갈어';
+
+ @override
+ String get appSettings_languageIt => '이탈리아어';
+
+ @override
+ String get appSettings_languageZh => '중국어';
+
+ @override
+ String get appSettings_languageSv => '스웨덴어';
+
+ @override
+ String get appSettings_languageNl => '네덜란드어';
+
+ @override
+ String get appSettings_languageSk => '슬로베니아어';
+
+ @override
+ String get appSettings_languageBg => '불가리';
+
+ @override
+ String get appSettings_languageRu => '러시아어';
+
+ @override
+ String get appSettings_languageUk => '우크라이나';
+
+ @override
+ String get appSettings_enableMessageTracing => '메시지 추적 기능 활성화';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ '메시지에 대한 상세한 경로 및 시간 정보를 표시';
+
+ @override
+ String get appSettings_notifications => '알림';
+
+ @override
+ String get appSettings_enableNotifications => '알림 활성화';
+
+ @override
+ String get appSettings_enableNotificationsSubtitle => '메시지와 광고에 대한 알림을 받으세요.';
+
+ @override
+ String get appSettings_notificationPermissionDenied => '알림 권한 거부';
+
+ @override
+ String get appSettings_notificationsEnabled => '알림 기능 활성화';
+
+ @override
+ String get appSettings_notificationsDisabled => '알림 기능 끄기';
+
+ @override
+ String get appSettings_messageNotifications => '메시지 알림';
+
+ @override
+ String get appSettings_messageNotificationsSubtitle => '새로운 메시지를 받을 때 알림 표시';
+
+ @override
+ String get appSettings_channelMessageNotifications => '채널 메시지 알림';
+
+ @override
+ String get appSettings_channelMessageNotificationsSubtitle =>
+ '채널 메시지를 수신할 때 알림 표시';
+
+ @override
+ String get appSettings_advertisementNotifications => '광고 알림';
+
+ @override
+ String get appSettings_advertisementNotificationsSubtitle =>
+ '새 노드가 발견되었을 때 알림 표시';
+
+ @override
+ String get appSettings_messaging => '메시징';
+
+ @override
+ String get appSettings_clearPathOnMaxRetry => 'Max 재시도 시 경로 명확하게 설정';
+
+ @override
+ String get appSettings_clearPathOnMaxRetrySubtitle =>
+ '5번의 전송 시도가 실패하면 연락 경로를 재설정';
+
+ @override
+ String get appSettings_pathsWillBeCleared => '5번의 시도 실패 후, 해당 경로가 확보될 것입니다.';
+
+ @override
+ String get appSettings_pathsWillNotBeCleared => '경로는 자동으로 정리되지 않습니다.';
+
+ @override
+ String get appSettings_autoRouteRotation => '자동 경로 순환';
+
+ @override
+ String get appSettings_autoRouteRotationSubtitle => '최적 경로와 방수 모드 사이를 전환';
+
+ @override
+ String get appSettings_autoRouteRotationEnabled => '자동 경로 순환 기능 활성화';
+
+ @override
+ String get appSettings_autoRouteRotationDisabled => '자동 경로 순환 기능 비활성화';
+
+ @override
+ String get appSettings_maxRouteWeight => '최대 경로 무게';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ '한 경로가 성공적인 배송을 통해 누적할 수 있는 최대 무게';
+
+ @override
+ String get appSettings_initialRouteWeight => '초기 경로 가중치';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle => '새롭게 발견된 경로의 초기 무게';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement => '성공 횟수 증가';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ '성공적으로 배송된 경로에 추가된 무게';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement => '오류 가중치 감소';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ '배송 실패 후 경로에서 제거된 무게';
+
+ @override
+ String get appSettings_maxMessageRetries => '최대 메시지 재시도 횟수';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle => '메시지를 실패로 처리하기 전 시도 횟수';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
+ @override
+ String get appSettings_battery => '배터리';
+
+ @override
+ String get appSettings_batteryChemistry => '배터리 화학';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return '$deviceName 당분간';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst => '장치를 선택하기 위해 연결';
+
+ @override
+ String get appSettings_batteryNmc => '18650 NMC (3.0-4.2V)';
+
+ @override
+ String get appSettings_batteryLifepo4 => 'LiFePO4 (2.6-3.65V)';
+
+ @override
+ String get appSettings_batteryLipo => '리튬 폴리머 (3.0-4.2V)';
+
+ @override
+ String get appSettings_mapDisplay => '지도 표시';
+
+ @override
+ String get appSettings_showRepeaters => '반복 기능 표시';
+
+ @override
+ String get appSettings_showRepeatersSubtitle => '지도에 반복자 노드를 표시';
+
+ @override
+ String get appSettings_showChatNodes => '채팅 노드 표시';
+
+ @override
+ String get appSettings_showChatNodesSubtitle => '지도에 채팅 노드를 표시';
+
+ @override
+ String get appSettings_showOtherNodes => '다른 노드 표시';
+
+ @override
+ String get appSettings_showOtherNodesSubtitle => '지도에서 다른 노드 유형을 표시';
+
+ @override
+ String get appSettings_timeFilter => '시간 필터';
+
+ @override
+ String get appSettings_timeFilterShowAll => '모든 노드 표시';
+
+ @override
+ String appSettings_timeFilterShowLast(int hours) {
+ return '지난 $hours 시간 동안의 노드 표시';
+ }
+
+ @override
+ String get appSettings_mapTimeFilter => '지도 필터';
+
+ @override
+ String get appSettings_showNodesDiscoveredWithin => '다음 내역에서 발견된 노드 표시:';
+
+ @override
+ String get appSettings_allTime => '모든 시간';
+
+ @override
+ String get appSettings_lastHour => '지난 시간';
+
+ @override
+ String get appSettings_last6Hours => '지난 6시간';
+
+ @override
+ String get appSettings_last24Hours => '지난 24시간';
+
+ @override
+ String get appSettings_lastWeek => '지난 주';
+
+ @override
+ String get appSettings_offlineMapCache => '오프라인 지도 캐시';
+
+ @override
+ String get appSettings_unitsTitle => '단위';
+
+ @override
+ String get appSettings_unitsMetric => '단위 (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => '제국 (피트/마일)';
+
+ @override
+ String get appSettings_noAreaSelected => '선택된 영역 없음';
+
+ @override
+ String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
+ return '선택된 영역 (줌 레벨: $minZoom - $maxZoom)';
+ }
+
+ @override
+ String get appSettings_debugCard => '디버깅';
+
+ @override
+ String get appSettings_appDebugLogging => '앱 디버깅 로깅';
+
+ @override
+ String get appSettings_appDebugLoggingSubtitle => '로그 앱 디버깅 메시지 (문제 해결을 위한)';
+
+ @override
+ String get appSettings_appDebugLoggingEnabled => '앱 디버깅 로깅 활성화';
+
+ @override
+ String get appSettings_appDebugLoggingDisabled => '앱 디버깅 로깅 비활성화';
+
+ @override
+ String get contacts_title => '연락처';
+
+ @override
+ String get contacts_noContacts => '아직 연락처는 없습니다.';
+
+ @override
+ String get contacts_contactsWillAppear => '장치가 광고를 할 때, 연락처 정보가 표시됩니다.';
+
+ @override
+ String get contacts_unread => '읽지 않음';
+
+ @override
+ String get contacts_searchContactsNoNumber => '연락처 검색...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return '$number $str 연락처 검색...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return '$number $str 검색 결과 보기...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return '$number $str 사용자 검색...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return '$number $str 검색 결과 반복기 검색';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return '$number $str 방 서버 검색';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => '읽지 않은 연락처가 없습니다.';
+
+ @override
+ String get contacts_noContactsFound => '연락처 또는 그룹이 검색되지 않았습니다.';
+
+ @override
+ String get contacts_deleteContact => '연락처 삭제';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return '$contactName를 연락처 목록에서 제거하시겠습니까?';
+ }
+
+ @override
+ String get contacts_manageRepeater => '리피터 관리';
+
+ @override
+ String get contacts_manageRoom => '방 서버 관리';
+
+ @override
+ String get contacts_roomLogin => '방 서버 로그인';
+
+ @override
+ String get contacts_openChat => '자유로운 대화';
+
+ @override
+ String get contacts_editGroup => '편집 그룹';
+
+ @override
+ String get contacts_deleteGroup => '그룹 삭제';
+
+ @override
+ String contacts_deleteGroupConfirm(String groupName) {
+ return '$groupName 삭제?';
+ }
+
+ @override
+ String get contacts_newGroup => '새로운 그룹';
+
+ @override
+ String get contacts_groupName => '그룹 이름';
+
+ @override
+ String get contacts_groupNameRequired => '그룹 이름이 필요합니다';
+
+ @override
+ String get contacts_groupNameReserved => '이 그룹 이름은 이미 사용 중입니다.';
+
+ @override
+ String contacts_groupAlreadyExists(String name) {
+ return '그룹 \"$name\"은 이미 존재합니다.';
+ }
+
+ @override
+ String get contacts_filterContacts => '연락처 필터링...';
+
+ @override
+ String get contacts_noContactsMatchFilter => '입력하신 검색 조건과 일치하는 연락처가 없습니다.';
+
+ @override
+ String get contacts_noMembers => '회원 없음';
+
+ @override
+ String get contacts_lastSeenNow => '최근';
+
+ @override
+ String contacts_lastSeenMinsAgo(int minutes) {
+ return '~ $minutes min.';
+ }
+
+ @override
+ String get contacts_lastSeenHourAgo => '약 1시간';
+
+ @override
+ String contacts_lastSeenHoursAgo(int hours) {
+ return '~ $hours hours';
+ }
+
+ @override
+ String get contacts_lastSeenDayAgo => '~ 1일';
+
+ @override
+ String contacts_lastSeenDaysAgo(int days) {
+ return '~ $days일';
+ }
+
+ @override
+ String get contact_info => '연락처';
+
+ @override
+ String get contact_settings => '연락처 설정';
+
+ @override
+ String get contact_telemetry => '텔레메트리';
+
+ @override
+ String get contact_lastSeen => '마지막으로 목격';
+
+ @override
+ String get contact_clearChat => '명확한 대화';
+
+ @override
+ String get contact_teleBase => '텔레메트리 기반';
+
+ @override
+ String get contact_teleBaseSubtitle => '배터리 잔량 및 기본적인 통신 데이터를 공유할 수 있도록 허용';
+
+ @override
+ String get contact_teleLoc => '텔레메트리 위치';
+
+ @override
+ String get contact_teleLocSubtitle => '위치 정보 공유 허용';
+
+ @override
+ String get contact_teleEnv => '텔레메트리 환경';
+
+ @override
+ String get contact_teleEnvSubtitle => '환경 센서 데이터를 공유하도록 허용';
+
+ @override
+ String get channels_title => '채널';
+
+ @override
+ String get channels_noChannelsConfigured => '구성된 채널이 없습니다.';
+
+ @override
+ String get channels_addPublicChannel => '공개 채널 추가';
+
+ @override
+ String get channels_searchChannels => '검색 채널...';
+
+ @override
+ String get channels_noChannelsFound => '채널을 찾을 수 없습니다.';
+
+ @override
+ String channels_channelIndex(int index) {
+ return '채널 $index';
+ }
+
+ @override
+ String get channels_hashtagChannel => '해시태그 채널';
+
+ @override
+ String get channels_public => '대중의';
+
+ @override
+ String get channels_private => '사립';
+
+ @override
+ String get channels_publicChannel => '공개 채널';
+
+ @override
+ String get channels_privateChannel => '개인 채널';
+
+ @override
+ String get channels_editChannel => '채널 편집';
+
+ @override
+ String get channels_muteChannel => '음소거 채널';
+
+ @override
+ String get channels_unmuteChannel => '채널 음소거 해제';
+
+ @override
+ String get channels_deleteChannel => '채널 삭제';
+
+ @override
+ String channels_deleteChannelConfirm(String name) {
+ return '$name 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return '채널 \"$name\" 삭제에 실패했습니다.';
+ }
+
+ @override
+ String channels_channelDeleted(String name) {
+ return '채널 \"$name\" 삭제';
+ }
+
+ @override
+ String get channels_addChannel => '채널 추가';
+
+ @override
+ String get channels_channelIndexLabel => '채널 인덱스';
+
+ @override
+ String get channels_channelName => '채널 이름';
+
+ @override
+ String get channels_usePublicChannel => '공개 채널 사용';
+
+ @override
+ String get channels_standardPublicPsk => '표준 공공 PSK';
+
+ @override
+ String get channels_pskHex => 'PSK (헥스)';
+
+ @override
+ String get channels_generateRandomPsk => '임의의 PSK 생성';
+
+ @override
+ String get channels_enterChannelName => '채널 이름을 입력해 주세요.';
+
+ @override
+ String get channels_pskMustBe32Hex => 'PSK(개인식별키)는 32자리 16진수 문자여야 합니다.';
+
+ @override
+ String channels_channelAdded(String name) {
+ return '채널 \"$name\" 추가';
+ }
+
+ @override
+ String channels_editChannelTitle(int index) {
+ return '채널 $index 편집';
+ }
+
+ @override
+ String get channels_smazCompression => 'SMAZ 압축';
+
+ @override
+ String channels_channelUpdated(String name) {
+ return '채널 \"$name\"이 업데이트되었습니다.';
+ }
+
+ @override
+ String get channels_publicChannelAdded => '공개 채널 추가';
+
+ @override
+ String get channels_sortBy => '정렬 기준 선택';
+
+ @override
+ String get channels_sortManual => '사용 설명서';
+
+ @override
+ String get channels_sortAZ => 'A부터 Z까지';
+
+ @override
+ String get channels_sortLatestMessages => '최신 메시지';
+
+ @override
+ String get channels_sortUnread => '읽지 않음';
+
+ @override
+ String get channels_createPrivateChannel => '개인 채널 만들기';
+
+ @override
+ String get channels_createPrivateChannelDesc => '비밀 키로 암호화되어 있습니다.';
+
+ @override
+ String get channels_joinPrivateChannel => '개인 채널에 참여하기';
+
+ @override
+ String get channels_joinPrivateChannelDesc => '비밀 키를 수동으로 입력합니다.';
+
+ @override
+ String get channels_joinPublicChannel => '공개 채널에 참여하세요';
+
+ @override
+ String get channels_joinPublicChannelDesc => '누구나 이 채널에 참여할 수 있습니다.';
+
+ @override
+ String get channels_joinHashtagChannel => '해시태그 채널에 참여하세요';
+
+ @override
+ String get channels_joinHashtagChannelDesc => '누구나 해시태그 채널에 참여할 수 있습니다.';
+
+ @override
+ String get channels_scanQrCode => 'QR 코드를 스캔';
+
+ @override
+ String get channels_scanQrCodeComingSoon => '곧 출시';
+
+ @override
+ String get channels_enterHashtag => '해시태그 입력';
+
+ @override
+ String get channels_hashtagHint => '예: #팀';
+
+ @override
+ String get chat_noMessages => '아직 메시지가 없습니다.';
+
+ @override
+ String get chat_sendMessage => '메시지를 보내기';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return '$contactName에게 메시지를 보내';
+ }
+
+ @override
+ String get chat_sendMessageToStart => '시작하려면 메시지를 보내세요.';
+
+ @override
+ String get chat_originalMessageNotFound => '원래 메시지를 찾을 수 없음';
+
+ @override
+ String chat_replyingTo(String name) {
+ return '$name에게 답변';
+ }
+
+ @override
+ String chat_replyTo(String name) {
+ return '$name님께 회신';
+ }
+
+ @override
+ String get chat_location => '위치';
+
+ @override
+ String get chat_typeMessage => '메시지를 입력하세요...';
+
+ @override
+ String chat_messageTooLong(int maxBytes) {
+ return '메시지가 너무 길어서 (최대 $maxBytes 바이트).';
+ }
+
+ @override
+ String get chat_messageCopied => '메시지가 복사되었습니다';
+
+ @override
+ String get chat_messageDeleted => '메시지가 삭제되었습니다.';
+
+ @override
+ String get chat_retryingMessage => '재시도 메시지';
+
+ @override
+ String chat_retryCount(int current, int max) {
+ return '$current/$max 시도';
+ }
+
+ @override
+ String get chat_sendGif => 'GIF 보내기';
+
+ @override
+ String get chat_reply => '답변';
+
+ @override
+ String get chat_addReaction => '댓글 추가';
+
+ @override
+ String get chat_me => '나';
+
+ @override
+ String get emojiCategorySmileys => '이모티콘';
+
+ @override
+ String get emojiCategoryGestures => '제스처';
+
+ @override
+ String get emojiCategoryHearts => '심장';
+
+ @override
+ String get emojiCategoryObjects => '대상';
+
+ @override
+ String get gifPicker_title => 'GIF 선택';
+
+ @override
+ String get gifPicker_searchHint => 'GIF 검색...';
+
+ @override
+ String get gifPicker_poweredBy => 'GIPHY에서 제공';
+
+ @override
+ String get gifPicker_noGifsFound => 'GIF 파일이 없습니다.';
+
+ @override
+ String get gifPicker_failedLoad => 'GIF 파일 로딩 실패';
+
+ @override
+ String get gifPicker_failedSearch => 'GIF 검색에 실패했습니다.';
+
+ @override
+ String get gifPicker_noInternet => '인터넷 연결 없음';
+
+ @override
+ String get debugLog_appTitle => '앱 디버깅 로그';
+
+ @override
+ String get debugLog_bleTitle => 'BLE 디버그 로그';
+
+ @override
+ String get debugLog_copyLog => '로그 기록';
+
+ @override
+ String get debugLog_clearLog => '명확한 로그';
+
+ @override
+ String get debugLog_copied => '디버깅 로그 복사';
+
+ @override
+ String get debugLog_bleCopied => 'BLE 로그 복사';
+
+ @override
+ String get debugLog_noEntries => '현재 디버깅 로그는 생성되지 않았습니다.';
+
+ @override
+ String get debugLog_enableInSettings => '설정에서 앱 디버깅 로깅을 활성화합니다.';
+
+ @override
+ String get debugLog_frames => '프레임';
+
+ @override
+ String get debugLog_rawLogRx => '원시 로그-RX';
+
+ @override
+ String get debugLog_noBleActivity => '현재 BLE 관련 활동은 없습니다.';
+
+ @override
+ String debugFrame_length(int count) {
+ return '프레임 길이: $count 바이트';
+ }
+
+ @override
+ String debugFrame_command(String value) {
+ return '명령: 0x$value';
+ }
+
+ @override
+ String get debugFrame_textMessageHeader => '텍스트 메시지 프레임:';
+
+ @override
+ String debugFrame_destinationPubKey(String pubKey) {
+ return '- 목적지 공개 키: $pubKey';
+ }
+
+ @override
+ String debugFrame_timestamp(int timestamp) {
+ return '- 시간: $timestamp';
+ }
+
+ @override
+ String debugFrame_flags(String value) {
+ return '- 플래그: 0x$value';
+ }
+
+ @override
+ String debugFrame_textType(int type, String label) {
+ return '- 텍스트 유형: $type ($label)';
+ }
+
+ @override
+ String get debugFrame_textTypeCli => '명령줄 인터페이스 (CLI)';
+
+ @override
+ String get debugFrame_textTypePlain => '단순한';
+
+ @override
+ String debugFrame_text(String text) {
+ return '- 텍스트: \"$text\"';
+ }
+
+ @override
+ String get debugFrame_hexDump => '헥스 덤프:';
+
+ @override
+ String get chat_pathManagement => '경로 관리';
+
+ @override
+ String get chat_ShowAllPaths => '모든 경로 표시';
+
+ @override
+ String get chat_routingMode => '라우팅 방식';
+
+ @override
+ String get chat_autoUseSavedPath => '자동 (저장된 경로 사용)';
+
+ @override
+ String get chat_forceFloodMode => '강수 모드 활성화';
+
+ @override
+ String get chat_recentAckPaths => '최근 사용한 ACK 경로 (사용하려면 탭):';
+
+ @override
+ String get chat_pathHistoryFull =>
+ '이력 기록은 이미 가득 차 있습니다. 항목을 삭제하여 새로운 항목을 추가할 수 있습니다.';
+
+ @override
+ String get chat_hopSingular => '점프';
+
+ @override
+ String get chat_hopPlural => '홉';
+
+ @override
+ String chat_hopsCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '홉',
+ one: '홉',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String get chat_successes => '성공 사례';
+
+ @override
+ String get chat_removePath => '경로 제거';
+
+ @override
+ String get chat_noPathHistoryYet => '아직 경로 기록이 없습니다.\n경로를 찾기 위해 메시지를 보내세요.';
+
+ @override
+ String get chat_pathActions => '경로 작업:';
+
+ @override
+ String get chat_setCustomPath => '사용자 지정 경로 설정';
+
+ @override
+ String get chat_setCustomPathSubtitle => '수동으로 경로를 지정';
+
+ @override
+ String get chat_clearPath => '명확한 길';
+
+ @override
+ String get chat_clearPathSubtitle => '다음 전송 시, 강제 재전송 설정';
+
+ @override
+ String get chat_pathCleared => '경로가 확보되었습니다. 다음 메시지는 경로를 다시 찾을 것입니다.';
+
+ @override
+ String get chat_floodModeSubtitle => '앱 바에서 라우팅 스위치를 사용';
+
+ @override
+ String get chat_floodModeEnabled =>
+ '홍수 모드 활성화됨. 앱 바의 경로 아이콘을 사용하여 다시 전환할 수 있습니다.';
+
+ @override
+ String get chat_fullPath => '전체 경로';
+
+ @override
+ String get chat_pathDetailsNotAvailable =>
+ '경로 정보는 아직 제공되지 않습니다. 메시지를 보내어 다시 시도해 보세요.';
+
+ @override
+ String chat_pathSetHops(int hopCount, String status) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hopCount,
+ locale: localeName,
+ other: 'hops',
+ one: 'hop',
+ );
+ return 'Path set: $hopCount $_temp0 - $status';
+ }
+
+ @override
+ String get chat_pathSavedLocally => '로컬에 저장. 동기화 연결';
+
+ @override
+ String get chat_pathDeviceConfirmed => '장치 확인 완료.';
+
+ @override
+ String get chat_pathDeviceNotConfirmed => '기기가 아직 확인되지 않았습니다.';
+
+ @override
+ String get chat_type => '종류';
+
+ @override
+ String get chat_path => '경로';
+
+ @override
+ String get chat_publicKey => '공개 키';
+
+ @override
+ String get chat_compressOutgoingMessages => '전송되는 메시지 압축';
+
+ @override
+ String get chat_floodForced => '홍수 (강제)';
+
+ @override
+ String get chat_directForced => '직접적인 (강제적인)';
+
+ @override
+ String chat_hopsForced(int count) {
+ return '$count번 띄우기 (강제)';
+ }
+
+ @override
+ String get chat_floodAuto => '홍수 (자동)';
+
+ @override
+ String get chat_direct => '직접';
+
+ @override
+ String get chat_poiShared => '공유된 POI';
+
+ @override
+ String chat_unread(int count) {
+ return '읽지 않음: $count';
+ }
+
+ @override
+ String get chat_openLink => '링크를 열기?';
+
+ @override
+ String get chat_openLinkConfirmation => '이 링크를 브라우저에서 열고 싶으신가요?';
+
+ @override
+ String get chat_open => '열기';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return '링크를 열 수 없습니다: $url';
+ }
+
+ @override
+ String get chat_invalidLink => '유효하지 않은 링크 형식';
+
+ @override
+ String get map_title => '노드 매핑';
+
+ @override
+ String get map_lineOfSight => '시야';
+
+ @override
+ String get map_losScreenTitle => '시야';
+
+ @override
+ String get map_noNodesWithLocation => '위치 정보가 있는 노드가 없습니다.';
+
+ @override
+ String get map_nodesNeedGps => '노드는 지도에 표시되려면 GPS 좌표를 공유해야 합니다.';
+
+ @override
+ String map_nodesCount(int count) {
+ return '노드: $count';
+ }
+
+ @override
+ String map_pinsCount(int count) {
+ return '핀: $count';
+ }
+
+ @override
+ String get map_chat => '채팅';
+
+ @override
+ String get map_repeater => '반복기';
+
+ @override
+ String get map_room => '방';
+
+ @override
+ String get map_sensor => '센서';
+
+ @override
+ String get map_pinDm => '핀 (DM)';
+
+ @override
+ String get map_pinPrivate => '개인 계정';
+
+ @override
+ String get map_pinPublic => '공개 (일반 공개)';
+
+ @override
+ String get map_lastSeen => '마지막으로 목격';
+
+ @override
+ String get map_disconnectConfirm => '이 장치와의 연결을 해제하시겠습니까?';
+
+ @override
+ String get map_from => '~부터';
+
+ @override
+ String get map_source => '출처';
+
+ @override
+ String get map_flags => '깃발';
+
+ @override
+ String get map_shareMarkerHere => '여기에서 마커 공유';
+
+ @override
+ String get map_setAsMyLocation => '내 위치로 설정';
+
+ @override
+ String get map_pinLabel => '핀 라벨';
+
+ @override
+ String get map_label => '레이블';
+
+ @override
+ String get map_pointOfInterest => '관심 지점';
+
+ @override
+ String get map_sendToContact => '연락처로 보내기';
+
+ @override
+ String get map_sendToChannel => '채널로 전송';
+
+ @override
+ String get map_noChannelsAvailable => '사용 가능한 채널이 없습니다.';
+
+ @override
+ String get map_publicLocationShare => '공개 장소 공유';
+
+ @override
+ String map_publicLocationShareConfirm(String channelLabel) {
+ return '현재 $channelLabel 채널에서 위치 정보를 공유하려고 합니다. 이 채널은 공개되어 있으며, PSK를 가진 모든 사용자가 이 위치 정보를 볼 수 있습니다.';
+ }
+
+ @override
+ String get map_connectToShareMarkers => '장치를 연결하여 마커를 공유';
+
+ @override
+ String get map_filterNodes => '필터 노드';
+
+ @override
+ String get map_nodeTypes => '노드 유형';
+
+ @override
+ String get map_chatNodes => '채팅 노드';
+
+ @override
+ String get map_repeaters => '다시 보내는 장치';
+
+ @override
+ String get map_otherNodes => '다른 노드';
+
+ @override
+ String get map_showOverlaps => '반복 키 중복';
+
+ @override
+ String get map_keyPrefix => '핵심 접두사';
+
+ @override
+ String get map_filterByKeyPrefix => '주요 접두사 기준으로 필터링';
+
+ @override
+ String get map_publicKeyPrefix => '공개 키 접두사';
+
+ @override
+ String get map_markers => '마커';
+
+ @override
+ String get map_showSharedMarkers => '공통 마커 표시';
+
+ @override
+ String get map_showGuessedLocations => '추정된 노드 위치 표시';
+
+ @override
+ String get map_showDiscoveryContacts => '디스커버리 담당자 연락처 보기';
+
+ @override
+ String get map_guessedLocation => '추측된 위치';
+
+ @override
+ String get map_lastSeenTime => '마지막으로 확인된 시간';
+
+ @override
+ String get map_sharedPin => '공유 비밀번호';
+
+ @override
+ String get map_joinRoom => '방에 참여';
+
+ @override
+ String get map_manageRepeater => '리피터 관리';
+
+ @override
+ String get map_tapToAdd => '노드에 클릭하여 경로에 추가합니다.';
+
+ @override
+ String get map_runTrace => '경로 추적';
+
+ @override
+ String get map_runTraceWithReturnPath => '원래 경로로 돌아가세요.';
+
+ @override
+ String get map_removeLast => '마지막 항목 삭제';
+
+ @override
+ String get map_pathTraceCancelled => '경로 추적 기능이 취소되었습니다.';
+
+ @override
+ String get mapCache_title => '오프라인 지도 캐시';
+
+ @override
+ String get mapCache_selectAreaFirst => '캐시할 영역을 먼저 선택하세요';
+
+ @override
+ String get mapCache_noTilesToDownload => '이 지역에 다운로드할 타일이 없습니다.';
+
+ @override
+ String get mapCache_downloadTilesTitle => '타일 다운로드';
+
+ @override
+ String mapCache_downloadTilesPrompt(int count) {
+ return '$count개의 타일을 오프라인 사용을 위해 다운로드하시겠습니까?';
+ }
+
+ @override
+ String get mapCache_downloadAction => '다운로드';
+
+ @override
+ String mapCache_cachedTiles(int count) {
+ return '$count 개의 타일 캐시';
+ }
+
+ @override
+ String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
+ return 'Cached $downloaded tiles ($failed failed)';
+ }
+
+ @override
+ String get mapCache_clearOfflineCacheTitle => '오프라인 캐시 삭제';
+
+ @override
+ String get mapCache_clearOfflineCachePrompt => '모든 캐시된 지도 템플릿을 삭제하시겠습니까?';
+
+ @override
+ String get mapCache_offlineCacheCleared => '오프라인 캐시 삭제';
+
+ @override
+ String get mapCache_noAreaSelected => '선택된 영역 없음';
+
+ @override
+ String get mapCache_cacheArea => '캐시 영역';
+
+ @override
+ String get mapCache_useCurrentView => '현재 보기 유지';
+
+ @override
+ String get mapCache_zoomRange => '줌 기능 범위';
+
+ @override
+ String mapCache_estimatedTiles(int count) {
+ return '예상되는 타일 개수: $count';
+ }
+
+ @override
+ String mapCache_downloadedTiles(int completed, int total) {
+ return 'Downloaded $completed / $total';
+ }
+
+ @override
+ String get mapCache_downloadTilesButton => '타일 다운로드';
+
+ @override
+ String get mapCache_clearCacheButton => '캐시 삭제';
+
+ @override
+ String mapCache_failedDownloads(int count) {
+ return '실패한 다운로드: $count';
+ }
+
+ @override
+ String mapCache_boundsLabel(
+ String north,
+ String south,
+ String east,
+ String west,
+ ) {
+ return 'N $north, S $south, E $east, W $west';
+ }
+
+ @override
+ String get time_justNow => '방금';
+
+ @override
+ String time_minutesAgo(int minutes) {
+ return '$minutes분 전';
+ }
+
+ @override
+ String time_hoursAgo(int hours) {
+ return '${hours}h ago';
+ }
+
+ @override
+ String time_daysAgo(int days) {
+ return '$days일 전';
+ }
+
+ @override
+ String get time_hour => '시간';
+
+ @override
+ String get time_hours => '시간';
+
+ @override
+ String get time_day => '하루';
+
+ @override
+ String get time_days => '일';
+
+ @override
+ String get time_week => '주';
+
+ @override
+ String get time_weeks => '몇 주';
+
+ @override
+ String get time_month => '달';
+
+ @override
+ String get time_months => '개월';
+
+ @override
+ String get time_minutes => '분';
+
+ @override
+ String get time_allTime => '모든 시간';
+
+ @override
+ String get dialog_disconnect => '연결 해제';
+
+ @override
+ String get dialog_disconnectConfirm => '이 장치와의 연결을 해제하시겠습니까?';
+
+ @override
+ String get login_repeaterLogin => '다시 로그인';
+
+ @override
+ String get login_roomLogin => '방 서버 로그인';
+
+ @override
+ String get login_password => '비밀번호';
+
+ @override
+ String get login_enterPassword => '비밀번호를 입력하세요';
+
+ @override
+ String get login_savePassword => '비밀번호 저장';
+
+ @override
+ String get login_savePasswordSubtitle => '비밀번호는 이 장치에 안전하게 저장됩니다.';
+
+ @override
+ String get login_repeaterDescription => '반복기 비밀번호를 입력하여 설정 및 상태를 확인하십시오.';
+
+ @override
+ String get login_roomDescription => '설정 및 상태에 액세스하려면 방 비밀번호를 입력하세요.';
+
+ @override
+ String get login_routing => '라우팅';
+
+ @override
+ String get login_routingMode => '라우팅 모드';
+
+ @override
+ String get login_autoUseSavedPath => '자동 (저장된 경로 사용)';
+
+ @override
+ String get login_forceFloodMode => '강수 모드 활성화';
+
+ @override
+ String get login_managePaths => '경로 관리';
+
+ @override
+ String get login_login => '로그인';
+
+ @override
+ String login_attempt(int current, int max) {
+ return '시도 $current/$max';
+ }
+
+ @override
+ String login_failed(String error) {
+ return '로그인 실패: $error';
+ }
+
+ @override
+ String get login_failedMessage =>
+ '로그인에 실패했습니다. 비밀번호가 잘못되었거나, 연결이 되지 않는 것 같습니다.';
+
+ @override
+ String get common_reload => '다시 로드';
+
+ @override
+ String get common_clear => '명확하게';
+
+ @override
+ String path_currentPath(String path) {
+ return '현재 경로: $path';
+ }
+
+ @override
+ String path_usingHopsPath(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'hops',
+ one: 'hop',
+ );
+ return 'Using $count $_temp0 path';
+ }
+
+ @override
+ String get path_enterCustomPath => '사용자 지정 경로 입력';
+
+ @override
+ String get path_currentPathLabel => '현재 경로';
+
+ @override
+ String get path_hexPrefixInstructions =>
+ '각 단계에 대한 2자리 헥사데진 접두사를 쉼표로 구분하여 입력하세요.';
+
+ @override
+ String get path_hexPrefixExample =>
+ '예시: A1, F2, 3C (각 노드는 자신의 공개 키의 첫 번째 바이트를 사용)';
+
+ @override
+ String get path_labelHexPrefixes => '경로 (헥스 접두사)';
+
+ @override
+ String get path_helperMaxHops =>
+ '최대 64개의 홉. 각 접두사는 2개의 16진수 문자(1바이트)로 구성됩니다.';
+
+ @override
+ String get path_selectFromContacts => '또 연락처 목록에서 선택:';
+
+ @override
+ String get path_noRepeatersFound => '반복 장치 또는 서버는 찾을 수 없습니다.';
+
+ @override
+ String get path_customPathsRequire =>
+ '사용자 정의 경로에는 메시지를 전달할 수 있는 중간 경로가 필요합니다.';
+
+ @override
+ String path_invalidHexPrefixes(String prefixes) {
+ return '유효하지 않은 16진수 접두사: $prefixes';
+ }
+
+ @override
+ String get path_tooLong => '경로가 너무 길어. 최대 64개의 연결만 허용됩니다.';
+
+ @override
+ String get path_setPath => '경로 설정';
+
+ @override
+ String get repeater_management => '리피터 관리';
+
+ @override
+ String get room_management => '방 서버 관리';
+
+ @override
+ String get repeater_guest => '반복 장비 정보';
+
+ @override
+ String get room_guest => '서버 정보';
+
+ @override
+ String get repeater_managementTools => '관리 도구';
+
+ @override
+ String get repeater_guestTools => '손님용 도구';
+
+ @override
+ String get repeater_status => '상태';
+
+ @override
+ String get repeater_statusSubtitle => '반복 장비의 상태, 통계, 및 이웃 장비 목록 보기';
+
+ @override
+ String get repeater_telemetry => '텔레메트리';
+
+ @override
+ String get repeater_telemetrySubtitle => '센서 및 시스템 상태에 대한 통신 데이터를 확인';
+
+ @override
+ String get repeater_cli => '명령줄 인터페이스 (CLI)';
+
+ @override
+ String get repeater_cliSubtitle => '리피터에 명령을 전송';
+
+ @override
+ String get repeater_neighbors => '이웃';
+
+ @override
+ String get repeater_neighborsSubtitle => '0홉 이웃 노드를 확인합니다.';
+
+ @override
+ String get repeater_settings => '설정';
+
+ @override
+ String get repeater_settingsSubtitle => '리피터 파라미터 설정';
+
+ @override
+ String get repeater_clockSyncAfterLogin => '로그인 후 시계 동기화';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ '성공적인 로그인 후, 자동으로 \"시간 동기화\"를 전송합니다.';
+
+ @override
+ String get repeater_statusTitle => '반복 장치 상태';
+
+ @override
+ String get repeater_routingMode => '라우팅 방식';
+
+ @override
+ String get repeater_autoUseSavedPath => '자동 (저장된 경로 사용)';
+
+ @override
+ String get repeater_forceFloodMode => '강수 모드 활성화';
+
+ @override
+ String get repeater_pathManagement => '경로 관리';
+
+ @override
+ String get repeater_refresh => '새롭게';
+
+ @override
+ String get repeater_statusRequestTimeout => '상태 확인 요청이 시간 초과되었습니다.';
+
+ @override
+ String repeater_errorLoadingStatus(String error) {
+ return '상태 로딩 오류: $error';
+ }
+
+ @override
+ String get repeater_systemInformation => '시스템 정보';
+
+ @override
+ String get repeater_battery => '배터리';
+
+ @override
+ String get repeater_clockAtLogin => '로그인 시 시간 표시';
+
+ @override
+ String get repeater_uptime => '가동 시간';
+
+ @override
+ String get repeater_queueLength => '대기 줄의 길이';
+
+ @override
+ String get repeater_debugFlags => '디버깅 플래그';
+
+ @override
+ String get repeater_radioStatistics => '라디오 통계';
+
+ @override
+ String get repeater_lastRssi => '마지막 RSSI 값';
+
+ @override
+ String get repeater_lastSnr => '마지막 SNR';
+
+ @override
+ String get repeater_noiseFloor => '잡음 수준';
+
+ @override
+ String get repeater_txAirtime => 'TX 에어타임';
+
+ @override
+ String get repeater_rxAirtime => 'RX 에어타임';
+
+ @override
+ String get repeater_packetStatistics => '패킷 통계';
+
+ @override
+ String get repeater_sent => '발송';
+
+ @override
+ String get repeater_received => '수신';
+
+ @override
+ String get repeater_duplicates => '중복';
+
+ @override
+ String repeater_daysHoursMinsSecs(
+ int days,
+ int hours,
+ int minutes,
+ int seconds,
+ ) {
+ return '$days일 $hours시간 $minutes분 $seconds초';
+ }
+
+ @override
+ String repeater_packetTxTotal(int total, String flood, String direct) {
+ return '총: $total, 홍수: $flood, 직접: $direct';
+ }
+
+ @override
+ String repeater_packetRxTotal(int total, String flood, String direct) {
+ return '총: $total, 홍수: $flood, 직접: $direct';
+ }
+
+ @override
+ String repeater_duplicatesFloodDirect(String flood, String direct) {
+ return '홍수: $flood, 직접: $direct';
+ }
+
+ @override
+ String repeater_duplicatesTotal(int total) {
+ return '총: $total';
+ }
+
+ @override
+ String get repeater_settingsTitle => '리피터 설정';
+
+ @override
+ String get repeater_basicSettings => '기본 설정';
+
+ @override
+ String get repeater_repeaterName => '반복 장비 이름';
+
+ @override
+ String get repeater_repeaterNameHelper => '이 반복기용 표시 이름';
+
+ @override
+ String get repeater_adminPassword => '관리자 비밀번호';
+
+ @override
+ String get repeater_adminPasswordHelper => '전체 접근 권한 비밀번호';
+
+ @override
+ String get repeater_guestPassword => '게스트 비밀번호';
+
+ @override
+ String get repeater_guestPasswordHelper => '읽기 전용 접근 비밀번호';
+
+ @override
+ String get repeater_radioSettings => '라디오 설정';
+
+ @override
+ String get repeater_frequencyMhz => '주파수 (MHz)';
+
+ @override
+ String get repeater_frequencyHelper => '300-2500 MHz';
+
+ @override
+ String get repeater_txPower => 'TX 파워';
+
+ @override
+ String get repeater_txPowerHelper => '1~30 dBm';
+
+ @override
+ String get repeater_bandwidth => '대역폭';
+
+ @override
+ String get repeater_spreadingFactor => '분산 계수';
+
+ @override
+ String get repeater_codingRate => '코딩 속도';
+
+ @override
+ String get repeater_locationSettings => '위치 설정';
+
+ @override
+ String get repeater_latitude => '위도';
+
+ @override
+ String get repeater_latitudeHelper => '십진법 위도 (예: 37.7749)';
+
+ @override
+ String get repeater_longitude => '경도';
+
+ @override
+ String get repeater_longitudeHelper => '십진법 위도 (예: -122.4194)';
+
+ @override
+ String get repeater_features => '특징';
+
+ @override
+ String get repeater_packetForwarding => '패킷 전송';
+
+ @override
+ String get repeater_packetForwardingSubtitle => '리피터가 패킷을 전달하도록 설정';
+
+ @override
+ String get repeater_guestAccess => '게스트 접근';
+
+ @override
+ String get repeater_guestAccessSubtitle => '게스트의 읽기 전용 접근 권한 허용';
+
+ @override
+ String get repeater_privacyMode => '개인 정보 보호 모드';
+
+ @override
+ String get repeater_privacyModeSubtitle => '광고에 이름/위치 정보 숨기기';
+
+ @override
+ String get repeater_advertisementSettings => '광고 설정';
+
+ @override
+ String get repeater_localAdvertInterval => '지역 광고 시간 간격';
+
+ @override
+ String repeater_localAdvertIntervalMinutes(int minutes) {
+ return '$minutes 분';
+ }
+
+ @override
+ String get repeater_floodAdvertInterval => '홍수 광고 간격';
+
+ @override
+ String repeater_floodAdvertIntervalHours(int hours) {
+ return '$hours 시간';
+ }
+
+ @override
+ String get repeater_encryptedAdvertInterval => '암호화된 광고 간격';
+
+ @override
+ String get repeater_dangerZone => '위험 구역';
+
+ @override
+ String get repeater_rebootRepeater => '리부트 반복';
+
+ @override
+ String get repeater_rebootRepeaterSubtitle => '리피터 장치를 재시작하세요.';
+
+ @override
+ String get repeater_rebootRepeaterConfirm => '반복기를 재부팅하시려는 것이 맞으신가요?';
+
+ @override
+ String get repeater_regenerateIdentityKey => '아이디 키 재 생성';
+
+ @override
+ String get repeater_regenerateIdentityKeySubtitle => '새로운 공개/개인 키 쌍 생성';
+
+ @override
+ String get repeater_regenerateIdentityKeyConfirm =>
+ '이를 통해 리피터에 새로운 식별자를 할당합니다. 계속 진행하시겠습니까?';
+
+ @override
+ String get repeater_eraseFileSystem => '파일 시스템 삭제';
+
+ @override
+ String get repeater_eraseFileSystemSubtitle => '리피터 파일 시스템을 포맷합니다.';
+
+ @override
+ String get repeater_eraseFileSystemConfirm =>
+ '경고: 이 작업은 리피터에 있는 모든 데이터를 삭제합니다. 이 작업을 되돌릴 수 없습니다!';
+
+ @override
+ String get repeater_eraseSerialOnly =>
+ '\'Erase\' 기능은 시리얼 콘솔을 통해서만 사용할 수 있습니다.';
+
+ @override
+ String repeater_commandSent(String command) {
+ return '명령 전송: $command';
+ }
+
+ @override
+ String repeater_errorSendingCommand(String error) {
+ return '명령 전송 오류: $error';
+ }
+
+ @override
+ String get repeater_confirm => '확인';
+
+ @override
+ String get repeater_settingsSaved => '설정이 성공적으로 저장되었습니다.';
+
+ @override
+ String repeater_errorSavingSettings(String error) {
+ return '설정 저장 오류: $error';
+ }
+
+ @override
+ String get repeater_refreshBasicSettings => '기본 설정 초기화';
+
+ @override
+ String get repeater_refreshRadioSettings => '라디오 설정 초기화';
+
+ @override
+ String get repeater_refreshTxPower => 'TX 전원 재설정';
+
+ @override
+ String get repeater_refreshLocationSettings => '위치 설정 초기화';
+
+ @override
+ String get repeater_refreshPacketForwarding => '패킷 전송 재시작';
+
+ @override
+ String get repeater_refreshGuestAccess => '게스트 접근 권한 갱신';
+
+ @override
+ String get repeater_refreshPrivacyMode => '개인 정보 보호 모드 재설정';
+
+ @override
+ String get repeater_refreshAdvertisementSettings => '광고 설정 재설정';
+
+ @override
+ String repeater_refreshed(String label) {
+ return '$label가 갱신됨';
+ }
+
+ @override
+ String repeater_errorRefreshing(String label) {
+ return '$label를 새로 고침 중 오류 발생';
+ }
+
+ @override
+ String get repeater_cliTitle => '리피터 CLI';
+
+ @override
+ String get repeater_debugNextCommand => '다음 명령 디버깅';
+
+ @override
+ String get repeater_commandHelp => '명령 도움';
+
+ @override
+ String get repeater_clearHistory => '명확한 역사';
+
+ @override
+ String get repeater_noCommandsSent => '아직 명령이 전송되지 않았습니다.';
+
+ @override
+ String get repeater_typeCommandOrUseQuick => '아래에 명령어를 입력하거나, 빠른 명령어를 사용하세요.';
+
+ @override
+ String get repeater_enterCommandHint => '명령어를 입력하세요...';
+
+ @override
+ String get repeater_previousCommand => '이전 명령어';
+
+ @override
+ String get repeater_nextCommand => '다음 명령어';
+
+ @override
+ String get repeater_enterCommandFirst => '먼저 명령어를 입력하세요';
+
+ @override
+ String get repeater_cliCommandFrameTitle => 'CLI 명령어 프레임';
+
+ @override
+ String repeater_cliCommandError(String error) {
+ return '오류: $error';
+ }
+
+ @override
+ String get repeater_cliQuickGetName => '이름을 알려주세요';
+
+ @override
+ String get repeater_cliQuickGetRadio => '라디오 듣기';
+
+ @override
+ String get repeater_cliQuickGetTx => 'TX 획득';
+
+ @override
+ String get repeater_cliQuickNeighbors => '이웃';
+
+ @override
+ String get repeater_cliQuickVersion => '버전';
+
+ @override
+ String get repeater_cliQuickAdvertise => '광고';
+
+ @override
+ String get repeater_cliQuickClock => '시계';
+
+ @override
+ String get repeater_cliQuickClockSync => '시계 동기화';
+
+ @override
+ String get repeater_cliQuickDiscovery => '이웃 발견하기';
+
+ @override
+ String get repeater_cliHelpAdvert => '광고 패킷을 발송';
+
+ @override
+ String get repeater_cliHelpReboot =>
+ '장치를 재부팅합니다. (참고: \'시간 초과\' 오류가 발생할 수 있으며, 이는 정상적인 현상입니다)';
+
+ @override
+ String get repeater_cliHelpClock => '각 기기의 시계에 표시되는 현재 시간';
+
+ @override
+ String get repeater_cliHelpPassword => '장치에 새로운 관리자 비밀번호를 설정합니다.';
+
+ @override
+ String get repeater_cliHelpVersion => '장치 버전 및 펌웨어 빌드 날짜를 표시합니다.';
+
+ @override
+ String get repeater_cliHelpClearStats => '다양한 통계 지표를 0으로 초기화합니다.';
+
+ @override
+ String get repeater_cliHelpSetAf => '에어 타임 요소를 설정합니다.';
+
+ @override
+ String get repeater_cliHelpSetTx =>
+ 'LoRa 전송 전력을 dBm 단위로 설정합니다. (설정을 적용하려면 재부팅 필요)';
+
+ @override
+ String get repeater_cliHelpSetRepeat => '이 노드에 대한 리피터 역할을 활성화하거나 비활성화합니다.';
+
+ @override
+ String get repeater_cliHelpSetAllowReadOnly =>
+ '(방 서버) \'켜짐\' 상태인 경우, 빈 비밀번호로 로그인할 수 있지만, 방에 게시할 수는 없습니다 (단, 읽기만 가능).';
+
+ @override
+ String get repeater_cliHelpSetFloodMax =>
+ '들어오는 플러드 패킷의 최대 홉 수를 설정합니다 (최대 홉 수보다 크거나 같으면 패킷은 전달되지 않습니다).';
+
+ @override
+ String get repeater_cliHelpSetIntThresh =>
+ '간섭 임계값을 설정합니다 (dB 단위). 기본값은 14입니다. 0으로 설정하면 채널 간섭 감지 기능을 비활성화합니다.';
+
+ @override
+ String get repeater_cliHelpSetAgcResetInterval =>
+ '자동 게인 제어기를 재설정하는 간격을 설정합니다. 0으로 설정하면 비활성화됩니다.';
+
+ @override
+ String get repeater_cliHelpSetMultiAcks =>
+ '\'더블 ACK\' 기능을 활성화하거나 비활성화할 수 있습니다.';
+
+ @override
+ String get repeater_cliHelpSetAdvertInterval =>
+ '로컬 (제로 홉) 광고 패킷을 전송하는 간격 (분 단위)을 설정합니다. 0으로 설정하면 비활성화됩니다.';
+
+ @override
+ String get repeater_cliHelpSetFloodAdvertInterval =>
+ '시간 단위로 광고 패킷을 전송하는 간격을 설정합니다. 0으로 설정하면 비활성화됩니다.';
+
+ @override
+ String get repeater_cliHelpSetGuestPassword =>
+ '게스트 비밀번호를 설정하거나 업데이트합니다. (반복 사용자, 게스트 로그인 시 \"통계 가져오기\" 요청을 보낼 수 있음)';
+
+ @override
+ String get repeater_cliHelpSetName => '광고 이름을 설정합니다.';
+
+ @override
+ String get repeater_cliHelpSetLat => '광고 지도의 위도를 설정합니다. (십진법 단위)';
+
+ @override
+ String get repeater_cliHelpSetLon => '광고 지도의 경도를 설정합니다. (십진도)';
+
+ @override
+ String get repeater_cliHelpSetRadio =>
+ '완전히 새로운 라디오 파라미터를 설정하고, 선호 사항에 저장합니다. 적용하려면 \"재부팅\" 명령이 필요합니다.';
+
+ @override
+ String get repeater_cliHelpSetRxDelay =>
+ '(실험용) 기본 설정 (최소 1이어야 함)으로, 수신된 패킷에 약간의 지연을 적용하며, 신호 강도/점수를 기준으로 설정합니다. 0으로 설정하면 비활성화됩니다.';
+
+ @override
+ String get repeater_cliHelpSetTxDelay =>
+ '공통 패킷의 전송 지연 시간을 설정하며, 시간-공기 시간과 무작위 슬롯 시스템을 곱하여 충돌 가능성을 줄입니다.';
+
+ @override
+ String get repeater_cliHelpSetDirectTxDelay =>
+ 'txdelay와 동일하게, 하지만 직접 모드 패킷 전송 시 무작위 지연을 적용하는 경우';
+
+ @override
+ String get repeater_cliHelpSetBridgeEnabled => '브리지 활성화/비활성화';
+
+ @override
+ String get repeater_cliHelpSetBridgeDelay => '패킷 재전송 전에 지연 시간을 설정합니다.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSource =>
+ '브리지가 수신된 패킷을 다시 전송할지, 아니면 전송된 패킷을 다시 전송할지 선택하십시오.';
+
+ @override
+ String get repeater_cliHelpSetBridgeBaud =>
+ 'rs232 브리지에 대한 직렬 통신 속도(baud rate)를 설정합니다.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSecret => 'ESPNow 브리지에 대한 비밀 설정';
+
+ @override
+ String get repeater_cliHelpSetAdcMultiplier =>
+ '특정 보드에서만 지원되는 방식으로, 보고되는 배터리 전압을 조정하기 위한 사용자 정의 요소를 설정할 수 있습니다.';
+
+ @override
+ String get repeater_cliHelpTempRadio =>
+ '주어진 시간(분) 동안 임시 라디오 파라미터를 설정하고, 이후 원래 라디오 파라미터로 되돌립니다. (설정을 저장하지 않습니다).';
+
+ @override
+ String get repeater_cliHelpSetPerm =>
+ 'ACL을 수정합니다. \"permissions\" 값이 0인 경우, 일치하는 항목(pubkey 접두사)을 제거합니다. pubkey-hex 길이가 완전하고 현재 ACL에 없는 경우 새로운 항목을 추가합니다. pubkey 접두사를 기준으로 항목을 업데이트합니다. 권한 비트는 펌웨어 역할에 따라 다르지만, 하위 2비트는 다음과 같습니다: 0 (게스트), 1 (읽기 전용), 2 (읽기/쓰기), 3 (관리자)';
+
+ @override
+ String get repeater_cliHelpGetBridgeType => '브리지형, RS232, ESPNOW 지원';
+
+ @override
+ String get repeater_cliHelpLogStart => '패킷 로깅을 파일 시스템으로 시작합니다.';
+
+ @override
+ String get repeater_cliHelpLogStop => '패킷 로깅을 파일 시스템으로 저장하는 것을 중단합니다.';
+
+ @override
+ String get repeater_cliHelpLogErase => '파일 시스템에서 패킷 로그를 삭제합니다.';
+
+ @override
+ String get repeater_cliHelpNeighbors =>
+ '제로 홉 광고를 통해 수신된 다른 리피터 노드 목록을 보여줍니다. 각 줄은 ID-프리픽스-16진수:타임스탬프:SNR-횟수-4 형식입니다.';
+
+ @override
+ String get repeater_cliHelpNeighborRemove =>
+ '이 함수는 지정된 pubkey 접두사(16진수)와 일치하는 첫 번째 항목을 이웃 목록에서 제거합니다.';
+
+ @override
+ String get repeater_cliHelpRegion =>
+ '(단일 시리즈) 정의된 모든 지역과 현재 홍수 허가 정보를 나열합니다.';
+
+ @override
+ String get repeater_cliHelpRegionLoad =>
+ '참고: 이는 여러 명령을 한 번에 실행하는 특별한 방식입니다. 각 후속 명령은 영역 이름이며 (부모 계층 구조를 나타내기 위해 공백으로 들여쓰기하며, 최소 1개의 공백을 사용) 공백으로 끝나는 줄 또는 명령을 보내어 종료합니다.';
+
+ @override
+ String get repeater_cliHelpRegionGet =>
+ '주어진 이름 접두사(또는 전역 검색을 위한 \"\\*\" 사용)를 사용하여 특정 지역을 검색합니다. 결과를 \"-> 지역 이름 (상위 지역 이름) \'F\'\" 형태로 반환합니다.';
+
+ @override
+ String get repeater_cliHelpRegionPut => '주어진 이름으로 지역 정의를 추가하거나 업데이트합니다.';
+
+ @override
+ String get repeater_cliHelpRegionRemove =>
+ '지정된 이름으로 특정 영역 정의를 제거합니다. (정확히 일치해야 하며, 하위 영역은 존재하지 않아야 합니다)';
+
+ @override
+ String get repeater_cliHelpRegionAllowf =>
+ '지정된 영역에 대한 \'물\' 접근 권한을 설정합니다. (\'*\'는 전역/기존 범위에 해당)';
+
+ @override
+ String get repeater_cliHelpRegionDenyf =>
+ '지정된 영역에 대해 \'Flood\' 권한을 제거합니다. (참고: 현재 단계에서는 전역/기존 범위에서 이 기능을 사용하지 않는 것이 좋습니다!!)';
+
+ @override
+ String get repeater_cliHelpRegionHome =>
+ '현재 \'홈\' 지역으로 응답합니다. (아직 적용되지 않았으며, 향후 사용을 위해 예약됨)';
+
+ @override
+ String get repeater_cliHelpRegionHomeSet => '\'홈\' 지역을 설정합니다.';
+
+ @override
+ String get repeater_cliHelpRegionSave => '지역 목록/지도를 저장에 유지합니다.';
+
+ @override
+ String get repeater_cliHelpGps =>
+ 'GPS 상태를 표시합니다. GPS가 꺼져 있으면 \"꺼짐\"이라고 표시하고, 켜져 있으면 \"켜짐\", 상태, 위치 정보, 위성 수 등을 표시합니다.';
+
+ @override
+ String get repeater_cliHelpGpsOnOff => 'GPS 전원 상태를 켜고 끄는 기능.';
+
+ @override
+ String get repeater_cliHelpGpsSync => '노드 시간을 GPS 시계와 동기화합니다.';
+
+ @override
+ String get repeater_cliHelpGpsSetLoc => '노드의 위치를 GPS 좌표로 설정하고, 설정을 저장합니다.';
+
+ @override
+ String get repeater_cliHelpGpsAdvert =>
+ '노드의 위치 광고 설정:\n- none: 광고에 위치 정보를 포함하지 않음\n- share: GPS 위치 정보를 공유 (SensorManager에서 가져옴)\n- prefs: 설정에 저장된 위치를 광고';
+
+ @override
+ String get repeater_cliHelpGpsAdvertSet => '위치 기반 광고 설정 구성';
+
+ @override
+ String get repeater_commandsListTitle => '명령 목록';
+
+ @override
+ String get repeater_commandsListNote =>
+ '참고: 다양한 \"set...\" 명령과 함께 \"get...\" 명령도 존재합니다.';
+
+ @override
+ String get repeater_general => '일반';
+
+ @override
+ String get repeater_settingsCategory => '설정';
+
+ @override
+ String get repeater_bridge => '다리';
+
+ @override
+ String get repeater_logging => '로깅';
+
+ @override
+ String get repeater_neighborsRepeaterOnly => '이웃 (단방향 통신만 지원)';
+
+ @override
+ String get repeater_regionManagementRepeaterOnly => '지역 관리 (단, 중계 기능만 사용)';
+
+ @override
+ String get repeater_regionNote =>
+ '지역별 관리 기능을 도입하여 지역 정의 및 권한 관리를 수행할 수 있습니다.';
+
+ @override
+ String get repeater_gpsManagement => 'GPS 관리';
+
+ @override
+ String get repeater_gpsNote => 'GPS 명령이 위치 관련 주제를 관리하기 위해 도입되었습니다.';
+
+ @override
+ String get telemetry_receivedData => '수신된 통신 데이터';
+
+ @override
+ String get telemetry_requestTimeout => '원격 모니터링 요청이 시간 초과되었습니다.';
+
+ @override
+ String telemetry_errorLoading(String error) {
+ return '$error 오류로 인해 통신 데이터를 로드하지 못했습니다.';
+ }
+
+ @override
+ String get telemetry_noData => '텔레메트리 데이터는 제공되지 않습니다.';
+
+ @override
+ String telemetry_channelTitle(int channel) {
+ return '채널 $channel';
+ }
+
+ @override
+ String get telemetry_batteryLabel => '배터리';
+
+ @override
+ String get telemetry_voltageLabel => '전압';
+
+ @override
+ String get telemetry_mcuTemperatureLabel => 'MCU의 온도';
+
+ @override
+ String get telemetry_temperatureLabel => '온도';
+
+ @override
+ String get telemetry_currentLabel => '현재';
+
+ @override
+ String telemetry_batteryValue(int percent, String volts) {
+ return '$percent% / ${volts}V';
+ }
+
+ @override
+ String telemetry_voltageValue(String volts) {
+ return '${volts}V';
+ }
+
+ @override
+ String telemetry_currentValue(String amps) {
+ return '${amps}A';
+ }
+
+ @override
+ String telemetry_temperatureValue(String celsius, String fahrenheit) {
+ return '$celsius°C / $fahrenheit°F';
+ }
+
+ @override
+ String get neighbors_receivedData => '이웃 정보 수집';
+
+ @override
+ String get neighbors_requestTimedOut => '이웃들이 시간 제한을 요청하고 있습니다.';
+
+ @override
+ String neighbors_errorLoading(String error) {
+ return '이웃 정보 로딩 중 오류: $error';
+ }
+
+ @override
+ String get neighbors_repeatersNeighbors => '반복기, 이웃';
+
+ @override
+ String get neighbors_noData => '이웃 정보는 없습니다.';
+
+ @override
+ String neighbors_unknownContact(String pubkey) {
+ return '알 수 없는 $pubkey';
+ }
+
+ @override
+ String neighbors_heardAgo(String time) {
+ return 'Heard: $time ago';
+ }
+
+ @override
+ String get channelPath_title => '패킷 경로';
+
+ @override
+ String get channelPath_viewMap => '지도 보기';
+
+ @override
+ String get channelPath_otherObservedPaths => '관찰된 다른 경로';
+
+ @override
+ String get channelPath_repeaterHops => '반복 홉';
+
+ @override
+ String get channelPath_noHopDetails => '이 패키지에 대한 자세한 정보는 제공되지 않습니다.';
+
+ @override
+ String get channelPath_messageDetails => '메시지 세부 정보';
+
+ @override
+ String get channelPath_senderLabel => '발신자';
+
+ @override
+ String get channelPath_timeLabel => '시간';
+
+ @override
+ String get channelPath_repeatsLabel => '반복';
+
+ @override
+ String channelPath_pathLabel(int index) {
+ return '경로 $index';
+ }
+
+ @override
+ String get channelPath_observedLabel => '관찰';
+
+ @override
+ String channelPath_observedPathTitle(int index, String hops) {
+ return '관찰된 경로 $index • $hops';
+ }
+
+ @override
+ String get channelPath_noLocationData => '위치 정보 없음';
+
+ @override
+ String channelPath_timeWithDate(int day, int month, String time) {
+ return '$day/$month $time';
+ }
+
+ @override
+ String channelPath_timeOnly(String time) {
+ return '$time';
+ }
+
+ @override
+ String get channelPath_unknownPath => '알 수 없음';
+
+ @override
+ String get channelPath_floodPath => '홍수';
+
+ @override
+ String get channelPath_directPath => '직접';
+
+ @override
+ String channelPath_observedZeroOf(int total) {
+ return '$total 중 0개';
+ }
+
+ @override
+ String channelPath_observedSomeOf(int observed, int total) {
+ return '$observed of $total hops';
+ }
+
+ @override
+ String get channelPath_mapTitle => '경로 지도';
+
+ @override
+ String get channelPath_noRepeaterLocations => '이 경로에 대한 중계기 설치 위치는 없습니다.';
+
+ @override
+ String channelPath_primaryPath(int index) {
+ return '경로 $index (주 경로)';
+ }
+
+ @override
+ String get channelPath_pathLabelTitle => '경로';
+
+ @override
+ String get channelPath_observedPathHeader => '관찰된 경로';
+
+ @override
+ String channelPath_selectedPathLabel(String label, String prefixes) {
+ return '$label • $prefixes';
+ }
+
+ @override
+ String get channelPath_noHopDetailsAvailable => '이 패킷에 대한 이동 정보는 제공되지 않습니다.';
+
+ @override
+ String get channelPath_unknownRepeater => '알 수 없는 중계기';
+
+ @override
+ String get community_title => '지역 사회';
+
+ @override
+ String get community_create => '커뮤니티 만들기';
+
+ @override
+ String get community_createDesc => '새로운 커뮤니티를 만들고 QR 코드를 통해 공유하세요.';
+
+ @override
+ String get community_join => '참여하기';
+
+ @override
+ String get community_joinTitle => '커뮤니티에 참여하기';
+
+ @override
+ String community_joinConfirmation(String name) {
+ return '$name님, 커뮤니티에 참여하고 싶으신가요?';
+ }
+
+ @override
+ String get community_scanQr => '커뮤니티 QR 스캔';
+
+ @override
+ String get community_scanInstructions => '카메라를 커뮤니티 QR 코드 방향으로 향하게 하세요.';
+
+ @override
+ String get community_showQr => 'QR 코드 표시';
+
+ @override
+ String get community_publicChannel => '지역 사회 대상';
+
+ @override
+ String get community_hashtagChannel => '커뮤니티 해시태그';
+
+ @override
+ String get community_name => '지역 이름';
+
+ @override
+ String get community_enterName => '커뮤니티 이름을 입력하세요';
+
+ @override
+ String community_created(String name) {
+ return '커뮤니티 \"$name\"이 생성되었습니다.';
+ }
+
+ @override
+ String community_joined(String name) {
+ return '\"$name\" 커뮤니티에 가입';
+ }
+
+ @override
+ String get community_qrTitle => '커뮤니티 공유';
+
+ @override
+ String community_qrInstructions(String name) {
+ return '이 QR 코드를 스캔하여 \"$name\"에 가입하세요.';
+ }
+
+ @override
+ String get community_hashtagPrivacyHint =>
+ '커뮤니티 해시태그 채널은 커뮤니티 구성원만 가입할 수 있습니다.';
+
+ @override
+ String get community_invalidQrCode => '유효하지 않은 커뮤니티 QR 코드';
+
+ @override
+ String get community_alreadyMember => '이미 회원인 경우';
+
+ @override
+ String community_alreadyMemberMessage(String name) {
+ return '이미 $name의 회원입니다.';
+ }
+
+ @override
+ String get community_addPublicChannel => '커뮤니티 공개 채널 추가';
+
+ @override
+ String get community_addPublicChannelHint => '이 커뮤니티에 공개 채널을 자동으로 추가합니다.';
+
+ @override
+ String get community_noCommunities => '아직 어느 커뮤니티도 가입하지 않았습니다.';
+
+ @override
+ String get community_scanOrCreate => 'QR 코드를 스캔하거나 커뮤니티를 만들어 시작하세요.';
+
+ @override
+ String get community_manageCommunities => '커뮤니티 관리';
+
+ @override
+ String get community_delete => '커뮤니티 떠나기';
+
+ @override
+ String community_deleteConfirm(String name) {
+ return '$name을 묻어두나요?';
+ }
+
+ @override
+ String community_deleteChannelsWarning(int count) {
+ return '또한, 이 기능은 $count개의 채널과 그에 해당하는 메시지를 삭제합니다.';
+ }
+
+ @override
+ String community_deleted(String name) {
+ return '지역 커뮤니티 \"$name\"';
+ }
+
+ @override
+ String get community_regenerateSecret => '비밀 복원';
+
+ @override
+ String community_regenerateSecretConfirm(String name) {
+ return '$name의 비밀 키를 재생성하시겠습니까? 모든 회원은 계속 통신을 위해 새로운 QR 코드를 스캔해야 합니다.';
+ }
+
+ @override
+ String get community_regenerate => '재생';
+
+ @override
+ String community_secretRegenerated(String name) {
+ return '$name을 위한 비밀 정보가 복원되었습니다.';
+ }
+
+ @override
+ String get community_updateSecret => '비밀 업데이트';
+
+ @override
+ String community_secretUpdated(String name) {
+ return '$name을 위한 비밀 정보 업데이트';
+ }
+
+ @override
+ String community_scanToUpdateSecret(String name) {
+ return '새로운 QR 코드를 스캔하여 $name의 비밀번호를 업데이트하세요.';
+ }
+
+ @override
+ String get community_addHashtagChannel => '커뮤니티 해시태그 추가';
+
+ @override
+ String get community_addHashtagChannelDesc => '이 커뮤니티를 위한 해시태그 채널을 추가하세요.';
+
+ @override
+ String get community_selectCommunity => '커뮤니티 선택';
+
+ @override
+ String get community_regularHashtag => '일반 해시태그';
+
+ @override
+ String get community_regularHashtagDesc => '공개 해시태그 (누구나 참여 가능)';
+
+ @override
+ String get community_communityHashtag => '커뮤니티 해시태그';
+
+ @override
+ String get community_communityHashtagDesc => '지역 주민을 위한';
+
+ @override
+ String community_forCommunity(String name) {
+ return '$name 님께';
+ }
+
+ @override
+ String get listFilter_tooltip => '필터링 및 정렬';
+
+ @override
+ String get listFilter_sortBy => '정렬 기준 선택';
+
+ @override
+ String get listFilter_latestMessages => '최신 메시지';
+
+ @override
+ String get listFilter_heardRecently => '최근에 들었습니다';
+
+ @override
+ String get listFilter_az => 'A부터 Z까지';
+
+ @override
+ String get listFilter_filters => '필터';
+
+ @override
+ String get listFilter_all => '모든';
+
+ @override
+ String get listFilter_favorites => '관심 목록';
+
+ @override
+ String get listFilter_addToFavorites => '즐겨찾으로 추가';
+
+ @override
+ String get listFilter_removeFromFavorites => '즐겨찾에서 제거';
+
+ @override
+ String get listFilter_users => '사용자';
+
+ @override
+ String get listFilter_repeaters => '다시 보내는 장치';
+
+ @override
+ String get listFilter_roomServers => '방 내 서버';
+
+ @override
+ String get listFilter_unreadOnly => '읽지 않은 항목만';
+
+ @override
+ String get listFilter_newGroup => '새로운 그룹';
+
+ @override
+ String get pathTrace_you => '당신';
+
+ @override
+ String get pathTrace_failed => '경로 추적 실패.';
+
+ @override
+ String get pathTrace_notAvailable => '경로 추적 기능은 제공되지 않습니다.';
+
+ @override
+ String get pathTrace_refreshTooltip => '경로 추적 재시작';
+
+ @override
+ String get pathTrace_someHopsNoLocation => '홉 중 하나 또는 여러 개에 위치 정보가 누락되었습니다!';
+
+ @override
+ String get pathTrace_clearTooltip => '명확한 경로.';
+
+ @override
+ String get losSelectStartEnd => 'LOS(최소 거리 경로)의 시작 및 종료 노드를 선택합니다.';
+
+ @override
+ String losRunFailed(String error) {
+ return '시야 확인 실패: $error';
+ }
+
+ @override
+ String get losClearAllPoints => '모든 사항을 명확히 합니다.';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'LOS(Line of Sight)를 사용하여 고도 프로필을 확인합니다.';
+
+ @override
+ String get losMenuTitle => 'LOS 메뉴';
+
+ @override
+ String get losMenuSubtitle => '사용자 지정 지점을 추가하려면, 노드를 탭하거나 맵을 길게 눌러 주세요.';
+
+ @override
+ String get losShowDisplayNodes => '노드 표시';
+
+ @override
+ String get losCustomPoints => '사용자 지정 포인트';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return '맞춤형 $index';
+ }
+
+ @override
+ String get losPointA => 'A 지점';
+
+ @override
+ String get losPointB => '점 B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return '안테나 A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenna B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'LOS (Loss of Signal) 상태로 전환';
+
+ @override
+ String get losNoElevationData => '고도 정보 없음';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, clear LOS, min clearance $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blocked by $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: 확인 중...';
+
+ @override
+ String get losStatusNoData => 'LOS: 데이터 없음';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total 개, $blocked 개, $unknown 개';
+ }
+
+ @override
+ String get losErrorElevationUnavailable => '샘플 중 하나 이상에 대한 고도 데이터가 없습니다.';
+
+ @override
+ String get losErrorInvalidInput => 'LOS 계산에 사용되는 부정확한 지점/고도 데이터.';
+
+ @override
+ String get losRenameCustomPoint => '사용자 지정된 지점의 이름을 변경';
+
+ @override
+ String get losPointName => '항목 이름';
+
+ @override
+ String get losShowPanelTooltip => 'LOS 패널 표시';
+
+ @override
+ String get losHidePanelTooltip => 'LOS 패널 숨기기';
+
+ @override
+ String get losElevationAttribution => '고도 데이터: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => '라디오 호라이즌';
+
+ @override
+ String get losLegendLosBeam => 'LOS 빔';
+
+ @override
+ String get losLegendTerrain => '지형';
+
+ @override
+ String get losFrequencyLabel => '빈도';
+
+ @override
+ String get losFrequencyInfoTooltip => '계산 내역 보기';
+
+ @override
+ String get losFrequencyDialogTitle => '라디오 수신 가능 범위 계산';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return '$baselineK에서 시작하여 $baselineFreq MHz의 주파수에서 계산을 시작하면, 현재 $frequencyMHz MHz 대역에 대한 k-값을 조정하여, 이는 곡선형 라디오 지평선 상한선을 정의합니다.';
+ }
+
+ @override
+ String get contacts_pathTrace => '경로 추적';
+
+ @override
+ String get contacts_ping => '핑';
+
+ @override
+ String get contacts_repeaterPathTrace => '리피터로 가는 경로';
+
+ @override
+ String get contacts_repeaterPing => '핑 반복';
+
+ @override
+ String get contacts_roomPathTrace => '방 서버로의 경로 추적';
+
+ @override
+ String get contacts_roomPing => '피нг 룸 서버';
+
+ @override
+ String get contacts_chatTraceRoute => '경로 추적 경로';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return '$name까지의 경로 추적';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => '클립보드가 비어 있습니다.';
+
+ @override
+ String get contacts_invalidAdvertFormat => '유효하지 않은 연락 정보';
+
+ @override
+ String get contacts_contactImported => '연락이 수신되었습니다.';
+
+ @override
+ String get contacts_contactImportFailed => '연락처를 가져오지 못했습니다.';
+
+ @override
+ String get contacts_zeroHopAdvert => '제로 홉 광고';
+
+ @override
+ String get contacts_floodAdvert => '홍수 광고';
+
+ @override
+ String get contacts_copyAdvertToClipboard => '광고 텍스트를 클립보드에 복사';
+
+ @override
+ String get contacts_addContactFromClipboard => '복사본에서 연락처 추가';
+
+ @override
+ String get contacts_ShareContact => '연락처를 복사';
+
+ @override
+ String get contacts_ShareContactZeroHop => '광고를 통해 연락처 공유';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => '광고를 통해 연락처를 받았습니다.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed => '연락처 전송에 실패했습니다.';
+
+ @override
+ String get contacts_contactAdvertCopied => '광고 내용이 복사되었습니다.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed => '광고를 클립보드에 복사하는 데 실패했습니다.';
+
+ @override
+ String get notification_activityTitle => '메쉬코어 활동';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '메시지들',
+ one: '메시지',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '채널 메시지',
+ one: '채널 메시지',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: '새 노드들',
+ one: '새 노드',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return '새로운 $contactType 발견';
+ }
+
+ @override
+ String get notification_receivedNewMessage => '새로운 메시지를 받았습니다';
+
+ @override
+ String get settings_gpxExportRepeaters => 'GPX로 전송/방 관리 서버';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'GPX 파일에 위치 정보를 포함하여 반복자/룸 서버를 내보냅니다.';
+
+ @override
+ String get settings_gpxExportContacts => 'GPX 형식으로 내보내기';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'GPX 파일에 위치 정보를 포함하여 동행하는 기능을 내보냅니다.';
+
+ @override
+ String get settings_gpxExportAll => '모든 연락처를 GPX 형식으로 내보내기';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ '위치 정보가 있는 모든 연락처를 GPX 파일로 내보냅니다.';
+
+ @override
+ String get settings_gpxExportSuccess => 'GPX 파일이 성공적으로 내보내졌습니다.';
+
+ @override
+ String get settings_gpxExportNoContacts => '수출할 연락처가 없습니다.';
+
+ @override
+ String get settings_gpxExportNotAvailable => '귀하의 장치/운영체제에서는 지원되지 않습니다.';
+
+ @override
+ String get settings_gpxExportError => '데이터 내보내기 과정에서 오류가 발생했습니다.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom => '중계 장치 및 서버 위치';
+
+ @override
+ String get settings_gpxExportChat => '함께 방문할 장소';
+
+ @override
+ String get settings_gpxExportAllContacts => '모든 연락처 위치';
+
+ @override
+ String get settings_gpxExportShareText => 'meshcore-open에서 추출한 지도 데이터';
+
+ @override
+ String get settings_gpxExportShareSubject => 'meshcore-open GPX 지도 데이터 내보내기';
+
+ @override
+ String get snrIndicator_nearByRepeaters => '주변의 중계기';
+
+ @override
+ String get snrIndicator_lastSeen => '마지막으로 목격';
+
+ @override
+ String get contactsSettings_title => '연락처 설정';
+
+ @override
+ String get contactsSettings_autoAddTitle => '자동 검색';
+
+ @override
+ String get contactsSettings_otherTitle => '다른 연락 관련 설정';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle => '자동으로 사용자 추가';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ '동반자가 자동으로 발견한 사용자를 추가할 수 있도록 합니다.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle => '자동으로 중계기 추가';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ '애완동물이 발견한 무선 라디오를 자동으로 추가할 수 있도록 설정합니다.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle => '자동으로 방 서버 추가';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ '애완동물이 발견한 방 서버를 자동으로 추가할 수 있도록 설정합니다.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle => '자동으로 센서 추가';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ '애완동물이 발견한 센서를 자동으로 추가할 수 있도록 설정합니다.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => '가장 오래된 것을 덮어쓰기';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ '연락처 목록이 가득 차면, 가장 오래된 (선호하지 않은) 연락처가 대체됩니다.';
+
+ @override
+ String get discoveredContacts_Title => '연락처 찾기';
+
+ @override
+ String get discoveredContacts_noMatching => '일치하는 연락처가 없습니다.';
+
+ @override
+ String get discoveredContacts_searchHint => '발견된 연락처 검색';
+
+ @override
+ String get discoveredContacts_contactAdded => '연락처 추가';
+
+ @override
+ String get discoveredContacts_addContact => '연락처 추가';
+
+ @override
+ String get discoveredContacts_copyContact => '복사';
+
+ @override
+ String get discoveredContacts_deleteContact => '발견된 연락처 삭제';
+
+ @override
+ String get discoveredContacts_deleteContactAll => '발견된 모든 연락처 삭제';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ '정말로 모든 검색된 연락처를 삭제하시겠습니까?';
+
+ @override
+ String get chat_sendCooldown => '다시 보내기 전에 잠시 기다려 주시기 바랍니다.';
+
+ @override
+ String get appSettings_jumpToOldestUnread => '가장 오래된, 아직 읽지 않은 항목으로 이동';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ '새로운 메시지가 없는 채팅을 열 때, 최신 메시지가 아닌 첫 번째 읽지 않은 메시지로 스크롤하세요.';
+
+ @override
+ String get appSettings_languageHu => '헝가리';
+
+ @override
+ String get appSettings_languageJa => '일본어';
+
+ @override
+ String get appSettings_languageKo => '한국어';
+
+ @override
+ String get radioStats_tooltip => '라디오 및 메시 통계';
+
+ @override
+ String get radioStats_screenTitle => '라디오 통계';
+
+ @override
+ String get radioStats_notConnected => '라디오 통계를 확인하기 위해 장치에 연결합니다.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ '무선 통계 기능을 사용하려면 v8 또는 그 이상의 호환 펌웨어가 필요합니다.';
+
+ @override
+ String get radioStats_waiting => '데이터를 기다리는 중…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return '잡음 수준: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return '마지막 RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return '마지막 SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX 방송 시간 (총): $seconds 초';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX 사용 시간 (총): $seconds 초';
+ }
+
+ @override
+ String get radioStats_chartCaption => '최근 샘플의 잡음 수준 (dBm)';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return '잡음 수준: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => '라디오 통계 가져오기…';
+
+ @override
+ String get radioStats_settingsTile => '라디오 통계';
+
+ @override
+ String get radioStats_settingsSubtitle => '잡음 수준, RSSI, 신호 대 잡음비, 통신 시간';
+
+ @override
+ String get translation_title => '번역';
+
+ @override
+ String get translation_enableTitle => '번역 기능 활성화';
+
+ @override
+ String get translation_enableSubtitle => '입력 메시지를 번역하고, 미리 번역 기능을 제공합니다.';
+
+ @override
+ String get translation_composerTitle => '보내기 전에 번역';
+
+ @override
+ String get translation_composerSubtitle => '컴포저 번역 아이콘의 기본 상태를 제어합니다.';
+
+ @override
+ String get translation_targetLanguage => '목표 언어';
+
+ @override
+ String get translation_useAppLanguage => '앱 언어 사용';
+
+ @override
+ String get translation_downloadedModelLabel => '다운로드한 모델';
+
+ @override
+ String get translation_presetModelLabel => '사전에 설정된 Hugging Face 모델';
+
+ @override
+ String get translation_manualUrlLabel => '수동 모델 URL';
+
+ @override
+ String get translation_downloadModel => '모델 다운로드';
+
+ @override
+ String get translation_downloading => '다운로드 중...';
+
+ @override
+ String get translation_working => '업무 중...';
+
+ @override
+ String get translation_stop => '멈춰';
+
+ @override
+ String get translation_mergingChunks => '다운로드한 파일 조각들을 최종 파일로 병합 중...';
+
+ @override
+ String get translation_downloadedModels => '다운로드한 모델';
+
+ @override
+ String get translation_deleteModel => '모델 삭제';
+
+ @override
+ String get translation_modelDownloaded => '번역 모델이 다운로드되었습니다.';
+
+ @override
+ String get translation_downloadStopped => '다운로드 중단됨.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return '다운로드 실패: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => '먼저 모델 URL을 입력하세요.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'PIN 보기';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'PIN 숨기기';
+
+ @override
+ String get scanner_linuxPairingPinTitle => '블루투스 페어링 PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return '$deviceName의 PIN을 입력하세요 (해당하는 경우에만 입력).';
+ }
+
+ @override
+ String get translation_messageTranslation => '메시지 번역';
+
+ @override
+ String get translation_translateBeforeSending => '보내기 전에 번역';
+
+ @override
+ String get translation_composerEnabledHint => '메시지는 전송하기 전에 번역될 것입니다.';
+
+ @override
+ String get translation_composerDisabledHint => '원래 작성된 언어로 메시지를 보내세요.';
+
+ @override
+ String translation_translateTo(String language) {
+ return '$language 번역';
+ }
+
+ @override
+ String get translation_translationOptions => '번역 옵션';
+
+ @override
+ String get translation_systemLanguage => '시스템 언어';
+}
diff --git a/lib/l10n/app_localizations_nl.dart b/lib/l10n/app_localizations_nl.dart
index 50f5744..dd770e1 100644
--- a/lib/l10n/app_localizations_nl.dart
+++ b/lib/l10n/app_localizations_nl.dart
@@ -38,6 +38,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get common_delete => 'Verwijderen';
+ @override
+ String get common_deleteAll => 'Alles verwijderen';
+
@override
String get common_close => 'Sluiten';
@@ -108,6 +111,134 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Verbind via TCP';
+
+ @override
+ String get tcpHostLabel => 'IP-adres';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Poort';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Voer het eindpunt in en verbind';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Verbinding maken met $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Een IP-adres is vereist.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'De poortwaarde moet tussen 1 en 65535 liggen.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'TCP-transport wordt niet ondersteund op deze platform.';
+
+ @override
+ String get tcpErrorTimedOut => 'De TCP-verbinding is verlopen.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Verbinding met TCP mislukt: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Verbind via USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Selecteer een gedetecteerd seriële apparaat en verbind deze direct met uw MeshCore-node.';
+
+ @override
+ String get usbScreenStatus => 'Selecteer een USB-apparaat';
+
+ @override
+ String get usbScreenNote =>
+ 'USB-serieel is actief op ondersteunde Android-apparaten en desktop-platforms.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Geen USB-apparaten gevonden. Sluit er een aan en herlaad.';
+
+ @override
+ String get usbErrorPermissionDenied => 'Toegang via USB is geweigerd.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Het geselecteerde USB-apparaat is niet meer beschikbaar.';
+
+ @override
+ String get usbErrorInvalidPort => 'Selecteer een geldig USB-apparaat.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Een andere verzoek om een USB-verbinding is al in behandeling.';
+
+ @override
+ String get usbErrorNotConnected => 'Er is geen USB-apparaat aangesloten.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Kon het geselecteerde USB-apparaat niet openen.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Kon niet verbinding maken met het geselecteerde USB-apparaat.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'USB-serieel is niet ondersteund op deze platform.';
+
+ @override
+ String get usbErrorAlreadyActive => 'Een USB-verbinding is al actief.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Geen USB-apparaat is geselecteerd.';
+
+ @override
+ String get usbErrorPortClosed => 'De USB-verbinding is niet actief.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Verbinding is verbroken. Zorg ervoor dat het apparaat de juiste USB-firmware heeft.';
+
+ @override
+ String get usbFallbackDeviceName => 'Web-serieapparaat';
+
+ @override
+ String get usbStatus_notConnected => 'Selecteer een USB-apparaat';
+
+ @override
+ String get usbStatus_connecting => 'Verbinding maken met USB-apparaat...';
+
+ @override
+ String get usbStatus_searching => 'Zoeken naar USB-apparaten...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Fout bij de USB-verbinding: $error';
+ }
+
@override
String get scanner_scanning => 'Scannen naar apparaten...';
@@ -142,6 +273,23 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get scanner_scan => 'Scan';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth is uitgeschakeld';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Zorg ervoor dat Bluetooth is ingeschakeld om naar apparaten te zoeken.';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome-browser vereist';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Deze webapplicatie vereist Google Chrome of een op Chromium gebaseerde browser voor Bluetooth-ondersteuning.';
+
+ @override
+ String get scanner_enableBluetooth => 'Activeer Bluetooth';
+
@override
String get device_quickSwitch => 'Snelle overschakeling';
@@ -165,7 +313,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_nodeSettings => 'Node Instellingen';
@override
- String get settings_nodeName => 'Node Naam';
+ String get settings_nodeName => 'Nodenaam';
@override
String get settings_nodeNameNotSet => 'Niet ingesteld';
@@ -223,6 +371,13 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get settings_longitude => 'Lengtegraad';
+ @override
+ String get settings_contactSettings => 'Contactinstellingen';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Instellingen voor het toevoegen van contacten';
+
@override
String get settings_privacyMode => 'Privacy Mode';
@@ -240,6 +395,50 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Privacy modus is uitgeschakeld';
+ @override
+ String get settings_privacy => 'Privacyinstellingen';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Beheer welke informatie wordt gedeeld';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Kies welke informatie uw apparaat deelt met anderen';
+
+ @override
+ String get settings_denyAll => 'Weiger alles';
+
+ @override
+ String get settings_allowByContact => 'Toestaan op basis van contactvlaggen';
+
+ @override
+ String get settings_allowAll => 'Alles toestaan';
+
+ @override
+ String get settings_telemetryBaseMode => 'Telemetrie-basismodus';
+
+ @override
+ String get settings_telemetryLocationMode => 'Telemetrie-locatiemodus';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Telemetrie-omgevingsmodus';
+
+ @override
+ String get settings_advertLocation => 'Advertentielocatie';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Locatie opnemen in advertentie';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Telemetrie-modus bijgewerkt';
+
@override
String get settings_actions => 'Acties';
@@ -253,7 +452,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_advertisementSent => 'Advertentie verzonden';
@override
- String get settings_syncTime => 'Synchronisatie Tijd';
+ String get settings_syncTime => 'Tijd Synchroniseren';
@override
String get settings_syncTimeSubtitle =>
@@ -273,7 +472,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_rebootDevice => 'Apparaat opnieuw opstarten';
@override
- String get settings_rebootDeviceSubtitle => 'Herstart het MeshCore apparaat';
+ String get settings_rebootDeviceSubtitle => 'Herstart het MeshCore-apparaat';
@override
String get settings_rebootDeviceConfirm =>
@@ -310,6 +509,10 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_aboutDescription =>
'Een open-source Flutter client voor MeshCore LoRa mesh netwerkapparaten.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS-hoogtegegevens: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Naam';
@@ -334,15 +537,6 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get settings_presets => 'Presets';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frequentie (MHz)';
@@ -362,7 +556,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_codingRate => 'Codeertarief';
@override
- String get settings_txPower => 'TX Vermogen (dBm)';
+ String get settings_txPower => 'TX-Vermogen (dBm)';
@override
String get settings_txPowerHelper => '0 - 22';
@@ -371,10 +565,15 @@ class AppLocalizationsNl extends AppLocalizations {
String get settings_txPowerInvalid => 'Ongeldige TX-vermogen (0-22 dBm)';
@override
- String get settings_longRange => 'Lange Afstand';
+ String get settings_clientRepeat => 'Off-Grid Herhalen';
@override
- String get settings_fastSpeed => 'Hoge Snelheid';
+ String get settings_clientRepeatSubtitle =>
+ 'Laat dit apparaat de berichten van andere apparaten doorsturen.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Om een signaal buiten het netwerk te versturen, zijn frequenties van 433, 869 of 918 MHz vereist.';
@override
String settings_error(String message) {
@@ -444,6 +643,19 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Russisch';
+
+ @override
+ String get appSettings_languageUk => 'Oekraïens';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Berichttracking inschakelen';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Gedetailleerde routerings- en timing-metadata voor berichten weergeven';
+
@override
String get appSettings_notifications => 'Notificaties';
@@ -521,6 +733,49 @@ class AppLocalizationsNl extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Automatische route rotatie is uitgeschakeld';
+ @override
+ String get appSettings_maxRouteWeight => 'Maximale gewicht voor de route';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Het maximale gewicht dat een route kan bereiken door succesvolle leveringen.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'เริ่มต้น gewicht van de route';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Startgewicht voor nieuwe, ontdekte routes';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Toename in het gewicht van het succes';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Gewicht wordt toegevoegd aan een route na een succesvolle levering.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Vermindering van het gewicht van fouten';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Gewicht verwijderd van een pad na een mislukte levering';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Aantal pogingen om berichten te versturen';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Aantal pogingen om een bericht opnieuw te versturen voordat het als mislukt wordt gemarkeerd';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batterij';
@@ -591,19 +846,28 @@ class AppLocalizationsNl extends AppLocalizations {
String get appSettings_allTime => 'Altijd';
@override
- String get appSettings_lastHour => 'Laat uur';
+ String get appSettings_lastHour => 'Afgelopen uur';
@override
- String get appSettings_last6Hours => 'laatste 6 uur';
+ String get appSettings_last6Hours => 'Afgelopen 6 uur';
@override
- String get appSettings_last24Hours => 'De laatste 24 uur';
+ String get appSettings_last24Hours => 'Afgelopen 24 uur';
@override
- String get appSettings_lastWeek => 'Laatste week';
+ String get appSettings_lastWeek => 'Afgelopen week';
@override
- String get appSettings_offlineMapCache => 'Offline Kaarten Cache';
+ String get appSettings_offlineMapCache => 'Offline Kaartcache';
+
+ @override
+ String get appSettings_unitsTitle => 'Eenheden';
+
+ @override
+ String get appSettings_unitsMetric => 'Metrisch (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperiaal (ft / mi)';
@override
String get appSettings_noAreaSelected => 'Geen gebied geselecteerd';
@@ -642,7 +906,35 @@ class AppLocalizationsNl extends AppLocalizations {
'Contacten verschijnen wanneer apparaten zich aanbieden.';
@override
- String get contacts_searchContacts => 'Zoek contacten...';
+ String get contacts_unread => 'Ongelezen';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Zoek contacten...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Zoek contacten...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Zoek $number$str favorieten...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Zoek $number$str gebruikers...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Zoek $number$str Repeaters...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Zoek $number$str Room servers...';
+ }
@override
String get contacts_noUnreadContacts => 'Geen ongelezen contacten';
@@ -690,6 +982,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'De groepnaam is verplicht.';
+ @override
+ String get contacts_groupNameReserved => 'Deze groepsnaam is gereserveerd';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'De groep \"$name\" bestaat al.';
@@ -729,6 +1024,40 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Laast gezien $days dagen geleden';
}
+ @override
+ String get contact_info => 'Contactinformatie';
+
+ @override
+ String get contact_settings => 'Contactinstellingen';
+
+ @override
+ String get contact_telemetry => 'Telemetrie';
+
+ @override
+ String get contact_lastSeen => 'Laatst gezien';
+
+ @override
+ String get contact_clearChat => 'Chat leegmaken';
+
+ @override
+ String get contact_teleBase => 'Telemetrie_basis';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Sta delen van batterij niveau en basis telemetrie toe';
+
+ @override
+ String get contact_teleLoc => 'Telemetrielocatie';
+
+ @override
+ String get contact_teleLocSubtitle => 'Locatiegegevens delen toestaan';
+
+ @override
+ String get contact_teleEnv => 'Telemetrieomgeving';
+
+ @override
+ String get contact_teleEnvSubtitle => 'Delen van omgevingsensordata toestaan';
+
@override
String get channels_title => 'Kanaal';
@@ -767,6 +1096,12 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get channels_editChannel => 'Kanaal bewerken';
+ @override
+ String get channels_muteChannel => 'Kanaal dempen';
+
+ @override
+ String get channels_unmuteChannel => 'Kanaal dempen opheffen';
+
@override
String get channels_deleteChannel => 'Kanaal verwijderen';
@@ -775,6 +1110,11 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Verwijderen \"$name\"? Dit kan niet worden teruggedraaid.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Kan kanaal $name niet verwijderen';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Kanaal \"$name\" verwijderd';
@@ -845,32 +1185,32 @@ class AppLocalizationsNl extends AppLocalizations {
String get channels_sortUnread => 'Ongelezen';
@override
- String get channels_createPrivateChannel => 'Maak een Privé Kanaal';
+ String get channels_createPrivateChannel => 'PrivéKanaal Aanmaken';
@override
String get channels_createPrivateChannelDesc =>
'Beveiligd met een geheime sleutel.';
@override
- String get channels_joinPrivateChannel => 'Sluit een Privé Kanaal aan';
+ String get channels_joinPrivateChannel => 'PrivéKanaal Toetreden';
@override
String get channels_joinPrivateChannelDesc =>
- 'Handmatig een geheime sleutel invoeren.';
+ 'Voer handmatig een geheime sleutel in.';
@override
- String get channels_joinPublicChannel => 'Sluit het Open Kanaal';
+ String get channels_joinPublicChannel => 'Publiek Kanaal Toetreden';
@override
String get channels_joinPublicChannelDesc =>
- 'Iedereen kan dit kanaal aanmelden.';
+ 'Iedereen kan toetreden tot dit kanaal.';
@override
- String get channels_joinHashtagChannel => 'Sluit een Hashtag Kanaal';
+ String get channels_joinHashtagChannel => 'Hashtag-kanaal Aanmaken';
@override
String get channels_joinHashtagChannelDesc =>
- 'Iedereen kan lid worden van hashtag-kanalen.';
+ 'Iedereen kan toetreden tot hashtag-kanalen.';
@override
String get channels_scanQrCode => 'Scan een QR-code';
@@ -887,6 +1227,14 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get chat_noMessages => 'Nog geen berichten.';
+ @override
+ String get chat_sendMessage => 'Verzend bericht';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Verstuur een bericht naar $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Een bericht sturen om te beginnen';
@@ -906,11 +1254,6 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get chat_location => 'Locatie';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Verstuur een bericht naar $contactName';
- }
-
@override
String get chat_typeMessage => 'Type een bericht...';
@@ -1062,6 +1405,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get chat_pathManagement => 'Beheer van Paden';
+ @override
+ String get chat_ShowAllPaths => 'Toon alle paden';
+
@override
String get chat_routingMode => 'Routeerwijze';
@@ -1199,9 +1545,33 @@ class AppLocalizationsNl extends AppLocalizations {
return 'Nieuw: $count';
}
+ @override
+ String get chat_openLink => 'Link openen?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Wilt u deze link in uw browser openen?';
+
+ @override
+ String get chat_open => 'Openen';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Kan link niet openen: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Ongeldig linkformaat';
+
@override
String get map_title => 'Node Map';
+ @override
+ String get map_lineOfSight => 'Zichtlijn';
+
+ @override
+ String get map_losScreenTitle => 'Zichtlijn';
+
@override
String get map_noNodesWithLocation => 'Geen nodes met locatiegegevens';
@@ -1259,6 +1629,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Deel marker hier';
+ @override
+ String get map_setAsMyLocation => 'Stel dit in als mijn locatie';
+
@override
String get map_pinLabel => 'Label vastzetten';
@@ -1304,6 +1677,9 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get map_otherNodes => 'Andere Nodes';
+ @override
+ String get map_showOverlaps => 'Herhalingssleutel overlapt';
+
@override
String get map_keyPrefix => 'Prefix sleutel';
@@ -1319,6 +1695,16 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Toon gedeelde markeringen';
+ @override
+ String get map_showGuessedLocations =>
+ 'Toon de voorspelde locaties van de knopen';
+
+ @override
+ String get map_showDiscoveryContacts => 'Ontdek contacten weergeven';
+
+ @override
+ String get map_guessedLocation => 'Geroerde locatie';
+
@override
String get map_lastSeenTime => 'Laatste Bekeken Tijd';
@@ -1326,11 +1712,27 @@ class AppLocalizationsNl extends AppLocalizations {
String get map_sharedPin => 'Gedeelde pin';
@override
- String get map_joinRoom => 'Sluit Kamer';
+ String get map_joinRoom => 'Kamer Toetreden';
@override
String get map_manageRepeater => 'Beheer Repeater';
+ @override
+ String get map_tapToAdd =>
+ 'Tik op knooppunten om ze toe te voegen aan het pad';
+
+ @override
+ String get map_runTrace => 'Padeshulp traceren';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Terugkeren op hetzelfde pad.';
+
+ @override
+ String get map_removeLast => 'Verwijder Laatste';
+
+ @override
+ String get map_pathTraceCancelled => 'Pad traceren geannuleerd';
+
@override
String get mapCache_title => 'Offline Kaarten Cache';
@@ -1602,7 +2004,16 @@ class AppLocalizationsNl extends AppLocalizations {
String get room_management => 'Beheer Server Kamer';
@override
- String get repeater_managementTools => 'Beheerinstrumenten';
+ String get repeater_guest => 'Informatie over herhalingsapparatuur';
+
+ @override
+ String get room_guest => 'Informatie over de server';
+
+ @override
+ String get repeater_managementTools => 'Beheerfuncties';
+
+ @override
+ String get repeater_guestTools => 'Gastenfuncties';
@override
String get repeater_status => 'Status';
@@ -1625,10 +2036,10 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_cliSubtitle => 'Verzend commando\'s naar de repeater';
@override
- String get repeater_neighbours => 'Buren';
+ String get repeater_neighbors => 'Buren';
@override
- String get repeater_neighboursSubtitle => 'Bekijk nul hops buren.';
+ String get repeater_neighborsSubtitle => 'Bekijk nul-hopsburen.';
@override
String get repeater_settings => 'Instellingen';
@@ -1636,6 +2047,14 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Configureer repeaterparameters';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Na het inloggen, klok synchroniseren';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatisch een \"klok synchroniseren\" bericht versturen na een succesvolle inlog.';
+
@override
String get repeater_statusTitle => 'Status repeater';
@@ -1694,10 +2113,10 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_noiseFloor => 'Ruisvloer';
@override
- String get repeater_txAirtime => 'TX Airtime';
+ String get repeater_txAirtime => 'TX-zendtijd';
@override
- String get repeater_rxAirtime => 'RX Airtime';
+ String get repeater_rxAirtime => 'RX-zendtijd';
@override
String get repeater_packetStatistics => 'Pakketstatistieken';
@@ -1742,7 +2161,7 @@ class AppLocalizationsNl extends AppLocalizations {
}
@override
- String get repeater_settingsTitle => 'Repeater Instellingen';
+ String get repeater_settingsTitle => 'Repeaterinstellingen';
@override
String get repeater_basicSettings => 'Basisinstellingen';
@@ -1751,19 +2170,19 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_repeaterName => 'Repeaternaam';
@override
- String get repeater_repeaterNameHelper => 'Weergave naam voor deze repeater';
+ String get repeater_repeaterNameHelper => 'Weergavenaam voor deze repeater';
@override
String get repeater_adminPassword => 'Admin wachtwoord';
@override
- String get repeater_adminPasswordHelper => 'Volledige toegangspaswoord';
+ String get repeater_adminPasswordHelper => 'Wachtwoord administratortoegang';
@override
- String get repeater_guestPassword => 'Wachtwoord Gast';
+ String get repeater_guestPassword => 'Gast wachtwoord';
@override
- String get repeater_guestPasswordHelper => 'Leesbeheer wachtwoord';
+ String get repeater_guestPasswordHelper => 'Wachtwoord gasttoegen';
@override
String get repeater_radioSettings => 'Radio Instellingen';
@@ -1790,7 +2209,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_codingRate => 'Codeertarief';
@override
- String get repeater_locationSettings => 'Locatie Instellingen';
+ String get repeater_locationSettings => 'Locatie-instellingen';
@override
String get repeater_latitude => 'Breedtegraad';
@@ -1822,14 +2241,14 @@ class AppLocalizationsNl extends AppLocalizations {
'Toegestane leesbeheer toegang voor gasten.';
@override
- String get repeater_privacyMode => 'Privacy Modus';
+ String get repeater_privacyMode => 'Privacymodus';
@override
String get repeater_privacyModeSubtitle =>
'Naam/locatie verbergen in advertenties';
@override
- String get repeater_advertisementSettings => 'Advertentie Instellingen';
+ String get repeater_advertisementSettings => 'Advertentie-instellingen';
@override
String get repeater_localAdvertInterval => 'Lokale Advertentie Interval';
@@ -1934,7 +2353,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_refreshGuestAccess => 'Toegang Gast Vernieuwen';
@override
- String get repeater_refreshPrivacyMode => 'Privacy Mode vernieuwen';
+ String get repeater_refreshPrivacyMode => 'Privacymode vernieuwen';
@override
String get repeater_refreshAdvertisementSettings =>
@@ -1960,10 +2379,10 @@ class AppLocalizationsNl extends AppLocalizations {
String get repeater_commandHelp => 'Help';
@override
- String get repeater_clearHistory => 'Verwijder Geschiedenis';
+ String get repeater_clearHistory => 'Geschiedenis Verwijderen';
@override
- String get repeater_noCommandsSent => 'Geen commando\'s verzonden nog.';
+ String get repeater_noCommandsSent => 'Nog geen commando\'s verzonden.';
@override
String get repeater_typeCommandOrUseQuick =>
@@ -1990,28 +2409,34 @@ class AppLocalizationsNl extends AppLocalizations {
}
@override
- String get repeater_cliQuickGetName => 'Haal Naam op';
+ String get repeater_cliQuickGetName => 'Naam opvragen';
@override
- String get repeater_cliQuickGetRadio => 'Radio ontvangen';
+ String get repeater_cliQuickGetRadio => 'Radio-instellingen opvragen';
@override
- String get repeater_cliQuickGetTx => 'Krijg TX';
+ String get repeater_cliQuickGetTx => 'TX opvragen';
@override
- String get repeater_cliQuickNeighbors => 'Buren';
+ String get repeater_cliQuickNeighbors => 'Buren opvragen';
@override
- String get repeater_cliQuickVersion => 'Versie';
+ String get repeater_cliQuickVersion => 'Versie opvragen';
@override
- String get repeater_cliQuickAdvertise => 'Advertenties';
+ String get repeater_cliQuickAdvertise => 'Advertenties opvragen';
@override
- String get repeater_cliQuickClock => 'Tijd';
+ String get repeater_cliQuickClock => 'Tijd opvragen';
@override
- String get repeater_cliHelpAdvert => 'Verstuurt een advertentiepakket';
+ String get repeater_cliQuickClockSync => 'Kloksynchronisatie';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Ontdek Buren';
+
+ @override
+ String get repeater_cliHelpAdvert => 'Advertentie uitzenden';
@override
String get repeater_cliHelpReboot =>
@@ -2283,7 +2708,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get telemetry_voltageLabel => 'Spanning';
@override
- String get telemetry_mcuTemperatureLabel => 'MCU Temperatuur';
+ String get telemetry_mcuTemperatureLabel => 'MCU-temperatuur';
@override
String get telemetry_temperatureLabel => 'Temperatuur';
@@ -2324,7 +2749,7 @@ class AppLocalizationsNl extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Herhalingen Buren';
+ String get neighbors_repeatersNeighbors => 'Repeatbburen';
@override
String get neighbors_noData => 'Geen gegevens van buren beschikbaar.';
@@ -2556,32 +2981,32 @@ class AppLocalizationsNl extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Regeneer Geheimwoord';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regeneere de geheime sleutel voor \"$name\"? Alle leden moeten de nieuwe QR-code scannen om verder te communiceren.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Regeneer';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Geheim hersteld voor \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Bijwerken Geheime';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Geheim gewijzigd voor \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Scan de nieuwe QR-code om het geheim voor \"$name\" bij te werken';
}
@override
@@ -2623,7 +3048,7 @@ class AppLocalizationsNl extends AppLocalizations {
String get listFilter_latestMessages => 'Recente berichten';
@override
- String get listFilter_heardRecently => 'Hoor je onlangs';
+ String get listFilter_heardRecently => 'Recent gezien';
@override
String get listFilter_az => 'A-Z';
@@ -2634,6 +3059,15 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get listFilter_all => 'Alles';
+ @override
+ String get listFilter_favorites => 'Favorieten';
+
+ @override
+ String get listFilter_addToFavorites => 'Toevoegen aan favorieten';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Verwijderen uit favorieten';
+
@override
String get listFilter_users => 'Gebruikers';
@@ -2648,4 +3082,608 @@ class AppLocalizationsNl extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nieuwe groep';
+
+ @override
+ String get pathTrace_you => 'Jij';
+
+ @override
+ String get pathTrace_failed => 'Padtrace mislukt.';
+
+ @override
+ String get pathTrace_notAvailable => 'Padtrace niet beschikbaar.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Path Trace vernieuwen.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Een of meer van de hops ontbreken een locatie!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Weg wissen';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Selecteer begin- en eindknooppunten voor LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Zichtlijncontrole mislukt: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Wis alle punten';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Voer LOS uit om het hoogteprofiel te bekijken';
+
+ @override
+ String get losMenuTitle => 'LOS-menu';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Tik op knooppunten of druk lang op de kaart voor aangepaste punten';
+
+ @override
+ String get losShowDisplayNodes => 'Toon weergaveknooppunten';
+
+ @override
+ String get losCustomPoints => 'Aangepaste punten';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Aangepast $index';
+ }
+
+ @override
+ String get losPointA => 'Punt A';
+
+ @override
+ String get losPointB => 'Punt B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenne A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenne B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Voer LOS uit';
+
+ @override
+ String get losNoElevationData => 'Geen hoogtegegevens';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, vrije LOS, min. vrije ruimte $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, geblokkeerd door $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: controleren...';
+
+ @override
+ String get losStatusNoData => 'LOS: geen gegevens';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total gewist, $blocked geblokkeerd, $unknown onbekend';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Hoogtegegevens niet beschikbaar voor een of meer monsters.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Ongeldige punten/hoogtegegevens voor LOS-berekening.';
+
+ @override
+ String get losRenameCustomPoint => 'Hernoem aangepast punt';
+
+ @override
+ String get losPointName => 'Puntnaam';
+
+ @override
+ String get losShowPanelTooltip => 'Toon LOS-paneel';
+
+ @override
+ String get losHidePanelTooltip => 'LOS-paneel verbergen';
+
+ @override
+ String get losElevationAttribution =>
+ 'Hoogtegegevens: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Radiohorizon';
+
+ @override
+ String get losLegendLosBeam => 'Zichtlijn';
+
+ @override
+ String get losLegendTerrain => 'Terrein';
+
+ @override
+ String get losFrequencyLabel => 'Frequentie';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Bekijk details van de berekening';
+
+ @override
+ String get losFrequencyDialogTitle => 'Berekening van de radiohorizon';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Beginnend met k=$baselineK bij $baselineFreq MHz, wordt bij de berekening de k-factor aangepast voor de huidige $frequencyMHz MHz-band, die de gebogen radiohorizonkap definieert.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Pad Traceren';
+
+ @override
+ String get contacts_ping => 'Pingen';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Pad traceren naar repeater';
+
+ @override
+ String get contacts_repeaterPing => 'Ping repeater';
+
+ @override
+ String get contacts_roomPathTrace => 'Padtrace naar room server';
+
+ @override
+ String get contacts_roomPing => 'Ping kamer server';
+
+ @override
+ String get contacts_chatTraceRoute => 'Route traceren';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Trace route to $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Knipbord is leeg.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Ongeldige contactgegevens';
+
+ @override
+ String get contacts_contactImported => 'Contact is geïmporteerd.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Contact kon niet geïmporteerd worden.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Zero Hop Reclame';
+
+ @override
+ String get contacts_floodAdvert => 'Overstromingsadvertentie';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Advert naar klembord kopiëren';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Contact uit klembord toevoegen';
+
+ @override
+ String get contacts_ShareContact => 'Contact naar Klembord kopiëren';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Contact delen via advertentie';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Contact verzonden via advertentie';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Mislukt om contact te verzenden';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Reclame gekopieerd naar Klembord.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopiëren van advertentie naar Clipboard is mislukt.';
+
+ @override
+ String get notification_activityTitle => 'MeshCore Activiteit';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'berichten',
+ one: 'bericht',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'kanaalberichten',
+ one: 'kanaalbericht',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nieuwe knooppunten',
+ one: 'nieuw knooppunt',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nieuw $contactType ontdekt';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nieuw bericht ontvangen';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exporteer repeaters / roomserver naar GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exporteert repeaters / roomserver met een locatie naar GPX-bestand.';
+
+ @override
+ String get settings_gpxExportContacts => 'Companions exporteren naar GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exporteert metgezellen met een locatie naar een GPX-bestand.';
+
+ @override
+ String get settings_gpxExportAll => 'Alle contacten exporteren naar GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exporteert alle contacten met een locatie naar een GPX-bestand.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Succesvol GPX-bestand geëxporteerd.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Geen contacten om te exporteren.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Niet ondersteund op uw apparaat/besturingssysteem';
+
+ @override
+ String get settings_gpxExportError => 'Er was een fout bij het exporteren.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Repeater- en kamer servers locaties';
+
+ @override
+ String get settings_gpxExportChat => 'Locaties van metgezellen';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Alle contactlocaties';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Kaartgegevens geëxporteerd uit meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open GPX kaartgegevens exporteren';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Nabije herhalingseenheden';
+
+ @override
+ String get snrIndicator_lastSeen => 'Laatst gezien';
+
+ @override
+ String get contactsSettings_title => 'Instellingen voor contacten';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatische detectie';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Andere instellingen voor contactgerelateerde zaken';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Gebruikers automatisch toevoegen';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Sta toe dat de companion automatisch ontdekte gebruikers toevoegt';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Automatisch herhalingstoestellen toevoegen';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Sta toe dat de companion automatisch ontdekte repeaters toevoegt';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Automatisch kamerservers toevoegen';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Sta toe dat de companion automatisch ontdekte kamer servers toevoegt.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Automatisch sensoren toevoegen';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Sta toe dat de companion automatisch ontdekte sensoren toevoegt';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Overschrijf Oudste';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Wanneer de contactenlijst vol is, wordt de oudste niet-favoriete contactpersoon vervangen.';
+
+ @override
+ String get discoveredContacts_Title => 'Ontdekte contacten';
+
+ @override
+ String get discoveredContacts_noMatching => 'Geen overeenkomende contacten';
+
+ @override
+ String get discoveredContacts_searchHint => 'Ontdekte contacten zoeken';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contact toegevoegd';
+
+ @override
+ String get discoveredContacts_addContact => 'Contact toevoegen';
+
+ @override
+ String get discoveredContacts_copyContact => 'Kopieer contact naar klembord';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Contact verwijderen';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Verwijder alle ontdekte contacten';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Weet u zeker dat u alle ontdekte contacten wilt verwijderen?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Gelieve even te wachten voordat u opnieuw verzendt.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Ga naar het oudste ongelezen bericht';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Bij het openen van een chat met ongelezen berichten, scroll dan naar het eerste ongelezen bericht, in plaats van naar het meest recente.';
+
+ @override
+ String get appSettings_languageHu => 'Hongaars';
+
+ @override
+ String get appSettings_languageJa => 'Japanisch';
+
+ @override
+ String get appSettings_languageKo => 'Koreaans';
+
+ @override
+ String get radioStats_tooltip => 'Statistieken voor radio en mesh-netwerken';
+
+ @override
+ String get radioStats_screenTitle => 'Statistieken over radio';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Verbind met een apparaat om radio-statistieken te bekijken.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Om de statistieken via radio te kunnen gebruiken, is firmware versie 8 of een nieuwere vereist.';
+
+ @override
+ String get radioStats_waiting => 'Wacht op gegevens…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Ruisfrequentie: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Laatste RSSI-waarde: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Laatste SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX-tijd (totaal): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Tijd besteed met RX (totaal): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Ruisfrequentie (dBm) over recente metingen.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Ruisfrequentie: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Radio-statistieken ophalen…';
+
+ @override
+ String get radioStats_settingsTile => 'Statistieken over radio';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Ruimtelijke ruis, RSSI, SNR en beschikbare tijd';
+
+ @override
+ String get translation_title => 'Vertaling';
+
+ @override
+ String get translation_enableTitle => 'Activeer vertaling';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Vertaal inkomende berichten en maak het mogelijk om berichten vooraf te vertalen.';
+
+ @override
+ String get translation_composerTitle => 'Vertaal voor verzending';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Stelt de standaardstatus van het pictogram voor de vertaling van de componist in.';
+
+ @override
+ String get translation_targetLanguage => 'Doeltaal';
+
+ @override
+ String get translation_useAppLanguage => 'Gebruik de taal van de app';
+
+ @override
+ String get translation_downloadedModelLabel => 'Gedownloade model';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Voorgeprogrammeerd Hugging Face-model';
+
+ @override
+ String get translation_manualUrlLabel => 'URL van de handleiding';
+
+ @override
+ String get translation_downloadModel => 'Download het model';
+
+ @override
+ String get translation_downloading => 'Downloaden...';
+
+ @override
+ String get translation_working => 'Werken...';
+
+ @override
+ String get translation_stop => 'Stoppen';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Het samenvoegen van de gedownloade stukken tot één eindbestand...';
+
+ @override
+ String get translation_downloadedModels => 'Gedownloade modellen';
+
+ @override
+ String get translation_deleteModel => 'Model verwijderen';
+
+ @override
+ String get translation_modelDownloaded => 'Vertalingmodel gedownload.';
+
+ @override
+ String get translation_downloadStopped => 'Download is afgebroken.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Download mislukt: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Voer eerst een URL van een model in.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Toon PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'PIN verbergen';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth‑koppelings‑PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Voer PIN in voor $deviceName (laat leeg als er geen is).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Berichtvertaling';
+
+ @override
+ String get translation_translateBeforeSending => 'Vertaal voor verzending';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'De berichten worden vertaald voordat ze verzonden worden.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Stuur berichten in de oorspronkelijke, getypte taal.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Vertalen naar $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Opties voor vertaling';
+
+ @override
+ String get translation_systemLanguage => 'Taal van het systeem';
}
diff --git a/lib/l10n/app_localizations_pl.dart b/lib/l10n/app_localizations_pl.dart
index 378858a..357dd7e 100644
--- a/lib/l10n/app_localizations_pl.dart
+++ b/lib/l10n/app_localizations_pl.dart
@@ -39,7 +39,10 @@ class AppLocalizationsPl extends AppLocalizations {
String get common_delete => 'Usuń';
@override
- String get common_close => 'Zamknąć';
+ String get common_deleteAll => 'Usuń wszystko';
+
+ @override
+ String get common_close => 'Zamknij';
@override
String get common_edit => 'Edytuj';
@@ -72,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';
@@ -84,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...';
@@ -108,6 +111,138 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Połącz się za pomocą protokołu TCP';
+
+ @override
+ String get tcpHostLabel => 'Adres IP';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Wprowadź adres URL i połącz';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Połączenie z $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Wymagana jest adresa IP.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'Numer portu musi mieścić się w zakresie od 1 do 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Transport TCP nie jest obsługiwany na tej platformie.';
+
+ @override
+ String get tcpErrorTimedOut =>
+ 'Połączenie TCP zakończyło się bez powodzenia.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Błąd połączenia TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Połącz przez USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ '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';
+
+ @override
+ String get usbScreenNote =>
+ 'Port szeregowy USB jest aktywny na urządzeniach z systemem Android i platformach stacjonarnych, które go obsługują.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Nie znaleziono żadnych urządzeń USB. Podłącz jedno i zaktualizuj.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Zostało odrzucone żądanie dostępu przez USB.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Wybór urządzenia USB już nie jest dostępny.';
+
+ @override
+ String get usbErrorInvalidPort => 'Wybierz prawidłowe urządzenie USB.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Kolejne żądanie połączenia przez USB jest już w trakcie realizacji.';
+
+ @override
+ String get usbErrorNotConnected => 'Brak podłączonego urządzenia USB.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Nie udało się otworzyć wybranego urządzenia USB.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Nie udało się połączyć z wybranym urządzeniem USB.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Port szeregowy USB nie jest obsługiwany na tym urządzeniu.';
+
+ @override
+ String get usbErrorAlreadyActive => 'Połączenie USB jest już aktywne.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Nie został wybrany żaden urządzenie USB.';
+
+ @override
+ String get usbErrorPortClosed => 'Połączenie USB nie jest aktywne.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Połączenie nie zostało nawiązane. Upewnij się, że urządzenie posiada oprogramowanie \"USB Companion\".';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Urządzenie do komunikacji przez sieć (seria)';
+
+ @override
+ String get usbStatus_notConnected => 'Wybierz urządzenie USB';
+
+ @override
+ String get usbStatus_connecting => 'Połączenie z urządzeniem USB...';
+
+ @override
+ String get usbStatus_searching => 'Wyszukiwanie urządzeń USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Błąd połączenia USB: $error';
+ }
+
@override
String get scanner_scanning => 'Skanowanie urządzeń...';
@@ -118,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) {
@@ -143,6 +278,23 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get scanner_scan => 'Przeskanuj';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth jest wyłączony';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Prosimy włączyć Bluetooth, aby przeskanować urządzenia.';
+
+ @override
+ String get scanner_chromeRequired => 'Wymagana przeglądarka Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Ta aplikacja internetowa wymaga przeglądarki Google Chrome lub opartej na Chromium do obsługi Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Włącz Bluetooth';
+
@override
String get device_quickSwitch => 'Szybka zmiana';
@@ -175,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';
@@ -226,15 +378,22 @@ class AppLocalizationsPl extends AppLocalizations {
String get settings_longitude => 'Długość';
@override
- String get settings_privacyMode => 'Tryb Prywatny';
+ 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 prywatności';
@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';
@@ -242,28 +401,73 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Tryb prywatności wyłączony';
+ @override
+ String get settings_privacy => 'Ustawienia prywatności';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Kontroluj jakie informacje są udostępniane.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Wybierz jakie informacje urządzenie udostępni innym.';
+
+ @override
+ String get settings_denyAll => 'Odmów wszystkim';
+
+ @override
+ String get settings_allowByContact => 'Zezwalaj według flag kontaktowych';
+
+ @override
+ String get settings_allowAll => 'Zezwalaj na wszystko';
+
+ @override
+ String get settings_telemetryBaseMode => 'Tryb podstawowy telemetrii';
+
+ @override
+ String get settings_telemetryLocationMode => 'Tryb położenia telemetrycznego';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Tryb środowiska telemetrycznego';
+
+ @override
+ String get settings_advertLocation => 'Lokalizacja reklamowa';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Uwzględnij lokalizację w ogłoszeniu';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Wielokrotne ACK: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated =>
+ 'Tryb telemetryczny zaktualizowany';
+
@override
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';
+ String get settings_sendAdvertisementSubtitle => 'Nadaj obecność teraz';
@override
- String get settings_advertisementSent => 'Reklama wysłana';
+ 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';
@@ -286,20 +490,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) {
@@ -311,10 +515,14 @@ 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_infoName => 'Imię';
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Dane wysokościowe LOS: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => 'Nazwa';
@override
String get settings_infoId => 'ID';
@@ -335,16 +543,7 @@ class AppLocalizationsPl extends AppLocalizations {
String get settings_infoChannelCount => 'Liczba kanałów';
@override
- String get settings_presets => 'Preset';
-
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
+ String get settings_presets => 'Presety';
@override
String get settings_frequency => 'Częstotliwość (MHz)';
@@ -360,13 +559,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';
@@ -375,10 +574,15 @@ class AppLocalizationsPl extends AppLocalizations {
String get settings_txPowerInvalid => 'Nieprawidłowa moc TX (0-22 dBm)';
@override
- String get settings_longRange => 'Długi zasięg';
+ String get settings_clientRepeat => 'Powtórzenie: Niezależne od sieci';
@override
- String get settings_fastSpeed => 'Szybka prędkość';
+ String get settings_clientRepeatSubtitle =>
+ 'Pozwól temu urządzeniu powtarzać pakiety danych dla innych urządzeń.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Powtórka poza siecią wymaga częstotliwości 433, 869 lub 918 MHz.';
@override
String settings_error(String message) {
@@ -448,6 +652,19 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Rosyjski';
+
+ @override
+ String get appSettings_languageUk => 'Ukraińska';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Włącz śledzenie wiadomości';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Pokaż szczegółowe metadane trasowania i czasu dla wiadomości';
+
@override
String get appSettings_notifications => 'Powiadomienia';
@@ -456,7 +673,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 =>
@@ -486,18 +703,18 @@ 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';
@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 =>
@@ -505,14 +722,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 =>
@@ -526,6 +743,49 @@ class AppLocalizationsPl extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Automatyczne obracanie tras wyłączone';
+ @override
+ String get appSettings_maxRouteWeight =>
+ 'Maksymalny dopuszczalny ciężar pojazdu';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Maksymalna waga, jaką ścieżka może zgromadzić dzięki udanym dostawom.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Początkowa waga trasy';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Początkowa waga dla nowych, odkrytych ścieżek';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement => 'Wzrost wagi sukcesu';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Waga dodana do ścieżki po pomyślnym dostarczeniu';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Zmniejszenie wagi kary';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Waga usunięta z trasy po nieudanej dostawie';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Maksymalna liczba prób wysłania wiadomości';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Liczba prób ponownego wysłania wiadomości przed oznaczaniem jej jako nieudanej';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Bateria';
@@ -534,7 +794,7 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String appSettings_batteryChemistryPerDevice(String deviceName) {
- return 'Ustawione na urządzenie ($deviceName)';
+ return 'Ustaw dla urządzenia ($deviceName)';
}
@override
@@ -554,11 +814,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ż przekaźniki';
@override
String get appSettings_showRepeatersSubtitle =>
- 'Wyświetl węzły powtarzające się na mapie';
+ 'Wyświetl węzły przekaźników na mapie';
@override
String get appSettings_showChatNodes => 'Pokaż Węzły Rozmowy';
@@ -586,13 +846,13 @@ 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:';
@override
- String get appSettings_allTime => 'Wszystko czasowo';
+ String get appSettings_allTime => 'Cały czas';
@override
String get appSettings_lastHour => 'Ostatnia godzina';
@@ -604,13 +864,22 @@ 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_noAreaSelected => 'Nie zaznaczono żadnej powierzchni.';
+ String get appSettings_unitsTitle => 'Jednostki';
+
+ @override
+ String get appSettings_unitsMetric => 'Metryczne (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperialne (ft / mi)';
+
+ @override
+ String get appSettings_noAreaSelected => 'Nie wybrano żadnego obszaru.';
@override
String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
@@ -625,15 +894,15 @@ 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 =>
- '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';
@@ -643,10 +912,46 @@ 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_searchContacts => 'Wyszukaj kontakty...';
+ String get contacts_unread => 'Nieprzeczytane';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Wyszukaj kontakty...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ String _temp0 = intl.Intl.pluralLogic(
+ number,
+ locale: localeName,
+ other: 'kontaktu',
+ many: 'kontaktów',
+ few: 'kontakty',
+ one: 'kontakt',
+ );
+ return 'Wyszukaj $number$str $_temp0...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Wyszukaj $number$str ulubione...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Wyszukaj $number$str Użytkowników...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Wyszukaj $number$str przekaźników...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Wyszukaj $number$str serwerów Room...';
+ }
@override
String get contacts_noUnreadContacts => 'Brak nieprzeczytanych kontaktów';
@@ -660,11 +965,11 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String contacts_removeConfirm(String contactName) {
- return 'Usuń $contactName z kontaktów?';
+ return 'Usunąć $contactName z kontaktów?';
}
@override
- String get contacts_manageRepeater => 'Zarządzaj Powtórzami';
+ String get contacts_manageRepeater => 'Zarządzaj przekaźnikiem';
@override
String get contacts_manageRoom => 'Zarządzaj Serwerem Pokoju';
@@ -695,6 +1000,9 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Nazwa grupy jest wymagana';
+ @override
+ String get contacts_groupNameReserved => 'Ta nazwa grupy jest zastrzeżona';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Grupa \"$name\" już istnieje';
@@ -711,29 +1019,65 @@ class AppLocalizationsPl extends AppLocalizations {
String get contacts_noMembers => 'Brak członków';
@override
- String get contacts_lastSeenNow => 'Ostatnie połączenie';
+ String get contacts_lastSeenNow => 'niedawno';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Ostatnie połączenie $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 'Ostatnie połączenie $hours godzin 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 'Ostatnie połączenie $days dni temu';
+ return '~ $days dni';
}
+ @override
+ String get contact_info => 'Informacje kontaktowe';
+
+ @override
+ String get contact_settings => 'Ustawienia kontaktowe';
+
+ @override
+ String get contact_telemetry => 'Telemetryka';
+
+ @override
+ String get contact_lastSeen => 'Ostatnio widziany';
+
+ @override
+ String get contact_clearChat => 'Wyczyść czat';
+
+ @override
+ String get contact_teleBase => 'Baza telemetryczna';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Pozwól na udostępnianie poziomu naładowania baterii i podstawowych danych telemetrycznych';
+
+ @override
+ String get contact_teleLoc => 'Lokalizacja telemetryczna';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Zezwalaj na udostępnianie danych lokalizacji';
+
+ @override
+ String get contact_teleEnv => 'Środowisko telemetryczne';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Zezwalaj na udostępnianie danych czujników środowiskowych';
+
@override
String get channels_title => 'Kanały';
@@ -755,13 +1099,13 @@ 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';
@override
- String get channels_private => 'Prywatne';
+ String get channels_private => 'Prywatny';
@override
String get channels_publicChannel => 'Kanał publiczny';
@@ -772,6 +1116,12 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get channels_editChannel => 'Edytuj kanał';
+ @override
+ String get channels_muteChannel => 'Wycisz kanał';
+
+ @override
+ String get channels_unmuteChannel => 'Wyłącz wyciszenie kanału';
+
@override
String get channels_deleteChannel => 'Usuń kanał';
@@ -780,6 +1130,11 @@ class AppLocalizationsPl extends AppLocalizations {
return 'Usuń \"$name\"? Nie można tego cofnąć.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Nie udało się usunąć kanału \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Kanał \"$name\" usunięto';
@@ -798,7 +1153,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)';
@@ -810,7 +1165,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) {
@@ -846,7 +1202,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ł';
@@ -891,6 +1247,14 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get chat_noMessages => 'Brak jeszcze wiadomości';
+ @override
+ String get chat_sendMessage => 'Wyślij wiadomość';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Wyślij wiadomość do $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Wyślij wiadomość, aby rozpocząć.';
@@ -905,17 +1269,12 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String chat_replyTo(String name) {
- return 'Odpowiedz $name';
+ return 'Odpowiedz do $name';
}
@override
String get chat_location => 'Lokalizacja';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Wyślij wiadomość do $contactName';
- }
-
@override
String get chat_typeMessage => 'Wpisz wiadomość...';
@@ -931,11 +1290,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
@@ -954,10 +1313,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';
@@ -1009,13 +1368,13 @@ 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.';
+ String get debugLog_noBleActivity => 'Brak aktywności BLE.';
@override
String debugFrame_length(int count) {
@@ -1032,12 +1391,12 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String debugFrame_destinationPubKey(String pubKey) {
- return '- Oznaczenie PubKey: $pubKey';
+ return '- Docelowy klucz publiczny: $pubKey';
}
@override
String debugFrame_timestamp(int timestamp) {
- return '- Timestamp: $timestamp';
+ return '- Znacznik czasu: $timestamp';
}
@override
@@ -1054,7 +1413,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) {
@@ -1062,11 +1421,14 @@ 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';
+ @override
+ String get chat_ShowAllPaths => 'Pokaż wszystkie ścieżki';
+
@override
String get chat_routingMode => 'Tryb routingu';
@@ -1074,7 +1436,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 =>
@@ -1085,18 +1447,20 @@ 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) {
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';
}
@@ -1109,13 +1473,13 @@ 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:';
@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ę.';
@@ -1125,11 +1489,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 =>
@@ -1137,7 +1501,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';
@@ -1169,7 +1533,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';
@@ -1181,7 +1545,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)';
@@ -1192,7 +1556,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';
@@ -1202,12 +1566,36 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String chat_unread(int count) {
- return 'Niezgłoszone: $count';
+ return 'Nieprzeczytane: $count';
}
+ @override
+ String get chat_openLink => 'Otworzyć link?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Czy chcesz otworzyć ten link w przeglądarce?';
+
+ @override
+ String get chat_open => 'Otwórz';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Nie można otworzyć linku: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Nieprawidłowy format linku';
+
@override
String get map_title => 'Mapa węzłów';
+ @override
+ String get map_lineOfSight => 'Linia wzroku';
+
+ @override
+ String get map_losScreenTitle => 'Linia wzroku';
+
@override
String get map_noNodesWithLocation => 'Brak węzłów z danymi lokalizacyjnymi';
@@ -1222,14 +1610,14 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String map_pinsCount(int count) {
- return 'Pinki: $count';
+ return 'Pinezki: $count';
}
@override
String get map_chat => 'Rozmowa';
@override
- String get map_repeater => 'Powtórzacz';
+ String get map_repeater => 'Przekaźnik';
@override
String get map_room => 'Pokój';
@@ -1238,13 +1626,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';
@@ -1266,7 +1654,10 @@ class AppLocalizationsPl extends AppLocalizations {
String get map_shareMarkerHere => 'Udostępnij znacznik tutaj';
@override
- String get map_pinLabel => 'Oznacz etykietę';
+ String get map_setAsMyLocation => 'Ustaw jako moją lokalizację';
+
+ @override
+ String get map_pinLabel => 'Etykieta pinezki';
@override
String get map_label => 'Etykieta';
@@ -1288,12 +1679,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';
@@ -1305,11 +1696,14 @@ 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';
+ @override
+ String get map_showOverlaps => 'Nakładające się klucze przekaźników';
+
@override
String get map_keyPrefix => 'Prefiks klucza';
@@ -1317,43 +1711,68 @@ 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 widiany';
+ String get map_showGuessedLocations =>
+ 'Pokaż przypuszczalne lokalizacje węzłów';
@override
- String get map_sharedPin => 'Podzielony PIN';
+ String get map_showDiscoveryContacts => 'Pokaż odkryte kontakty';
+
+ @override
+ String get map_guessedLocation => 'Przypuszczalna lokalizacja';
+
+ @override
+ String get map_lastSeenTime => 'Ostatni raz widziany';
+
+ @override
+ 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 przekaźnikiem';
@override
- String get mapCache_title => 'Bufor Map Offline';
+ String get map_tapToAdd => 'Kliknij na węzły, aby dodać je do ścieżki.';
+
+ @override
+ String get map_runTrace => 'Uruchom ślad ścieżki';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Wróć tą samą ścieżką';
+
+ @override
+ String get map_removeLast => 'Usuń ostatni';
+
+ @override
+ String get map_pathTraceCancelled => 'Śledzenie ścieżki anulowano.';
+
+ @override
+ 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
@@ -1361,12 +1780,12 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String mapCache_cachedTiles(int count) {
- return 'Pamiętanych $count płytek';
+ return 'Zapisano w pamięci podręcznej $count kafelków';
}
@override
String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
- return 'Pamiętane $downloaded płytki ($failed nieudane)';
+ return 'Zapisano w pamięci podręcznej $downloaded kafelków ($failed nieudanych)';
}
@override
@@ -1375,14 +1794,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';
@@ -1391,11 +1810,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
@@ -1404,7 +1823,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ą';
@@ -1464,13 +1883,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';
@@ -1480,7 +1899,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 przekaźnika';
@override
String get login_roomLogin => 'Logowanie do pokoju';
@@ -1500,14 +1919,14 @@ 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 przekaźnika, aby uzyskać dostęp do ustawień i stanu.';
@override
String get login_roomDescription =>
'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';
@@ -1516,7 +1935,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';
@@ -1536,17 +1955,17 @@ 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 przekaźnik 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
@@ -1554,8 +1973,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.';
}
@@ -1564,7 +1985,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 =>
@@ -1572,21 +1993,21 @@ 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:';
@override
String get path_noRepeatersFound =>
- 'Nie znaleziono repeaterów ani serwerów pokoi.';
+ 'Nie znaleziono przekaźników ani serwerów pokoi.';
@override
String get path_customPathsRequire =>
@@ -1605,23 +2026,32 @@ 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 przekaźnikami';
@override
String get room_management => 'Zarządzanie Serwerem Pokoju';
+ @override
+ String get repeater_guest => 'Informacje dotyczące urządzenia powtarzającego';
+
+ @override
+ String get room_guest => 'Informacje o serwerze';
+
@override
String get repeater_managementTools => 'Narzędzia Zarządzania';
+ @override
+ String get repeater_guestTools => 'Narzędzia dla gości';
+
@override
String get repeater_status => 'Status';
@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 => 'Telemetry';
+ String get repeater_telemetry => 'Telemetria';
@override
String get repeater_telemetrySubtitle =>
@@ -1631,23 +2061,30 @@ 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 przekaźnika';
@override
- String get repeater_neighbours => 'Sąsiedzi';
+ String get repeater_neighbors => 'Sąsiedzi';
@override
- String get repeater_neighboursSubtitle =>
- 'Wyświetl sąsiedztwo zerowych hopów.';
+ String get repeater_neighborsSubtitle => 'Wyświetl sąsiadów zero-hop.';
@override
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_clockSyncAfterLogin =>
+ 'Synchronizacja zegara po zalogowaniu';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatycznie wysyłaj powiadomienie \"synchronizacja zegara\" po pomyślnym zalogowaniu.';
+
+ @override
+ String get repeater_statusTitle => 'Status przekaźnika';
@override
String get repeater_routingMode => 'Tryb routingu';
@@ -1657,7 +2094,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';
@@ -1666,7 +2103,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) {
@@ -1683,7 +2121,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';
@@ -1704,10 +2142,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';
@@ -1733,17 +2171,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
@@ -1752,28 +2190,28 @@ class AppLocalizationsPl extends AppLocalizations {
}
@override
- String get repeater_settingsTitle => 'Ustawienia Powtórki';
+ String get repeater_settingsTitle => 'Ustawienia przekaźnika';
@override
String get repeater_basicSettings => 'Podstawowe Ustawienia';
@override
- String get repeater_repeaterName => 'Nazwa Powtórnika';
+ 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';
@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';
@@ -1785,7 +2223,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';
@@ -1794,10 +2232,10 @@ 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';
+ String get repeater_codingRate => 'Współczynnik kodowania';
@override
String get repeater_locationSettings => 'Ustawienia Lokalizacji';
@@ -1822,7 +2260,7 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get repeater_packetForwardingSubtitle =>
- 'Włącz repeater, aby przekazywać pakiety.';
+ 'Włącz przekaźnik, aby przekazywać pakiety.';
@override
String get repeater_guestAccess => 'Dostęp dla gości';
@@ -1832,17 +2270,17 @@ 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 =>
- '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) {
@@ -1850,7 +2288,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) {
@@ -1859,21 +2297,20 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get repeater_encryptedAdvertInterval =>
- 'Zaszyfrowany Interwał Reklamowy';
+ 'Interwał Zaszyfrowanego Rozgłoszenia';
@override
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 repeater?';
+ 'Czy na pewno chcesz zrestartować ten przekaźnik?';
@override
String get repeater_regenerateIdentityKey => 'Wygeneruj klucz tożsamości';
@@ -1884,18 +2321,18 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get repeater_regenerateIdentityKeyConfirm =>
- 'To zostanie wygenerowane nowe 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 powielacza';
+ '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 =>
@@ -1926,10 +2363,10 @@ 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ż TX power';
+ String get repeater_refreshTxPower => 'Odśwież moc TX';
@override
String get repeater_refreshLocationSettings =>
@@ -1946,7 +2383,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) {
@@ -1959,7 +2396,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ę';
@@ -1990,7 +2427,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) {
@@ -1998,10 +2435,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';
@@ -2013,13 +2450,19 @@ 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';
@override
- String get repeater_cliHelpAdvert => 'Wysyła pakiet reklamowy';
+ String get repeater_cliQuickClockSync => 'Synchronizacja zegara';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Odkryj Sąsiadów';
+
+ @override
+ String get repeater_cliHelpAdvert => 'Wysyła pakiet rozgłoszeniowy';
@override
String get repeater_cliHelpReboot =>
@@ -2050,7 +2493,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 =>
@@ -2058,7 +2501,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 =>
@@ -2066,7 +2509,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 =>
@@ -2074,26 +2517,26 @@ 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 =>
- '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 przekaźnikó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 =>
@@ -2144,7 +2587,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 =>
@@ -2160,7 +2603,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 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 =>
@@ -2168,11 +2611,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 =>
@@ -2188,11 +2631,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 =>
@@ -2222,18 +2665,18 @@ 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ń';
@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';
@@ -2248,11 +2691,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 =>
@@ -2266,15 +2709,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
@@ -2298,7 +2741,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) {
@@ -2307,7 +2750,7 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String telemetry_voltageValue(String volts) {
- return '${volts}W';
+ return '${volts}V';
}
@override
@@ -2333,7 +2776,7 @@ class AppLocalizationsPl extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Powtarzacze Sąsiedzi';
+ String get neighbors_repeatersNeighbors => 'Sąsiedzi przekaźników';
@override
String get neighbors_noData => 'Brak danych dotyczących sąsiadów.';
@@ -2355,10 +2798,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 przekaźników';
@override
String get channelPath_noHopDetails =>
@@ -2386,7 +2829,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
@@ -2406,7 +2849,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';
@@ -2426,7 +2869,7 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get channelPath_noRepeaterLocations =>
- 'Brak dostępnych lokalizacji powtarzaczy dla tego ścieżki.';
+ 'Brak dostępnych lokalizacji przekaźników dla tej ścieżki.';
@override
String channelPath_primaryPath(int index) {
@@ -2449,7 +2892,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ść';
@@ -2564,32 +3007,32 @@ class AppLocalizationsPl extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Zregeneruj sekret';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regeneruj tajny klucz dla \"$name\"? Wszyscy członkowie będą musieli zeskanować nowy kod QR, aby kontynuować komunikację.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Zregeneruj';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Hasło ponownie wygenerowane dla \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Zaktualizuj tajny klucz';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Hasło zaktualizowane dla \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Skanuj nowy kod QR, aby zaktualizować sekret dla \"$name\"';
}
@override
@@ -2642,11 +3085,20 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get listFilter_all => 'Wszystko';
+ @override
+ String get listFilter_favorites => 'Ulubione';
+
+ @override
+ String get listFilter_addToFavorites => 'Dodaj do ulubionych';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Usuń z ulubionych';
+
@override
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';
@@ -2656,4 +3108,613 @@ class AppLocalizationsPl extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nowa grupa';
+
+ @override
+ String get pathTrace_you => 'Ty';
+
+ @override
+ String get pathTrace_failed => 'Śledzenie ścieżki nie powiodło się.';
+
+ @override
+ String get pathTrace_notAvailable => 'Ścieżka śledzenia niedostępna.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Odśwież ścieżkę.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Jeden lub więcej z chmieli nie ma określonej lokalizacji!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Wyczyść ścieżkę';
+
+ @override
+ String get losSelectStartEnd => 'Wybierz węzły początkowe i końcowe dla LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Sprawdzenie pola widzenia nie powiodło się: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Wyczyść wszystkie punkty';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Uruchom LOS, aby wyświetlić profil wysokości';
+
+ @override
+ String get losMenuTitle => 'Menu LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Stuknij węzły lub naciśnij i przytrzymaj mapę, aby uzyskać niestandardowe punkty';
+
+ @override
+ String get losShowDisplayNodes => 'Pokaż węzły wyświetlające';
+
+ @override
+ String get losCustomPoints => 'Punkty niestandardowe';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Niestandardowe $index';
+ }
+
+ @override
+ String get losPointA => 'Punkt A';
+
+ @override
+ String get losPointB => 'Punkt B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antena A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antena B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Uruchom LOS-a';
+
+ @override
+ String get losNoElevationData => 'Brak danych o wysokości';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, czysty LOS, minimalny prześwit $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, zablokowane przez $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: sprawdzam...';
+
+ @override
+ String get losStatusNoData => 'LOS: brak danych';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total jasne, $blocked zablokowane, $unknown nieznane';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Dane dotyczące wysokości są niedostępne dla jednej lub większej liczby próbek.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Nieprawidłowe dane punktów/wysokości do obliczenia LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Zmień nazwę punktu niestandardowego';
+
+ @override
+ String get losPointName => 'Nazwa punktu';
+
+ @override
+ String get losShowPanelTooltip => 'Pokaż panel LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Ukryj panel LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Horyzont radiowy';
+
+ @override
+ String get losLegendLosBeam => 'Linia widoczności';
+
+ @override
+ String get losLegendTerrain => 'Teren';
+
+ @override
+ String get losFrequencyLabel => 'Częstotliwość';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Zobacz szczegóły obliczenia';
+
+ @override
+ String get losFrequencyDialogTitle => 'Obliczanie horyzontu radiowego';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Zaczynając od k=$baselineK przy $baselineFreq MHz, obliczenia korygują współczynnik k dla bieżącego pasma $frequencyMHz MHz, które definiuje zakrzywiony limit horyzontu radiowego.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Śledzenie Ścieżek';
+
+ @override
+ String get contacts_ping => 'Pingować';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Śledzenie ścieżki do przekaźnika';
+
+ @override
+ String get contacts_repeaterPing => 'Ping przekaźnika';
+
+ @override
+ String get contacts_roomPathTrace =>
+ 'Śledzenie ścieżki do serwera pokojowego';
+
+ @override
+ String get contacts_roomPing => 'Pinguj serwer pokoju';
+
+ @override
+ String get contacts_chatTraceRoute => 'Śledź trasę promienia';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Śledź trasę do $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Schowek jest pusty.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Nieprawidłowe dane kontaktowe';
+
+ @override
+ String get contacts_contactImported => 'Kontakt został zaimportowany.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Kontakt nie został zaimportowany.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Rozgłoszenie zero-hop';
+
+ @override
+ String get contacts_floodAdvert => 'Rozgłoszenie zalewowe';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Kopiuj rozgłoszenie do schowka';
+
+ @override
+ String get contacts_addContactFromClipboard => 'Dodaj kontakt z schowka';
+
+ @override
+ String get contacts_ShareContact => 'Kopiuj kontakt do schowka';
+
+ @override
+ String get contacts_ShareContactZeroHop =>
+ 'Udostępnij kontakt przez rozgłoszenie';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Wysłano kontakt przez rozgłoszenie.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Nie udało się wysłać kontaktu.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Rozgłoszenie skopiowano do schowka.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopiowanie rozgłoszenia do schowka nie powiodło się.';
+
+ @override
+ String get notification_activityTitle => 'Aktywność MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'wiadomości',
+ many: 'wiadomości',
+ few: 'wiadomości',
+ one: 'wiadomość',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'wiadomości kanału',
+ many: 'wiadomości kanału',
+ few: 'wiadomości kanału',
+ one: 'wiadomość kanału',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nowych węzłów',
+ many: 'nowych węzłów',
+ few: 'nowe węzły',
+ one: 'nowy węzeł',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nowy $contactType wykryty';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Otrzymano nową wiadomość';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Eksportuj przekaźniki / roomservery do GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Eksportuje przekaźniki / roomservery z lokalizacją do pliku GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Eksportuj towarzyszy do GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Eksportuje towarzyszy z lokalizacją do pliku GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Eksportuj wszystkie kontakty do GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Eksportuje wszystkie kontakty z lokalizacją do pliku GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Pomyślnie wyeksportowano plik GPX.';
+
+ @override
+ String get settings_gpxExportNoContacts =>
+ 'Brak kontaktów do wyeksportowania.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Nie obsługiwane na Twoim urządzeniu/systemie operacyjnym';
+
+ @override
+ String get settings_gpxExportError => 'Wystąpił błąd podczas eksportowania.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Lokalizacje przekaźników i roomserverów';
+
+ @override
+ String get settings_gpxExportChat => 'Lokalizacje towarzyszy';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Wszystkie lokalizacje kontaktów';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Dane mapy wyeksportowane z meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'Eksport danych mapy GPX meshcore-open';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Pobliskie przekaźniki';
+
+ @override
+ String get snrIndicator_lastSeen => 'Ostatnio widziany';
+
+ @override
+ String get contactsSettings_title => 'Ustawienia kontaktów';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatyczne odnajdywanie';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Inne ustawienia związane z kontaktami';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Automatycznie dodaj użytkowników';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Pozwól towarzyszowi automatycznie dodawać znalezione użytkowników.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Automatyczne dodawanie przekaźników';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Zezwól towarzyszowi na automatyczne dodawanie odkrytych przekaźników.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Automatycznie dodaj roomservery';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Zezwól towarzyszowi na automatyczne dodawanie znalezionych roomserverów.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Automatycznie dodaj czujniki';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Zezwól towarzyszowi na automatyczne dodawanie wykrytych czujników.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Nadpisz najstarszy';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Gdy lista kontaktów jest pełna, najstarszy nieulubiony kontakt zostanie zastąpiony.';
+
+ @override
+ String get discoveredContacts_Title => 'Odkryte Kontakty';
+
+ @override
+ String get discoveredContacts_noMatching => 'Brak pasujących kontaktów';
+
+ @override
+ String get discoveredContacts_searchHint => 'Wyszukaj odkryte kontakty';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kontakt dodany';
+
+ @override
+ String get discoveredContacts_addContact => 'Dodaj kontakt';
+
+ @override
+ String get discoveredContacts_copyContact => 'Kopiuj kontakt do schowka';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Usuń kontakt';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Usuń wszystkie odkryte kontakty';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Czy na pewno chcesz usunąć wszystkie znalezione kontakty?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Prosimy o chwilowe oczekiwanie przed ponownym wysłaniem.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Przejdź do najstarszego nieodczytanej wiadomości';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Przy otwieraniu czatu z nieodczytanymi wiadomościami, przewijaj, aby przejść do pierwszej nieodczytanej wiadomości, zamiast do najnowszej.';
+
+ @override
+ String get appSettings_languageHu => 'Węgierski';
+
+ @override
+ String get appSettings_languageJa => 'Japoński';
+
+ @override
+ String get appSettings_languageKo => 'Koreański';
+
+ @override
+ String get radioStats_tooltip => 'Statystyki dotyczące radia i siatki';
+
+ @override
+ String get radioStats_screenTitle => 'Statystyki radiowe';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Połącz się z urządzeniem, aby wyświetlić statystyki radiowe.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Statystyki radiowe wymagają towarzyszącej oprogramowania w wersji 8 lub nowszej.';
+
+ @override
+ String get radioStats_waiting => 'Czekam na dane…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Poziom szumów: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Ostatni poziom RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Ostatni poziom SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Czas emisji w stacji TX (całkowity): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Czas wykorzystania kanału RX (całkowity): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Poziom szumów (dBm) w ostatnich próbkach.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Poziom szumów: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Pobieranie danych dotyczących radia…';
+
+ @override
+ String get radioStats_settingsTile => 'Statystyki radiowe';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Szum tła, RSSI, SNR oraz czas dostępny';
+
+ @override
+ String get translation_title => 'Tłumaczenie';
+
+ @override
+ String get translation_enableTitle => 'Włącz tłumaczenie';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Tłumaczenie otrzymywanych wiadomości oraz umożliwienie tłumaczenia przed wysłaniem.';
+
+ @override
+ String get translation_composerTitle => 'Przekład przed wysłaniem';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Kontroluje domyślny stan ikony tłumaczenia w edytorze.';
+
+ @override
+ String get translation_targetLanguage => 'Język docelowy';
+
+ @override
+ String get translation_useAppLanguage => 'Użyj języka aplikacji';
+
+ @override
+ String get translation_downloadedModelLabel => 'Pobudowany model';
+
+ @override
+ String get translation_presetModelLabel => 'Wspólny model Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'Adres URL do wersji manualnej';
+
+ @override
+ String get translation_downloadModel => 'Pobierz model';
+
+ @override
+ String get translation_downloading => 'Pobieranie...';
+
+ @override
+ String get translation_working => 'Praca...';
+
+ @override
+ String get translation_stop => 'Zatrzymaj się';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Scalanie pobranych fragmentów w jeden plik końcowy...';
+
+ @override
+ String get translation_downloadedModels => 'Pobrane modele';
+
+ @override
+ String get translation_deleteModel => 'Usuń model';
+
+ @override
+ String get translation_modelDownloaded => 'Model tłumaczenia został pobrany.';
+
+ @override
+ String get translation_downloadStopped => 'Pobieranie zakończone.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Nie udało się pobrać: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Najpierw wprowadź adres URL modelu.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Pokaż PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Ukryj PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Kod PIN parowania Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Wprowadź kod PIN dla $deviceName (pozostaw puste, jeśli brak).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Tłumaczenie wiadomości';
+
+ @override
+ String get translation_translateBeforeSending => 'Przekład przed wysłaniem';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Komunikaty zostaną przetłumaczone przed wysłaniem.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Wysyłaj wiadomości w oryginalnym, wpisanym formacie.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Tłumacz na $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Opcje tłumaczenia';
+
+ @override
+ String get translation_systemLanguage => 'Język systemu';
}
diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart
index ae02aff..2dfcd8b 100644
--- a/lib/l10n/app_localizations_pt.dart
+++ b/lib/l10n/app_localizations_pt.dart
@@ -38,6 +38,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get common_delete => 'Excluir';
+ @override
+ String get common_deleteAll => 'Excluir Tudo';
+
@override
String get common_close => 'Fechar';
@@ -108,6 +111,136 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Estabelecer conexão via TCP';
+
+ @override
+ String get tcpHostLabel => 'Endereço IP';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Porta';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Insira o endereço final e conecte-se.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Conectando a $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'É necessário fornecer um endereço IP.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'O valor do porto deve estar entre 1 e 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'O protocolo TCP não é suportado nesta plataforma.';
+
+ @override
+ String get tcpErrorTimedOut => 'A conexão TCP expirou.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Falha na conexão TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Conecte via USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Selecione um dispositivo serial detectado e conecte-o diretamente ao seu nó MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Selecione um dispositivo USB';
+
+ @override
+ String get usbScreenNote =>
+ 'A comunicação serial via USB está ativa em dispositivos Android e plataformas de desktop compatíveis.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Nenhum dispositivo USB encontrado. Conecte um e atualize.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'A permissão para acesso via USB foi negada.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'O dispositivo USB selecionado não está mais disponível.';
+
+ @override
+ String get usbErrorInvalidPort => 'Selecione um dispositivo USB válido.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Já existe uma solicitação de conexão USB em andamento.';
+
+ @override
+ String get usbErrorNotConnected => 'Não há nenhum dispositivo USB conectado.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Não foi possível abrir o dispositivo USB selecionado.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Não foi possível conectar ao dispositivo USB selecionado.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'A comunicação serial via USB não é suportada nesta plataforma.';
+
+ @override
+ String get usbErrorAlreadyActive => 'A conexão USB já está ativa.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Nenhum dispositivo USB foi selecionado.';
+
+ @override
+ String get usbErrorPortClosed => 'A conexão USB não está ativa.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'A conexão expirou. Verifique se o dispositivo possui o firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName => 'Dispositivo de Serial para a Web';
+
+ @override
+ String get usbStatus_notConnected => 'Selecione um dispositivo USB';
+
+ @override
+ String get usbStatus_connecting => 'Conectando ao dispositivo USB...';
+
+ @override
+ String get usbStatus_searching => 'Procurando por dispositivos USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Falha na conexão USB: $error';
+ }
+
@override
String get scanner_scanning => 'Procurando por dispositivos...';
@@ -143,6 +276,23 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get scanner_scan => 'Digitalizar';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth está desativado';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Por favor, ative o Bluetooth para escanear por dispositivos.';
+
+ @override
+ String get scanner_chromeRequired => 'Navegador Chrome necessário';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Esta aplicação web requer o Google Chrome ou um navegador baseado no Chromium para suporte de Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Ative o Bluetooth';
+
@override
String get device_quickSwitch => 'Mudar rapidamente';
@@ -224,6 +374,13 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get settings_longitude => 'Longitude';
+ @override
+ String get settings_contactSettings => 'Configurações de Contato';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Configurações para como os contatos são adicionados';
+
@override
String get settings_privacyMode => 'Modo de Privacidade';
@@ -241,6 +398,51 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Modo de privacidade desativado';
+ @override
+ String get settings_privacy => 'Configurações de Privacidade';
+
+ @override
+ String get settings_privacySubtitle => 'Controle o que é compartilhado.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Escolha quais informações o seu dispositivo compartilha com os outros.';
+
+ @override
+ String get settings_denyAll => 'Negar todos';
+
+ @override
+ String get settings_allowByContact => 'Permitir por bandeiras de contato';
+
+ @override
+ String get settings_allowAll => 'Permitir todos';
+
+ @override
+ String get settings_telemetryBaseMode => 'Modo Base de Telemetria';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Modo de Localização de Telemetria';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Modo de Ambiente de Telemetria';
+
+ @override
+ String get settings_advertLocation => 'Localização do Anúncio';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Incluir localização no anúncio';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Modo de telemetria atualizado';
+
@override
String get settings_actions => 'Ações';
@@ -314,6 +516,10 @@ class AppLocalizationsPt extends AppLocalizations {
String get settings_aboutDescription =>
'Um cliente Flutter de código aberto para dispositivos de rede mesh LoRa Core da MeshCore.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Dados de elevação LOS: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Nome';
@@ -338,15 +544,6 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get settings_presets => 'Presets';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frequência (MHz)';
@@ -375,10 +572,15 @@ class AppLocalizationsPt extends AppLocalizations {
String get settings_txPowerInvalid => 'Potência de TX inválida (0-22 dBm)';
@override
- String get settings_longRange => 'Alcance Longo';
+ String get settings_clientRepeat => 'Repetição sem rede';
@override
- String get settings_fastSpeed => 'Velocidade Rápida';
+ String get settings_clientRepeatSubtitle =>
+ 'Permita que este dispositivo repita pacotes de rede para outros dispositivos.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'A repetição fora da rede requer frequências de 433, 869 ou 918 MHz.';
@override
String settings_error(String message) {
@@ -448,6 +650,20 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Russo';
+
+ @override
+ String get appSettings_languageUk => 'Ucraniano';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Ativar rastreamento de mensagens';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Mostrar metadados detalhados de roteamento e tempo para as mensagens';
+
@override
String get appSettings_notifications => 'Notificações';
@@ -525,6 +741,49 @@ class AppLocalizationsPt extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Rotação de roteamento automático desativada';
+ @override
+ String get appSettings_maxRouteWeight => 'Peso Máximo da Rota';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Peso máximo que um determinado percurso pode acumular com entregas bem-sucedidas.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Peso Inicial da Rota';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Peso inicial para novos caminhos descobertos';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Aumento do peso para indicar sucesso';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Peso adicionado a um caminho após a entrega bem-sucedida.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Redução do peso da falha';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Peso removido de um caminho após uma tentativa de entrega malsucedida.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Número máximo de tentativas de envio de mensagens';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Número de tentativas de reenvio antes de classificar uma mensagem como falha.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Bateria';
@@ -608,6 +867,15 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Cache de Mapa Offline';
+ @override
+ String get appSettings_unitsTitle => 'Unidades';
+
+ @override
+ String get appSettings_unitsMetric => 'Métrico (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperial (ft/mi)';
+
@override
String get appSettings_noAreaSelected => 'Nenhuma área selecionada';
@@ -646,7 +914,35 @@ class AppLocalizationsPt extends AppLocalizations {
'Os contatos serão exibidos quando os dispositivos anunciarem.';
@override
- String get contacts_searchContacts => 'Pesquisar contatos...';
+ String get contacts_unread => 'Não lido';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Pesquisar Contatos...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Pesquisar contatos...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Pesquisar $number$str Favoritos...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Pesquisar $number$str Usuários...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Pesquisar $number$str Repetidores...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Pesquisar $number$str servidores de sala...';
+ }
@override
String get contacts_noUnreadContacts => 'Sem contatos não lidos.';
@@ -695,6 +991,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'O nome do grupo é obrigatório.';
+ @override
+ String get contacts_groupNameReserved => 'Este nome de grupo está reservado';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'O grupo \"$name\" já existe';
@@ -734,6 +1033,42 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Última vez visto $days dias atrás';
}
+ @override
+ String get contact_info => 'Informações de Contato';
+
+ @override
+ String get contact_settings => 'Configurações de Contato';
+
+ @override
+ String get contact_telemetry => 'Telemetria';
+
+ @override
+ String get contact_lastSeen => 'Visto pela última vez';
+
+ @override
+ String get contact_clearChat => 'Limpar Chat';
+
+ @override
+ String get contact_teleBase => 'Base de Telemetria';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Permitir compartilhamento do nível da bateria e telemetria básica';
+
+ @override
+ String get contact_teleLoc => 'Localização de Telemetria';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Permitir compartilhamento de dados de localização';
+
+ @override
+ String get contact_teleEnv => 'Ambiente de Telemetria';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Permitir compartilhamento de dados do sensor de ambiente';
+
@override
String get channels_title => 'Canais';
@@ -772,6 +1107,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get channels_editChannel => 'Editar canal';
+ @override
+ String get channels_muteChannel => 'Silenciar canal';
+
+ @override
+ String get channels_unmuteChannel => 'Ativar canal';
+
@override
String get channels_deleteChannel => 'Excluir canal';
@@ -780,6 +1121,11 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Excluir \"$name\"? Não pode ser desfeito.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Falha ao excluir o canal \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Canal \"$name\" excluído';
@@ -892,6 +1238,14 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get chat_noMessages => 'Ainda não existem mensagens.';
+ @override
+ String get chat_sendMessage => 'Enviar mensagem';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Enviar uma mensagem para $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Enviar uma mensagem para começar';
@@ -911,11 +1265,6 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get chat_location => 'Localização';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Enviar uma mensagem para $contactName';
- }
-
@override
String get chat_typeMessage => 'Digite uma mensagem...';
@@ -1067,6 +1416,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get chat_pathManagement => 'Gerenciamento de Caminhos';
+ @override
+ String get chat_ShowAllPaths => 'Mostrar todos os caminhos';
+
@override
String get chat_routingMode => 'Modo de roteamento';
@@ -1204,9 +1556,33 @@ class AppLocalizationsPt extends AppLocalizations {
return 'Não lido: $count';
}
+ @override
+ String get chat_openLink => 'Abrir link?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Deseja abrir este link no seu navegador?';
+
+ @override
+ String get chat_open => 'Abrir';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Não foi possível abrir o link: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Formato de link inválido';
+
@override
String get map_title => 'Mapa de Nós';
+ @override
+ String get map_lineOfSight => 'Linha de visão';
+
+ @override
+ String get map_losScreenTitle => 'Linha de visão';
+
@override
String get map_noNodesWithLocation =>
'Não existem nós com dados de localização.';
@@ -1265,6 +1641,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Compartilhar marcador aqui';
+ @override
+ String get map_setAsMyLocation => 'Defina minha localização';
+
@override
String get map_pinLabel => 'Rótulo de marcador';
@@ -1310,6 +1689,9 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get map_otherNodes => 'Outros Nós';
+ @override
+ String get map_showOverlaps => 'Sobreposições da Chave Repeater';
+
@override
String get map_keyPrefix => 'Prefixo Chave';
@@ -1325,6 +1707,16 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Mostrar marcadores compartilhados';
+ @override
+ String get map_showGuessedLocations =>
+ 'Mostrar as localizações dos nós estimados';
+
+ @override
+ String get map_showDiscoveryContacts => 'Mostrar Contatos de Descoberta';
+
+ @override
+ String get map_guessedLocation => 'Localização estimada';
+
@override
String get map_lastSeenTime => 'Último Tempo de Visualização';
@@ -1337,6 +1729,21 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get map_manageRepeater => 'Gerenciar Repetidor';
+ @override
+ String get map_tapToAdd => 'Toque nos nós para adicioná-los ao caminho.';
+
+ @override
+ String get map_runTrace => 'Executar Traçado de Caminho';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Retornar ao mesmo caminho.';
+
+ @override
+ String get map_removeLast => 'Remover Último';
+
+ @override
+ String get map_pathTraceCancelled => 'Rastreamento de caminho cancelado.';
+
@override
String get mapCache_title => 'Cache de Mapa Offline';
@@ -1608,9 +2015,18 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get room_management => 'Gerenciamento de Servidor de Sala';
+ @override
+ String get repeater_guest => 'Informações sobre repetidores';
+
+ @override
+ String get room_guest => 'Informações do Servidor';
+
@override
String get repeater_managementTools => 'Ferramentas de Gerenciamento';
+ @override
+ String get repeater_guestTools => 'Ferramentas para hóspedes';
+
@override
String get repeater_status => 'Status';
@@ -1632,11 +2048,10 @@ class AppLocalizationsPt extends AppLocalizations {
String get repeater_cliSubtitle => 'Enviar comandos ao repetidor';
@override
- String get repeater_neighbours => 'Vizinhos';
+ String get repeater_neighbors => 'Vizinhos';
@override
- String get repeater_neighboursSubtitle =>
- 'Visualizar vizinhos de salto zero.';
+ String get repeater_neighborsSubtitle => 'Visualizar vizinhos de salto zero.';
@override
String get repeater_settings => 'Configurações';
@@ -1644,6 +2059,14 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Configurar parâmetros do repetidor';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Sincronização do relógio após o login';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Enviar automaticamente a sincronização do \"relógio\" após um login bem-sucedido.';
+
@override
String get repeater_statusTitle => 'Status do Repetidor';
@@ -2020,6 +2443,12 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Relógio';
+ @override
+ String get repeater_cliQuickClockSync => 'Sincronização do Relógio';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Descobrir Vizinhos';
+
@override
String get repeater_cliHelpAdvert => 'Envia um pacote de anúncios';
@@ -2335,7 +2764,7 @@ class AppLocalizationsPt extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Repetidores Vizinhos';
+ String get neighbors_repeatersNeighbors => 'Repetidores Vizinhos';
@override
String get neighbors_noData => 'Não estão disponíveis dados de vizinhos.';
@@ -2567,32 +2996,32 @@ class AppLocalizationsPt extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Regenerar Senha Segura';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regenerar a chave secreta para \"$name\"? Todos os membros precisarão escanear o novo código QR para continuar a comunicação.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Regenerar';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Senha secreta regenerada para \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Atualizar Segredo';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Segredo atualizado para \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Scanar o novo código QR para atualizar o segredo para \"$name\"\n\n\n+++++';
}
@override
@@ -2645,6 +3074,15 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get listFilter_all => 'Tudo';
+ @override
+ String get listFilter_favorites => 'Favoritos';
+
+ @override
+ String get listFilter_addToFavorites => 'Adicionar aos favoritos';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Remover da lista de favoritos';
+
@override
String get listFilter_users => 'Usuários';
@@ -2659,4 +3097,607 @@ class AppLocalizationsPt extends AppLocalizations {
@override
String get listFilter_newGroup => 'Novo grupo';
+
+ @override
+ String get pathTrace_you => 'Você';
+
+ @override
+ String get pathTrace_failed => 'Falha no rastreamento de caminho.';
+
+ @override
+ String get pathTrace_notAvailable => 'Traçado de caminho não disponível.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Atualizar Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Um ou mais dos lúpulos estão sem localização!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Limpar caminho';
+
+ @override
+ String get losSelectStartEnd => 'Selecione nós iniciais e finais para LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Falha na verificação da linha de visão: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Limpe todos os pontos';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Execute o LOS para visualizar o perfil de elevação';
+
+ @override
+ String get losMenuTitle => 'Menu LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Toque nos nós ou mantenha pressionado o mapa para obter pontos personalizados';
+
+ @override
+ String get losShowDisplayNodes => 'Mostrar nós de exibição';
+
+ @override
+ String get losCustomPoints => 'Pontos personalizados';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return '$index personalizado';
+ }
+
+ @override
+ String get losPointA => 'Ponto A';
+
+ @override
+ String get losPointB => 'Ponto B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antena A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antena B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Executar LOS';
+
+ @override
+ String get losNoElevationData => 'Sem dados de elevação';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, limpar LOS, liberação mínima $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, bloqueado por $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: verificando...';
+
+ @override
+ String get losStatusNoData => 'LOS: sem dados';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total limpo, $blocked bloqueado, $unknown desconhecido';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Dados de elevação indisponíveis para uma ou mais amostras.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Dados de pontos/elevação inválidos para cálculo de LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Renomear ponto personalizado';
+
+ @override
+ String get losPointName => 'Nome do ponto';
+
+ @override
+ String get losShowPanelTooltip => 'Mostrar painel LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Ocultar painel LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Dados de elevação: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Horizonte de rádio';
+
+ @override
+ String get losLegendLosBeam => 'Linha de visada';
+
+ @override
+ String get losLegendTerrain => 'Terreno';
+
+ @override
+ String get losFrequencyLabel => 'Frequência';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Ver detalhes do cálculo';
+
+ @override
+ String get losFrequencyDialogTitle => 'Cálculo do horizonte de rádio';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Começando em k=$baselineK em $baselineFreq MHz, o cálculo ajusta o fator k para a banda atual de $frequencyMHz MHz, que define o limite do horizonte de rádio curvo.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Traçado de Caminho';
+
+ @override
+ String get contacts_ping => 'Pingar';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Traçar caminho para repetidor';
+
+ @override
+ String get contacts_repeaterPing => 'Pingar repetidor';
+
+ @override
+ String get contacts_roomPathTrace => 'Traçar caminho para o servidor da sala';
+
+ @override
+ String get contacts_roomPing => 'Pingar servidor da sala';
+
+ @override
+ String get contacts_chatTraceRoute => 'Rastrear rota do caminho';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Rastrear rota para $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Área de Transferência Está Vazia.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Dados de Contato Inválidos';
+
+ @override
+ String get contacts_contactImported => 'Contato foi importado.';
+
+ @override
+ String get contacts_contactImportFailed => 'Contato falhou ao ser importado.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Anúncio Zero Hop';
+
+ @override
+ String get contacts_floodAdvert => 'Anúncio de Inundação';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Copiar Anúncio para Área de Transferência';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Adicionar Contato da Área de Transferência';
+
+ @override
+ String get contacts_ShareContact =>
+ 'Copiar contato para Área de Transferência';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Compartilhar contato por anúncio';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Enviou contato por anúncio.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed => 'Falha ao enviar contato.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Anúncio copiado para a Área de Transferência.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Cópia do anúncio para a Área de Transferência falhou.';
+
+ @override
+ String get notification_activityTitle => 'Atividade MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'mensagens',
+ one: 'mensagem',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'mensagens de canal',
+ one: 'mensagem de canal',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'novos nós',
+ one: 'novo nó',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Novo $contactType descoberto';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nova mensagem recebida';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exportar repetidores / servidor de sala para GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exporta repetidores / roomserver com localização para arquivo GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Exportar companheiros para GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exporta companheiros com uma localização para um arquivo GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Exportar todos os contatos para GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exporta todos os contatos com uma localização para um arquivo GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Arquivo GPX exportado com sucesso.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Nenhum contato para exportar.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Não suportado no seu dispositivo/SO';
+
+ @override
+ String get settings_gpxExportError => 'Ocorreu um erro ao exportar.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Localizações do servidor de repetidor e sala';
+
+ @override
+ String get settings_gpxExportChat => 'Localizações de companheiros';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Todos os locais de contatos';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Dados do mapa exportados do meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open exportação de dados de mapa GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Repetidores Próximos';
+
+ @override
+ String get snrIndicator_lastSeen => 'Visto pela última vez';
+
+ @override
+ String get contactsSettings_title => 'Configurações de contatos';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Descoberta Automática';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Outras configurações relacionadas a contatos';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Adicionar usuários automaticamente';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Permitir que o companheiro adicione automaticamente os usuários descobertos.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Adicionar repetidores automaticamente';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Permitir que o companheiro adicione automaticamente os repetidores descobertos.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Adicionar automaticamente servidores de sala';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Permitir que o companheiro adicione automaticamente os servidores de salas descobertos.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Adicionar sensores automaticamente';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Permitir que o companheiro adicione automaticamente sensores descobertos.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle =>
+ 'Sobrescrever o Mais Antigo';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Quando a lista de contatos estiver cheia, o contato mais antigo não favoritado será substituído.';
+
+ @override
+ String get discoveredContacts_Title => 'Contatos Descobertos';
+
+ @override
+ String get discoveredContacts_noMatching => 'Nenhum contato correspondente';
+
+ @override
+ String get discoveredContacts_searchHint => 'Pesquisar contatos descobertos';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Contato adicionado';
+
+ @override
+ String get discoveredContacts_addContact => 'Adicionar Contato';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Copiar Contato para a área de transferência';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Excluir Contato';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Excluir Todos os Contatos Descobertos';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Tem certeza de que deseja excluir todos os contatos descobertos?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Por favor, aguarde um momento antes de reenviar.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Vá para a mensagem mais antiga não lida';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Ao abrir uma conversa com mensagens não lidas, role para a primeira mensagem não lida, em vez da mais recente.';
+
+ @override
+ String get appSettings_languageHu => 'Húngaro';
+
+ @override
+ String get appSettings_languageJa => 'Japonês';
+
+ @override
+ String get appSettings_languageKo => 'Coreano';
+
+ @override
+ String get radioStats_tooltip => 'Estatísticas de rádio e malha';
+
+ @override
+ String get radioStats_screenTitle => 'Estatísticas de rádio';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Conecte-se a um dispositivo para visualizar estatísticas de rádio.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'As estatísticas de rádio exigem o firmware v8 ou uma versão mais recente.';
+
+ @override
+ String get radioStats_waiting => 'Aguardando dados…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Nível de ruído: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Último RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Último SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Tempo de transmissão da TX (total): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Tempo de uso do RX (total): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Nível de ruído (dBm) em amostras recentes.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Nível de ruído: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Obtendo estatísticas de rádio…';
+
+ @override
+ String get radioStats_settingsTile => 'Estatísticas de rádio';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Nível de ruído, RSSI, SNR e tempo de transmissão';
+
+ @override
+ String get translation_title => 'Tradução';
+
+ @override
+ String get translation_enableTitle => 'Ativar a tradução';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Traduzir mensagens recebidas e permitir a tradução antes do envio.';
+
+ @override
+ String get translation_composerTitle => 'Traduza antes de enviar';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Controla o estado padrão do ícone de tradução do compositor.';
+
+ @override
+ String get translation_targetLanguage => 'Língua-alvo';
+
+ @override
+ String get translation_useAppLanguage => 'Utilize o idioma da aplicação';
+
+ @override
+ String get translation_downloadedModelLabel => 'Modelo baixado';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Modelo pré-definido da Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'URL do modelo manual';
+
+ @override
+ String get translation_downloadModel => 'Baixar modelo';
+
+ @override
+ String get translation_downloading => 'Baixando...';
+
+ @override
+ String get translation_working => 'Trabalhando...';
+
+ @override
+ String get translation_stop => 'Pare';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Combinando os fragmentos baixados em um único arquivo...';
+
+ @override
+ String get translation_downloadedModels => 'Modelos baixados';
+
+ @override
+ String get translation_deleteModel => 'Excluir modelo';
+
+ @override
+ String get translation_modelDownloaded => 'Modelo de tradução baixado.';
+
+ @override
+ String get translation_downloadStopped => 'Download interrompido.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Falha na descarga: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Insira primeiro a URL do modelo.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Mostrar PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Ocultar PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'PIN de emparelhamento Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Insira o PIN para $deviceName (deixe em branco se não houver).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Tradução da mensagem';
+
+ @override
+ String get translation_translateBeforeSending => 'Traduzir antes de enviar';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'As mensagens serão traduzidas antes de serem enviadas.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Envie mensagens no idioma original, conforme digitado.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Traduzir para $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Opções de tradução';
+
+ @override
+ String get translation_systemLanguage => 'Idioma do sistema';
}
diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart
new file mode 100644
index 0000000..4fac42c
--- /dev/null
+++ b/lib/l10n/app_localizations_ru.dart
@@ -0,0 +1,3717 @@
+// ignore: unused_import
+import 'package:intl/intl.dart' as intl;
+import 'app_localizations.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Russian (`ru`).
+class AppLocalizationsRu extends AppLocalizations {
+ AppLocalizationsRu([String locale = 'ru']) : super(locale);
+
+ @override
+ String get appTitle => 'MeshCore Open';
+
+ @override
+ String get nav_contacts => 'Контакты';
+
+ @override
+ String get nav_channels => 'Каналы';
+
+ @override
+ String get nav_map => 'Карта';
+
+ @override
+ String get common_cancel => 'Отмена';
+
+ @override
+ String get common_ok => 'OK';
+
+ @override
+ String get common_connect => 'Коннект';
+
+ @override
+ String get common_unknownDevice => 'Неизвестное устройство';
+
+ @override
+ String get common_save => 'Сохранить';
+
+ @override
+ String get common_delete => 'Удалить';
+
+ @override
+ String get common_deleteAll => 'Удалить все';
+
+ @override
+ String get common_close => 'Закрыть';
+
+ @override
+ String get common_edit => 'Изменить';
+
+ @override
+ String get common_add => 'Добавить';
+
+ @override
+ String get common_settings => 'Настройки';
+
+ @override
+ String get common_disconnect => 'Отключить';
+
+ @override
+ String get common_connected => 'Подключено';
+
+ @override
+ String get common_disconnected => 'Отключено';
+
+ @override
+ String get common_create => 'Создать';
+
+ @override
+ String get common_continue => 'Продолжить';
+
+ @override
+ String get common_share => 'Поделиться';
+
+ @override
+ String get common_copy => 'Копировать';
+
+ @override
+ String get common_retry => 'Повторить';
+
+ @override
+ String get common_hide => 'Скрыть';
+
+ @override
+ String get common_remove => 'Убрать';
+
+ @override
+ String get common_enable => 'Включить';
+
+ @override
+ String get common_disable => 'Выключить';
+
+ @override
+ String get common_reboot => 'Перезагрузить';
+
+ @override
+ String get common_loading => 'Загрузка...';
+
+ @override
+ String get common_notAvailable => '—';
+
+ @override
+ String common_voltageValue(String volts) {
+ return '$volts В';
+ }
+
+ @override
+ String common_percentValue(int percent) {
+ return '$percent%';
+ }
+
+ @override
+ String get scanner_title => 'MeshCore Open';
+
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Установить соединение по протоколу TCP';
+
+ @override
+ String get tcpHostLabel => 'IP-адрес';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Порт';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Введите адрес и подключитесь.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Подключение к $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Необходимо указать IP-адрес.';
+
+ @override
+ String get tcpErrorPortInvalid =>
+ 'Порт должен находиться в диапазоне от 1 до 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Протокол TCP не поддерживается на этой платформе.';
+
+ @override
+ String get tcpErrorTimedOut => 'Соединение TCP не удалось установить.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Не удалось установить соединение TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Подключение через USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Выберите обнаруженное устройство с последовательным интерфейсом и подключите его напрямую к вашему узлу MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Выберите USB-устройство';
+
+ @override
+ String get usbScreenNote =>
+ 'USB-серийный порт активен на поддерживаемых устройствах Android и на настольных платформах.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Не обнаружено устройств USB. Подключите одно из них и обновите список.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Запрос на доступ через USB был отклонен.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Выбранное USB-устройство больше недоступно.';
+
+ @override
+ String get usbErrorInvalidPort => 'Выберите действительное USB-устройство.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Еще одно запрошенное соединение через USB уже находится в процессе.';
+
+ @override
+ String get usbErrorNotConnected => 'Ни одно USB-устройство не подключено.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Не удалось открыть выбранное USB-устройство.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Не удалось установить соединение с выбранным USB-устройством.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Поддержка последовательного USB отсутствует на данной платформе.';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB-соединение уже установлено.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Не было выбрано ни одно устройство USB.';
+
+ @override
+ String get usbErrorPortClosed => 'USB-соединение не установлено.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Соединение не установлено. Убедитесь, что устройство имеет установленное программное обеспечение USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Устройство для последовательного подключения к сети';
+
+ @override
+ String get usbStatus_notConnected => 'Выберите USB-устройство';
+
+ @override
+ String get usbStatus_connecting => 'Подключение к USB-устройству...';
+
+ @override
+ String get usbStatus_searching => 'Поиск USB-устройств...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Не удалось установить соединение через USB: $error';
+ }
+
+ @override
+ String get scanner_scanning => 'Поиск устройств...';
+
+ @override
+ String get scanner_connecting => 'Подключение...';
+
+ @override
+ String get scanner_disconnecting => 'Отключение...';
+
+ @override
+ String get scanner_notConnected => 'Не подключено';
+
+ @override
+ String scanner_connectedTo(String deviceName) {
+ return 'Подключено к $deviceName';
+ }
+
+ @override
+ String get scanner_searchingDevices => 'Поиск устройств MeshCore...';
+
+ @override
+ String get scanner_tapToScan => 'Нажмите для поиска MeshCore устройств';
+
+ @override
+ String scanner_connectionFailed(String error) {
+ return 'Подключение не удалось: $error';
+ }
+
+ @override
+ String get scanner_stop => 'Стоп';
+
+ @override
+ String get scanner_scan => 'Сканирование';
+
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth выключен';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Пожалуйста, включите Bluetooth, чтобы найти устройства.';
+
+ @override
+ String get scanner_chromeRequired => 'Требуется браузер Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Для поддержки Bluetooth в этом веб-приложении требуется Google Chrome или браузер на базе Chromium.';
+
+ @override
+ String get scanner_enableBluetooth => 'Включите Bluetooth';
+
+ @override
+ String get device_quickSwitch => 'Быстрое переключение';
+
+ @override
+ String get device_meshcore => 'MeshCore';
+
+ @override
+ String get settings_title => 'Настройки';
+
+ @override
+ String get settings_deviceInfo => 'Информация об устройстве';
+
+ @override
+ String get settings_appSettings => 'Настройки приложения';
+
+ @override
+ String get settings_appSettingsSubtitle =>
+ 'Уведомления, сообщения и настройки карты';
+
+ @override
+ String get settings_nodeSettings => 'Настройки ноды';
+
+ @override
+ String get settings_nodeName => 'Имя ноды';
+
+ @override
+ String get settings_nodeNameNotSet => 'Не установлено';
+
+ @override
+ String get settings_nodeNameHint => 'Введите имя ноды';
+
+ @override
+ String get settings_nodeNameUpdated => 'Имя обновлено';
+
+ @override
+ String get settings_radioSettings => 'Настройки радио';
+
+ @override
+ String get settings_radioSettingsSubtitle =>
+ 'Частота, мощность и коэффициент распространения';
+
+ @override
+ String get settings_radioSettingsUpdated => 'Настройки радио обновлены';
+
+ @override
+ String get settings_location => 'Позиция';
+
+ @override
+ String get settings_locationSubtitle => 'Координаты GPS';
+
+ @override
+ String get settings_locationUpdated => 'Позиция и настройки GPS обновлены';
+
+ @override
+ String get settings_locationBothRequired => 'Введите широту и долготу.';
+
+ @override
+ String get settings_locationInvalid => 'Неверная широта или долгота.';
+
+ @override
+ String get settings_locationGPSEnable => 'Включить GPS';
+
+ @override
+ String get settings_locationGPSEnableSubtitle =>
+ 'Включение GPS для автоматического обновления позиции.';
+
+ @override
+ String get settings_locationIntervalSec =>
+ 'Интервал для позиционирования GPS (секунды)';
+
+ @override
+ String get settings_locationIntervalInvalid =>
+ 'Интервал должен составлять не менее 60 секунд и не более 86400 секунд.';
+
+ @override
+ String get settings_latitude => 'Широта';
+
+ @override
+ String get settings_longitude => 'Долгота';
+
+ @override
+ String get settings_contactSettings => 'Настройки контактов';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Настройки добавления контактов';
+
+ @override
+ String get settings_privacyMode => 'Режим конфиденциальности';
+
+ @override
+ String get settings_privacyModeSubtitle =>
+ 'Скрыть имя/позицию в анонсировании';
+
+ @override
+ String get settings_privacyModeToggle =>
+ 'Включите режим конфиденциальности, чтобы скрыть свое имя и местоположение в анонсировании.';
+
+ @override
+ String get settings_privacyModeEnabled => 'Режим конфиденциальности включен';
+
+ @override
+ String get settings_privacyModeDisabled =>
+ 'Режим конфиденциальности выключен';
+
+ @override
+ String get settings_privacy => 'Настройки конфиденциальности';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Контролируйте, какую информацию делиться.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Выберите, какую информацию ваше устройство будет делиться с другими.';
+
+ @override
+ String get settings_denyAll => 'Отклонить все';
+
+ @override
+ String get settings_allowByContact => 'Разрешить по флагам контактов';
+
+ @override
+ String get settings_allowAll => 'Разрешить все';
+
+ @override
+ String get settings_telemetryBaseMode => 'Базовый режим телеметрии';
+
+ @override
+ String get settings_telemetryLocationMode =>
+ 'Режим местоположения телеметрии';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Режим среды телеметрии';
+
+ @override
+ String get settings_advertLocation => 'Местоположение рекламы';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Включить местоположение в объявление';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Мульти-ACK: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Режим телеметрии обновлен';
+
+ @override
+ String get settings_actions => 'Действия';
+
+ @override
+ String get settings_sendAdvertisement => 'Отправить анонсирование';
+
+ @override
+ String get settings_sendAdvertisementSubtitle =>
+ 'Отправить анонсирование о присутствии сейчас';
+
+ @override
+ String get settings_advertisementSent => 'Анонсирование отправлено';
+
+ @override
+ String get settings_syncTime => 'Синхронизация времени';
+
+ @override
+ String get settings_syncTimeSubtitle => 'Синхронизировать время с телефоном';
+
+ @override
+ String get settings_timeSynchronized => 'Время синхронизировано';
+
+ @override
+ String get settings_refreshContacts => 'Обновить контакты';
+
+ @override
+ String get settings_refreshContactsSubtitle =>
+ 'Перезагрузить список контактов с устройства';
+
+ @override
+ String get settings_rebootDevice => 'Перезагрузить устройство';
+
+ @override
+ String get settings_rebootDeviceSubtitle =>
+ 'Перезапустить устройство MeshCore';
+
+ @override
+ String get settings_rebootDeviceConfirm =>
+ 'Вы уверены, что хотите перезагрузить устройство? Вы будете отключены.';
+
+ @override
+ String get settings_debug => 'Отладка';
+
+ @override
+ String get settings_bleDebugLog => 'Журнал отладки BLE';
+
+ @override
+ String get settings_bleDebugLogSubtitle =>
+ 'Команды BLE, ответы и сырые данные';
+
+ @override
+ String get settings_appDebugLog => 'Журнал отладки приложения';
+
+ @override
+ String get settings_appDebugLogSubtitle => 'Сообщения отладки приложения';
+
+ @override
+ String get settings_about => 'О программе';
+
+ @override
+ String settings_aboutVersion(String version) {
+ return 'MeshCore Open v$version';
+ }
+
+ @override
+ String get settings_aboutLegalese => '2026 MeshCore Open Source Project';
+
+ @override
+ String get settings_aboutDescription =>
+ 'Открытое клиентское приложение на Flutter для устройств MeshCore с LoRa-сетями.';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Данные о высоте LOS: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => 'Имя';
+
+ @override
+ String get settings_infoId => 'ID';
+
+ @override
+ String get settings_infoStatus => 'Статус';
+
+ @override
+ String get settings_infoBattery => 'Батарея';
+
+ @override
+ String get settings_infoPublicKey => 'Публичный ключ';
+
+ @override
+ String get settings_infoContactsCount => 'Количество контактов';
+
+ @override
+ String get settings_infoChannelCount => 'Количество каналов';
+
+ @override
+ String get settings_presets => 'Пресеты';
+
+ @override
+ String get settings_frequency => 'Частота (МГц)';
+
+ @override
+ String get settings_frequencyHelper => '300.0 – 2500.0';
+
+ @override
+ String get settings_frequencyInvalid => 'Недопустимая частота (300–2500 МГц)';
+
+ @override
+ String get settings_bandwidth => 'Полоса пропускания';
+
+ @override
+ String get settings_spreadingFactor => 'Коэффициент расширения';
+
+ @override
+ String get settings_codingRate => 'Коэффициент кодирования';
+
+ @override
+ String get settings_txPower => 'Мощность передачи (дБм)';
+
+ @override
+ String get settings_txPowerHelper => '0 – 22';
+
+ @override
+ String get settings_txPowerInvalid =>
+ 'Недопустимая мощность передачи (0–22 дБм)';
+
+ @override
+ String get settings_clientRepeat => 'Повторение \"вне сети\"';
+
+ @override
+ String get settings_clientRepeatSubtitle =>
+ 'Позвольте этому устройству повторять пакеты данных для других устройств.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Для работы в режиме \"без подключения к сети\" требуется частота 433, 869 или 918 МГц.';
+
+ @override
+ String settings_error(String message) {
+ return 'Ошибка: $message';
+ }
+
+ @override
+ String get appSettings_title => 'Настройки приложения';
+
+ @override
+ String get appSettings_appearance => 'Внешний вид';
+
+ @override
+ String get appSettings_theme => 'Тема';
+
+ @override
+ String get appSettings_themeSystem => 'Как в системе';
+
+ @override
+ String get appSettings_themeLight => 'Светлая';
+
+ @override
+ String get appSettings_themeDark => 'Тёмная';
+
+ @override
+ String get appSettings_language => 'Язык';
+
+ @override
+ String get appSettings_languageSystem => 'Как в системе';
+
+ @override
+ String get appSettings_languageEn => 'Английский';
+
+ @override
+ String get appSettings_languageFr => 'Французский';
+
+ @override
+ String get appSettings_languageEs => 'Испанский';
+
+ @override
+ String get appSettings_languageDe => 'Немецкий';
+
+ @override
+ String get appSettings_languagePl => 'Польский';
+
+ @override
+ String get appSettings_languageSl => 'Словенский';
+
+ @override
+ String get appSettings_languagePt => 'Португальский';
+
+ @override
+ String get appSettings_languageIt => 'Итальянский';
+
+ @override
+ String get appSettings_languageZh => 'Китайский';
+
+ @override
+ String get appSettings_languageSv => 'Шведский';
+
+ @override
+ String get appSettings_languageNl => 'Нидерландский';
+
+ @override
+ String get appSettings_languageSk => 'Словацкий';
+
+ @override
+ String get appSettings_languageBg => 'Болгарский';
+
+ @override
+ String get appSettings_languageRu => 'Русский';
+
+ @override
+ String get appSettings_languageUk => 'Українська';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Включить трассировку сообщений';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Показывать подробные метаданные о маршрутизации и времени для сообщений';
+
+ @override
+ String get appSettings_notifications => 'Уведомления';
+
+ @override
+ String get appSettings_enableNotifications => 'Включить уведомления';
+
+ @override
+ String get appSettings_enableNotificationsSubtitle =>
+ 'Получать уведомления о сообщениях и оповещениях';
+
+ @override
+ String get appSettings_notificationPermissionDenied =>
+ 'Разрешение на уведомления отклонено';
+
+ @override
+ String get appSettings_notificationsEnabled => 'Уведомления включены';
+
+ @override
+ String get appSettings_notificationsDisabled => 'Уведомления отключены';
+
+ @override
+ String get appSettings_messageNotifications => 'Уведомления о сообщениях';
+
+ @override
+ String get appSettings_messageNotificationsSubtitle =>
+ 'Показывать уведомление при получении новых сообщений';
+
+ @override
+ String get appSettings_channelMessageNotifications =>
+ 'Уведомления о сообщениях в каналах';
+
+ @override
+ String get appSettings_channelMessageNotificationsSubtitle =>
+ 'Показывать уведомление при получении сообщений в каналах';
+
+ @override
+ String get appSettings_advertisementNotifications =>
+ 'Уведомления об анонсированиях';
+
+ @override
+ String get appSettings_advertisementNotificationsSubtitle =>
+ 'Показывать уведомление при обнаружении новых нод';
+
+ @override
+ String get appSettings_messaging => 'Обмен сообщениями';
+
+ @override
+ String get appSettings_clearPathOnMaxRetry =>
+ 'Сбросить маршрут после максимального числа попыток';
+
+ @override
+ String get appSettings_clearPathOnMaxRetrySubtitle =>
+ 'Сбросить маршрут контакта после 5 неудачных попыток отправки';
+
+ @override
+ String get appSettings_pathsWillBeCleared =>
+ 'Маршруты будут сброшены после 5 неудачных попыток';
+
+ @override
+ String get appSettings_pathsWillNotBeCleared =>
+ 'Маршруты не будут автоматически сбрасываться';
+
+ @override
+ String get appSettings_autoRouteRotation =>
+ 'Автоматическое переключение маршрутов';
+
+ @override
+ String get appSettings_autoRouteRotationSubtitle =>
+ 'Циклически переключаться между лучшими маршрутами и режимом рассылки';
+
+ @override
+ String get appSettings_autoRouteRotationEnabled =>
+ 'Автоматическое переключение маршрутов включено';
+
+ @override
+ String get appSettings_autoRouteRotationDisabled =>
+ 'Автоматическое переключение маршрутов отключено';
+
+ @override
+ String get appSettings_maxRouteWeight =>
+ 'Максимальный допустимый вес маршрута';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Максимальный вес, который может быть перевезён по определённому маршруту при успешных доставках.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Начальный вес маршрута';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Начальный вес для новых, только что открытых маршрутов';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Увеличение веса успеха';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Вес, добавленный к маршруту после успешной доставки.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Уменьшение веса неудачи';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Вес, который был удален с пути после неудачной доставки.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Максимальное количество повторных попыток отправки сообщения';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Количество попыток повторной отправки сообщения перед тем, как пометить его как неудачное.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
+ @override
+ String get appSettings_battery => 'Батарея';
+
+ @override
+ String get appSettings_batteryChemistry => 'Химия батареи';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return 'Установить для устройства ($deviceName)';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst =>
+ 'Подключитесь к устройству, чтобы выбрать';
+
+ @override
+ String get appSettings_batteryNmc => '18650 NMC (3.0–4.2 В)';
+
+ @override
+ String get appSettings_batteryLifepo4 => 'LiFePO4 (2.6–3.65 В)';
+
+ @override
+ String get appSettings_batteryLipo => 'LiPo (3.0–4.2 В)';
+
+ @override
+ String get appSettings_mapDisplay => 'Отображение карты';
+
+ @override
+ String get appSettings_showRepeaters => 'Показывать репитеры';
+
+ @override
+ String get appSettings_showRepeatersSubtitle =>
+ 'Отображать репитеры на карте';
+
+ @override
+ String get appSettings_showChatNodes => 'Показывать чат-ноды';
+
+ @override
+ String get appSettings_showChatNodesSubtitle =>
+ 'Отображать чат-ноды на карте';
+
+ @override
+ String get appSettings_showOtherNodes => 'Показывать другие ноды';
+
+ @override
+ String get appSettings_showOtherNodesSubtitle =>
+ 'Отображать другие типы нод на карте';
+
+ @override
+ String get appSettings_timeFilter => 'Фильтр по времени';
+
+ @override
+ String get appSettings_timeFilterShowAll => 'Показывать все ноды';
+
+ @override
+ String appSettings_timeFilterShowLast(int hours) {
+ return 'Показывать ноды за последние $hours ч';
+ }
+
+ @override
+ String get appSettings_mapTimeFilter => 'Временной фильтр карты';
+
+ @override
+ String get appSettings_showNodesDiscoveredWithin =>
+ 'Показывать ноды, обнаруженные за:';
+
+ @override
+ String get appSettings_allTime => 'Всё время';
+
+ @override
+ String get appSettings_lastHour => 'Последний час';
+
+ @override
+ String get appSettings_last6Hours => 'Последние 6 часов';
+
+ @override
+ String get appSettings_last24Hours => 'Последние 24 часа';
+
+ @override
+ String get appSettings_lastWeek => 'Последнюю неделю';
+
+ @override
+ String get appSettings_offlineMapCache => 'Кэш офлайн-карты';
+
+ @override
+ String get appSettings_unitsTitle => 'Единицы';
+
+ @override
+ String get appSettings_unitsMetric => 'Метрическая (м/км)';
+
+ @override
+ String get appSettings_unitsImperial => 'Имперская (ft / mi)';
+
+ @override
+ String get appSettings_noAreaSelected => 'Область не выбрана';
+
+ @override
+ String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
+ return 'Область выбрана (масштаб $minZoom–$maxZoom)';
+ }
+
+ @override
+ String get appSettings_debugCard => 'Отладка';
+
+ @override
+ String get appSettings_appDebugLogging => 'Журнал отладки приложения';
+
+ @override
+ String get appSettings_appDebugLoggingSubtitle =>
+ 'Записывать отладочные сообщения приложения для диагностики';
+
+ @override
+ String get appSettings_appDebugLoggingEnabled =>
+ 'Журнал отладки приложения включён';
+
+ @override
+ String get appSettings_appDebugLoggingDisabled =>
+ 'Журнал отладки приложения отключён';
+
+ @override
+ String get contacts_title => 'Контакты';
+
+ @override
+ String get contacts_noContacts => 'Контактов пока нет';
+
+ @override
+ String get contacts_contactsWillAppear =>
+ 'Контакты появятся, когда устройства начнут рассылать оповещения';
+
+ @override
+ String get contacts_unread => 'Непрочитанное';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Поиск контактов...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Поиск контактов...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Поиск $number$str избранного...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Поиск $number$str пользователей...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Поиск $number$str ретрансляторов...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Поиск $number$str серверов комнат...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => 'Нет непрочитанных контактов';
+
+ @override
+ String get contacts_noContactsFound => 'Контакты или группы не найдены';
+
+ @override
+ String get contacts_deleteContact => 'Удалить контакт';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return 'Удалить $contactName из контактов?';
+ }
+
+ @override
+ String get contacts_manageRepeater => 'Управление репитером';
+
+ @override
+ String get contacts_manageRoom => 'Управление сервером комнат';
+
+ @override
+ String get contacts_roomLogin => 'Вход на сервер комнат';
+
+ @override
+ String get contacts_openChat => 'Открыть чат';
+
+ @override
+ String get contacts_editGroup => 'Изменить группу';
+
+ @override
+ String get contacts_deleteGroup => 'Удалить группу';
+
+ @override
+ String contacts_deleteGroupConfirm(String groupName) {
+ return 'Удалить \"$groupName\"?';
+ }
+
+ @override
+ String get contacts_newGroup => 'Новая группа';
+
+ @override
+ String get contacts_groupName => 'Имя группы';
+
+ @override
+ String get contacts_groupNameRequired => 'Имя группы обязательно';
+
+ @override
+ String get contacts_groupNameReserved => 'Это имя группы зарезервировано';
+
+ @override
+ String contacts_groupAlreadyExists(String name) {
+ return 'Группа \"$name\" уже существует';
+ }
+
+ @override
+ String get contacts_filterContacts => 'Фильтр контактов...';
+
+ @override
+ String get contacts_noContactsMatchFilter =>
+ 'Нет контактов, соответствующих фильтру';
+
+ @override
+ String get contacts_noMembers => 'Нет участников';
+
+ @override
+ String get contacts_lastSeenNow => 'Видели только что';
+
+ @override
+ String contacts_lastSeenMinsAgo(int minutes) {
+ return 'Видели $minutes мин назад';
+ }
+
+ @override
+ String get contacts_lastSeenHourAgo => 'Видели 1 час назад';
+
+ @override
+ String contacts_lastSeenHoursAgo(int hours) {
+ return 'Видели $hours ч назад';
+ }
+
+ @override
+ String get contacts_lastSeenDayAgo => 'Видели 1 день назад';
+
+ @override
+ String contacts_lastSeenDaysAgo(int days) {
+ return 'Видели $days дн. назад';
+ }
+
+ @override
+ String get contact_info => 'Контактная информация';
+
+ @override
+ String get contact_settings => 'Настройки контактов';
+
+ @override
+ String get contact_telemetry => 'Телеметрия';
+
+ @override
+ String get contact_lastSeen => 'Последний раз видели';
+
+ @override
+ String get contact_clearChat => 'Очистить чат';
+
+ @override
+ String get contact_teleBase => 'База телеметрии';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Разрешить обмен уровнем заряда батареи и базовой телеметрией';
+
+ @override
+ String get contact_teleLoc => 'Местоположение телеметрии';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Разрешить обмен данными о местоположении';
+
+ @override
+ String get contact_teleEnv => 'Среда телеметрии';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Разрешить обмен данными датчиков окружающей среды';
+
+ @override
+ String get channels_title => 'Каналы';
+
+ @override
+ String get channels_noChannelsConfigured => 'Каналы не настроены';
+
+ @override
+ String get channels_addPublicChannel => 'Добавить публичный канал';
+
+ @override
+ String get channels_searchChannels => 'Поиск каналов...';
+
+ @override
+ String get channels_noChannelsFound => 'Каналы не найдены';
+
+ @override
+ String channels_channelIndex(int index) {
+ return 'Канал $index';
+ }
+
+ @override
+ String get channels_hashtagChannel => 'Хэштег-канал';
+
+ @override
+ String get channels_public => 'Публичный';
+
+ @override
+ String get channels_private => 'Приватный';
+
+ @override
+ String get channels_publicChannel => 'Публичный канал';
+
+ @override
+ String get channels_privateChannel => 'Приватный канал';
+
+ @override
+ String get channels_editChannel => 'Изменить канал';
+
+ @override
+ String get channels_muteChannel => 'Отключить уведомления канала';
+
+ @override
+ String get channels_unmuteChannel => 'Включить уведомления канала';
+
+ @override
+ String get channels_deleteChannel => 'Удалить канал';
+
+ @override
+ String channels_deleteChannelConfirm(String name) {
+ return 'Удалить \"$name\"? Это действие нельзя отменить.';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Не удалось удалить канал $name.';
+ }
+
+ @override
+ String channels_channelDeleted(String name) {
+ return 'Канал \"$name\" удалён';
+ }
+
+ @override
+ String get channels_addChannel => 'Добавить канал';
+
+ @override
+ String get channels_channelIndexLabel => 'Индекс канала';
+
+ @override
+ String get channels_channelName => 'Имя канала';
+
+ @override
+ String get channels_usePublicChannel => 'Использовать публичный канал';
+
+ @override
+ String get channels_standardPublicPsk => 'Стандартный публичный PSK';
+
+ @override
+ String get channels_pskHex => 'PSK (Hex)';
+
+ @override
+ String get channels_generateRandomPsk => 'Сгенерировать случайный PSK';
+
+ @override
+ String get channels_enterChannelName => 'Введите имя канала';
+
+ @override
+ String get channels_pskMustBe32Hex =>
+ 'PSK должен содержать 32 шестнадцатеричных символа';
+
+ @override
+ String channels_channelAdded(String name) {
+ return 'Канал \"$name\" добавлен';
+ }
+
+ @override
+ String channels_editChannelTitle(int index) {
+ return 'Изменить канал $index';
+ }
+
+ @override
+ String get channels_smazCompression => 'Сжатие SMAZ';
+
+ @override
+ String channels_channelUpdated(String name) {
+ return 'Канал \"$name\" обновлён';
+ }
+
+ @override
+ String get channels_publicChannelAdded => 'Публичный канал добавлен';
+
+ @override
+ String get channels_sortBy => 'Сортировка';
+
+ @override
+ String get channels_sortManual => 'Вручную';
+
+ @override
+ String get channels_sortAZ => 'По алфавиту';
+
+ @override
+ String get channels_sortLatestMessages => 'По последним сообщениям';
+
+ @override
+ String get channels_sortUnread => 'По непрочитанным';
+
+ @override
+ String get channels_createPrivateChannel => 'Создать приватный канал';
+
+ @override
+ String get channels_createPrivateChannelDesc => 'Защищён секретным ключом.';
+
+ @override
+ String get channels_joinPrivateChannel =>
+ 'Присоединиться к приватному каналу';
+
+ @override
+ String get channels_joinPrivateChannelDesc =>
+ 'Введите секретный ключ вручную.';
+
+ @override
+ String get channels_joinPublicChannel => 'Присоединиться к публичному каналу';
+
+ @override
+ String get channels_joinPublicChannelDesc =>
+ 'К этому каналу может присоединиться любой.';
+
+ @override
+ String get channels_joinHashtagChannel => 'Присоединиться к хэштег-каналу';
+
+ @override
+ String get channels_joinHashtagChannelDesc =>
+ 'К хэштег-каналам может присоединиться любой.';
+
+ @override
+ String get channels_scanQrCode => 'Сканировать QR-код';
+
+ @override
+ String get channels_scanQrCodeComingSoon => 'Скоро будет';
+
+ @override
+ String get channels_enterHashtag => 'Введите хэштег';
+
+ @override
+ String get channels_hashtagHint => 'например, #команда';
+
+ @override
+ String get chat_noMessages => 'Сообщений пока нет';
+
+ @override
+ String get chat_sendMessage => 'Отправить сообщение';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Отправить сообщение $contactName';
+ }
+
+ @override
+ String get chat_sendMessageToStart => 'Отправьте сообщение, чтобы начать';
+
+ @override
+ String get chat_originalMessageNotFound => 'Исходное сообщение не найдено';
+
+ @override
+ String chat_replyingTo(String name) {
+ return 'Ответ для $name';
+ }
+
+ @override
+ String chat_replyTo(String name) {
+ return 'Ответить $name';
+ }
+
+ @override
+ String get chat_location => 'Местоположение';
+
+ @override
+ String get chat_typeMessage => 'Напишите сообщение...';
+
+ @override
+ String chat_messageTooLong(int maxBytes) {
+ return 'Сообщение слишком длинное (макс. $maxBytes байт).';
+ }
+
+ @override
+ String get chat_messageCopied => 'Сообщение скопировано';
+
+ @override
+ String get chat_messageDeleted => 'Сообщение удалено';
+
+ @override
+ String get chat_retryingMessage => 'Повтор отправки сообщения';
+
+ @override
+ String chat_retryCount(int current, int max) {
+ return 'Попытка $current/$max';
+ }
+
+ @override
+ String get chat_sendGif => 'Отправить GIF';
+
+ @override
+ String get chat_reply => 'Ответить';
+
+ @override
+ String get chat_addReaction => 'Добавить реакцию';
+
+ @override
+ String get chat_me => 'Я';
+
+ @override
+ String get emojiCategorySmileys => 'Смайлы';
+
+ @override
+ String get emojiCategoryGestures => 'Жесты';
+
+ @override
+ String get emojiCategoryHearts => 'Сердечки';
+
+ @override
+ String get emojiCategoryObjects => 'Предметы';
+
+ @override
+ String get gifPicker_title => 'Выберите GIF';
+
+ @override
+ String get gifPicker_searchHint => 'Поиск GIF...';
+
+ @override
+ String get gifPicker_poweredBy => 'Работает на GIPHY';
+
+ @override
+ String get gifPicker_noGifsFound => 'GIF не найдены';
+
+ @override
+ String get gifPicker_failedLoad => 'Не удалось загрузить GIF';
+
+ @override
+ String get gifPicker_failedSearch => 'Не удалось выполнить поиск GIF';
+
+ @override
+ String get gifPicker_noInternet => 'Нет подключения к интернету';
+
+ @override
+ String get debugLog_appTitle => 'Журнал отладки приложения';
+
+ @override
+ String get debugLog_bleTitle => 'Журнал отладки BLE';
+
+ @override
+ String get debugLog_copyLog => 'Копировать журнал';
+
+ @override
+ String get debugLog_clearLog => 'Очистить журнал';
+
+ @override
+ String get debugLog_copied => 'Журнал отладки скопирован';
+
+ @override
+ String get debugLog_bleCopied => 'Журнал BLE скопирован';
+
+ @override
+ String get debugLog_noEntries => 'Журнал отладки пока пуст';
+
+ @override
+ String get debugLog_enableInSettings =>
+ 'Включите запись журнала отладки в настройках';
+
+ @override
+ String get debugLog_frames => 'Фреймы';
+
+ @override
+ String get debugLog_rawLogRx => 'Сырой журнал приёма';
+
+ @override
+ String get debugLog_noBleActivity => 'Активность BLE пока отсутствует';
+
+ @override
+ String debugFrame_length(int count) {
+ return 'Длина фрейма: $count байт';
+ }
+
+ @override
+ String debugFrame_command(String value) {
+ return 'Команда: 0x$value';
+ }
+
+ @override
+ String get debugFrame_textMessageHeader => 'Фрейм текстового сообщения:';
+
+ @override
+ String debugFrame_destinationPubKey(String pubKey) {
+ return '- Публичный ключ получателя: $pubKey';
+ }
+
+ @override
+ String debugFrame_timestamp(int timestamp) {
+ return '- Временная метка: $timestamp';
+ }
+
+ @override
+ String debugFrame_flags(String value) {
+ return '- Флаги: 0x$value';
+ }
+
+ @override
+ String debugFrame_textType(int type, String label) {
+ return '- Тип текста: $type ($label)';
+ }
+
+ @override
+ String get debugFrame_textTypeCli => 'CLI';
+
+ @override
+ String get debugFrame_textTypePlain => 'Обычный';
+
+ @override
+ String debugFrame_text(String text) {
+ return '- Текст: \"$text\"';
+ }
+
+ @override
+ String get debugFrame_hexDump => 'Шестнадцатеричный дамп:';
+
+ @override
+ String get chat_pathManagement => 'Управление маршрутами';
+
+ @override
+ String get chat_ShowAllPaths => 'Показать все пути';
+
+ @override
+ String get chat_routingMode => 'Режим маршрутизации';
+
+ @override
+ String get chat_autoUseSavedPath => 'Авто (использовать сохранённый маршрут)';
+
+ @override
+ String get chat_forceFloodMode => 'Принудительный режим рассылки';
+
+ @override
+ String get chat_recentAckPaths =>
+ 'Недавние подтверждённые маршруты (нажмите, чтобы использовать):';
+
+ @override
+ String get chat_pathHistoryFull =>
+ 'История маршрутов заполнена. Удалите записи, чтобы добавить новые.';
+
+ @override
+ String get chat_hopSingular => 'хоп';
+
+ @override
+ String get chat_hopPlural => 'хопов';
+
+ @override
+ String chat_hopsCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'хопов',
+ many: 'хопов',
+ few: 'хопа',
+ one: 'хоп',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String get chat_successes => 'успешно';
+
+ @override
+ String get chat_removePath => 'Удалить маршрут';
+
+ @override
+ String get chat_noPathHistoryYet =>
+ 'История маршрутов пока пуста.\nОтправьте сообщение, чтобы обнаружить маршруты.';
+
+ @override
+ String get chat_pathActions => 'Действия с маршрутом:';
+
+ @override
+ String get chat_setCustomPath => 'Указать маршрут вручную';
+
+ @override
+ String get chat_setCustomPathSubtitle => 'Вручную задать маршрут передачи';
+
+ @override
+ String get chat_clearPath => 'Очистить маршрут';
+
+ @override
+ String get chat_clearPathSubtitle =>
+ 'Принудительно обновить маршрут при следующей отправке';
+
+ @override
+ String get chat_pathCleared =>
+ 'Маршрут очищен. Следующее сообщение обновит маршрут.';
+
+ @override
+ String get chat_floodModeSubtitle =>
+ 'Используйте переключатель маршрутизации в панели приложения';
+
+ @override
+ String get chat_floodModeEnabled =>
+ 'Режим рассылки включён. Отключите через значок маршрутизации в панели приложения.';
+
+ @override
+ String get chat_fullPath => 'Полный маршрут';
+
+ @override
+ String get chat_pathDetailsNotAvailable =>
+ 'Детали маршрута ещё недоступны. Попробуйте отправить сообщение для обновления.';
+
+ @override
+ String chat_pathSetHops(int hopCount, String status) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hopCount,
+ locale: localeName,
+ other: 'хопов',
+ many: 'хопов',
+ few: 'хопа',
+ one: 'хоп',
+ );
+ return 'Маршрут установлен: $hopCount $_temp0 — $status';
+ }
+
+ @override
+ String get chat_pathSavedLocally =>
+ 'Сохранено локально. Подключитесь для синхронизации.';
+
+ @override
+ String get chat_pathDeviceConfirmed => 'Подтверждено устройством.';
+
+ @override
+ String get chat_pathDeviceNotConfirmed => 'Ещё не подтверждено устройством.';
+
+ @override
+ String get chat_type => 'Тип';
+
+ @override
+ String get chat_path => 'Маршрут';
+
+ @override
+ String get chat_publicKey => 'Публичный ключ';
+
+ @override
+ String get chat_compressOutgoingMessages => 'Сжимать исходящие сообщения';
+
+ @override
+ String get chat_floodForced => 'Рассылка (принудительно)';
+
+ @override
+ String get chat_directForced => 'Прямой (принудительно)';
+
+ @override
+ String chat_hopsForced(int count) {
+ return '$count хоп(ов) (принудительно)';
+ }
+
+ @override
+ String get chat_floodAuto => 'Рассылка (авто)';
+
+ @override
+ String get chat_direct => 'Прямой';
+
+ @override
+ String get chat_poiShared => 'Точка интереса отправлена';
+
+ @override
+ String chat_unread(int count) {
+ return 'Непрочитанных: $count';
+ }
+
+ @override
+ String get chat_openLink => 'Открыть ссылку?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Хотите открыть эту ссылку в вашем браузере?';
+
+ @override
+ String get chat_open => 'Открыть';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Не удалось открыть ссылку: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Неправильный формат ссылки';
+
+ @override
+ String get map_title => 'Карта нод';
+
+ @override
+ String get map_lineOfSight => 'Линия видимости';
+
+ @override
+ String get map_losScreenTitle => 'Линия видимости';
+
+ @override
+ String get map_noNodesWithLocation => 'Нет нод с данными о местоположении';
+
+ @override
+ String get map_nodesNeedGps =>
+ 'Ноды должны передавать свои GPS-координаты, чтобы отображаться на карте';
+
+ @override
+ String map_nodesCount(int count) {
+ return 'Нод: $count';
+ }
+
+ @override
+ String map_pinsCount(int count) {
+ return 'Меток: $count';
+ }
+
+ @override
+ String get map_chat => 'Чат';
+
+ @override
+ String get map_repeater => 'Репитер';
+
+ @override
+ String get map_room => 'Комната';
+
+ @override
+ String get map_sensor => 'Сенсор';
+
+ @override
+ String get map_pinDm => 'Метка (ЛС)';
+
+ @override
+ String get map_pinPrivate => 'Метка (Приватная)';
+
+ @override
+ String get map_pinPublic => 'Метка (Публичная)';
+
+ @override
+ String get map_lastSeen => 'Последнее появление';
+
+ @override
+ String get map_disconnectConfirm =>
+ 'Вы уверены, что хотите отключиться от этого устройства?';
+
+ @override
+ String get map_from => 'От';
+
+ @override
+ String get map_source => 'Источник';
+
+ @override
+ String get map_flags => 'Флаги';
+
+ @override
+ String get map_shareMarkerHere => 'Поделиться меткой здесь';
+
+ @override
+ String get map_setAsMyLocation => 'Установить мое местоположение';
+
+ @override
+ String get map_pinLabel => 'Метка';
+
+ @override
+ String get map_label => 'Подпись';
+
+ @override
+ String get map_pointOfInterest => 'Точка интереса';
+
+ @override
+ String get map_sendToContact => 'Отправить контакту';
+
+ @override
+ String get map_sendToChannel => 'Отправить в канал';
+
+ @override
+ String get map_noChannelsAvailable => 'Нет доступных каналов';
+
+ @override
+ String get map_publicLocationShare => 'Публичная передача местоположения';
+
+ @override
+ String map_publicLocationShareConfirm(String channelLabel) {
+ return 'Вы собираетесь поделиться местоположением в $channelLabel. Этот канал публичный, и любой, у кого есть PSK, сможет его увидеть.';
+ }
+
+ @override
+ String get map_connectToShareMarkers =>
+ 'Подключитесь к устройству, чтобы делиться метками';
+
+ @override
+ String get map_filterNodes => 'Фильтр нод';
+
+ @override
+ String get map_nodeTypes => 'Типы нод';
+
+ @override
+ String get map_chatNodes => 'Чат-ноды';
+
+ @override
+ String get map_repeaters => 'Репитеры';
+
+ @override
+ String get map_otherNodes => 'Другие ноды';
+
+ @override
+ String get map_showOverlaps => 'Перекрытия ключа повтора';
+
+ @override
+ String get map_keyPrefix => 'Префикс ключа';
+
+ @override
+ String get map_filterByKeyPrefix => 'Фильтр по префиксу ключа';
+
+ @override
+ String get map_publicKeyPrefix => 'Префикс публичного ключа';
+
+ @override
+ String get map_markers => 'Метки';
+
+ @override
+ String get map_showSharedMarkers => 'Показывать общие метки';
+
+ @override
+ String get map_showGuessedLocations =>
+ 'Отобразить предполагаемые места расположения узлов';
+
+ @override
+ String get map_showDiscoveryContacts => 'Показать контакты Discovery';
+
+ @override
+ String get map_guessedLocation => 'Угаданное место';
+
+ @override
+ String get map_lastSeenTime => 'Время последнего появления';
+
+ @override
+ String get map_sharedPin => 'Общая метка';
+
+ @override
+ String get map_joinRoom => 'Присоединиться к комнате';
+
+ @override
+ String get map_manageRepeater => 'Управление репитером';
+
+ @override
+ String get map_tapToAdd => 'Нажимайте на узлы, чтобы добавить их в путь.';
+
+ @override
+ String get map_runTrace => 'Запустить трассировку пути';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Вернуться обратно по тому же пути';
+
+ @override
+ String get map_removeLast => 'Удалить последний';
+
+ @override
+ String get map_pathTraceCancelled => 'Отмена трассировки пути';
+
+ @override
+ String get mapCache_title => 'Кэш офлайн-карты';
+
+ @override
+ String get mapCache_selectAreaFirst =>
+ 'Сначала выберите область для кэширования';
+
+ @override
+ String get mapCache_noTilesToDownload =>
+ 'Нет плиток для загрузки в этой области';
+
+ @override
+ String get mapCache_downloadTilesTitle => 'Загрузить плитки';
+
+ @override
+ String mapCache_downloadTilesPrompt(int count) {
+ return 'Загрузить $count плиток для офлайн-использования?';
+ }
+
+ @override
+ String get mapCache_downloadAction => 'Загрузить';
+
+ @override
+ String mapCache_cachedTiles(int count) {
+ return 'Закэшировано $count плиток';
+ }
+
+ @override
+ String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
+ return 'Закэшировано $downloaded плиток ($failed не загружено)';
+ }
+
+ @override
+ String get mapCache_clearOfflineCacheTitle => 'Очистить офлайн-кэш';
+
+ @override
+ String get mapCache_clearOfflineCachePrompt =>
+ 'Удалить все закэшированные плитки карты?';
+
+ @override
+ String get mapCache_offlineCacheCleared => 'Офлайн-кэш очищен';
+
+ @override
+ String get mapCache_noAreaSelected => 'Область не выбрана';
+
+ @override
+ String get mapCache_cacheArea => 'Область кэширования';
+
+ @override
+ String get mapCache_useCurrentView => 'Использовать текущий вид';
+
+ @override
+ String get mapCache_zoomRange => 'Диапазон масштаба';
+
+ @override
+ String mapCache_estimatedTiles(int count) {
+ return 'Оценочное количество плиток: $count';
+ }
+
+ @override
+ String mapCache_downloadedTiles(int completed, int total) {
+ return 'Загружено $completed из $total';
+ }
+
+ @override
+ String get mapCache_downloadTilesButton => 'Загрузить плитки';
+
+ @override
+ String get mapCache_clearCacheButton => 'Очистить кэш';
+
+ @override
+ String mapCache_failedDownloads(int count) {
+ return 'Неудачных загрузок: $count';
+ }
+
+ @override
+ String mapCache_boundsLabel(
+ String north,
+ String south,
+ String east,
+ String west,
+ ) {
+ return 'С $north, Ю $south, В $east, З $west';
+ }
+
+ @override
+ String get time_justNow => 'Только что';
+
+ @override
+ String time_minutesAgo(int minutes) {
+ return '$minutes мин назад';
+ }
+
+ @override
+ String time_hoursAgo(int hours) {
+ return '$hours ч назад';
+ }
+
+ @override
+ String time_daysAgo(int days) {
+ return '$days дн. назад';
+ }
+
+ @override
+ String get time_hour => 'час';
+
+ @override
+ String get time_hours => 'часов';
+
+ @override
+ String get time_day => 'день';
+
+ @override
+ String get time_days => 'дней';
+
+ @override
+ String get time_week => 'неделя';
+
+ @override
+ String get time_weeks => 'недель';
+
+ @override
+ String get time_month => 'месяц';
+
+ @override
+ String get time_months => 'месяцев';
+
+ @override
+ String get time_minutes => 'минут';
+
+ @override
+ String get time_allTime => 'Всё время';
+
+ @override
+ String get dialog_disconnect => 'Отключиться';
+
+ @override
+ String get dialog_disconnectConfirm =>
+ 'Вы уверены, что хотите отключиться от этого устройства?';
+
+ @override
+ String get login_repeaterLogin => 'Вход в репитер';
+
+ @override
+ String get login_roomLogin => 'Вход на сервер комнат';
+
+ @override
+ String get login_password => 'Пароль';
+
+ @override
+ String get login_enterPassword => 'Введите пароль';
+
+ @override
+ String get login_savePassword => 'Сохранить пароль';
+
+ @override
+ String get login_savePasswordSubtitle =>
+ 'Пароль будет надёжно сохранён на этом устройстве';
+
+ @override
+ String get login_repeaterDescription =>
+ 'Введите пароль репитера для доступа к настройкам и статусу.';
+
+ @override
+ String get login_roomDescription =>
+ 'Введите пароль комнаты для доступа к настройкам и статусу.';
+
+ @override
+ String get login_routing => 'Маршрутизация';
+
+ @override
+ String get login_routingMode => 'Режим маршрутизации';
+
+ @override
+ String get login_autoUseSavedPath =>
+ 'Авто (использовать сохранённый маршрут)';
+
+ @override
+ String get login_forceFloodMode => 'Принудительный режим рассылки';
+
+ @override
+ String get login_managePaths => 'Управление маршрутами';
+
+ @override
+ String get login_login => 'Войти';
+
+ @override
+ String login_attempt(int current, int max) {
+ return 'Попытка $current/$max';
+ }
+
+ @override
+ String login_failed(String error) {
+ return 'Ошибка входа: $error';
+ }
+
+ @override
+ String get login_failedMessage =>
+ 'Не удалось войти. Либо пароль неверен, либо репитер недоступен.';
+
+ @override
+ String get common_reload => 'Обновить';
+
+ @override
+ String get common_clear => 'Очистить';
+
+ @override
+ String path_currentPath(String path) {
+ return 'Текущий маршрут: $path';
+ }
+
+ @override
+ String path_usingHopsPath(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'хопов',
+ many: 'хопов',
+ few: 'хопа',
+ one: 'хоп',
+ );
+ return 'Используется маршрут из $count $_temp0';
+ }
+
+ @override
+ String get path_enterCustomPath => 'Введите маршрут вручную';
+
+ @override
+ String get path_currentPathLabel => 'Текущий маршрут';
+
+ @override
+ String get path_hexPrefixInstructions =>
+ 'Введите 2-символьные шестнадцатеричные префиксы для каждого хопа, разделённые запятыми.';
+
+ @override
+ String get path_hexPrefixExample =>
+ 'Пример: A1,F2,3C (каждый узел использует первый байт своего публичного ключа)';
+
+ @override
+ String get path_labelHexPrefixes => 'Маршрут (шестнадцатеричные префиксы)';
+
+ @override
+ String get path_helperMaxHops =>
+ 'Максимум 64 хопа. Каждый префикс — 2 шестнадцатеричных символа (1 байт)';
+
+ @override
+ String get path_selectFromContacts => 'Или выберите из контактов:';
+
+ @override
+ String get path_noRepeatersFound => 'Репитеры или серверы комнат не найдены.';
+
+ @override
+ String get path_customPathsRequire =>
+ 'Пользовательские маршруты требуют промежуточных узлов, способных ретранслировать сообщения.';
+
+ @override
+ String path_invalidHexPrefixes(String prefixes) {
+ return 'Недопустимые шестнадцатеричные префиксы: $prefixes';
+ }
+
+ @override
+ String get path_tooLong => 'Маршрут слишком длинный. Максимум 64 хопа.';
+
+ @override
+ String get path_setPath => 'Установить маршрут';
+
+ @override
+ String get repeater_management => 'Управление репитером';
+
+ @override
+ String get room_management => 'Управление сервером комнат';
+
+ @override
+ String get repeater_guest => 'Информация о ретрансляторе';
+
+ @override
+ String get room_guest => 'Информация о сервере';
+
+ @override
+ String get repeater_managementTools => 'Инструменты управления';
+
+ @override
+ String get repeater_guestTools => 'Инструменты для гостей';
+
+ @override
+ String get repeater_status => 'Статус';
+
+ @override
+ String get repeater_statusSubtitle =>
+ 'Просмотр статуса, статистики и соседей репитера';
+
+ @override
+ String get repeater_telemetry => 'Телеметрия';
+
+ @override
+ String get repeater_telemetrySubtitle =>
+ 'Просмотр телеметрии датчиков и системной статистики';
+
+ @override
+ String get repeater_cli => 'CLI';
+
+ @override
+ String get repeater_cliSubtitle => 'Отправка команд репитеру';
+
+ @override
+ String get repeater_neighbors => 'Соседи';
+
+ @override
+ String get repeater_neighborsSubtitle => 'Просмотр соседей на нулевом хопе.';
+
+ @override
+ String get repeater_settings => 'Настройки';
+
+ @override
+ String get repeater_settingsSubtitle => 'Настройка параметров репитера';
+
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Синхронизация часов после входа в систему';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Автоматически отправлять сообщение \"синхронизация времени\" после успешной авторизации.';
+
+ @override
+ String get repeater_statusTitle => 'Статус репитера';
+
+ @override
+ String get repeater_routingMode => 'Режим маршрутизации';
+
+ @override
+ String get repeater_autoUseSavedPath =>
+ 'Авто (использовать сохранённый маршрут)';
+
+ @override
+ String get repeater_forceFloodMode => 'Принудительный режим рассылки';
+
+ @override
+ String get repeater_pathManagement => 'Управление маршрутами';
+
+ @override
+ String get repeater_refresh => 'Обновить';
+
+ @override
+ String get repeater_statusRequestTimeout => 'Время ожидания статуса истекло.';
+
+ @override
+ String repeater_errorLoadingStatus(String error) {
+ return 'Ошибка загрузки статуса: $error';
+ }
+
+ @override
+ String get repeater_systemInformation => 'Системная информация';
+
+ @override
+ String get repeater_battery => 'Батарея';
+
+ @override
+ String get repeater_clockAtLogin => 'Время (при входе)';
+
+ @override
+ String get repeater_uptime => 'Время работы';
+
+ @override
+ String get repeater_queueLength => 'Длина очереди';
+
+ @override
+ String get repeater_debugFlags => 'Флаги отладки';
+
+ @override
+ String get repeater_radioStatistics => 'Радиостатистика';
+
+ @override
+ String get repeater_lastRssi => 'Последний RSSI';
+
+ @override
+ String get repeater_lastSnr => 'Последний SNR';
+
+ @override
+ String get repeater_noiseFloor => 'Уровень шума';
+
+ @override
+ String get repeater_txAirtime => 'Время эфира (передача)';
+
+ @override
+ String get repeater_rxAirtime => 'Время эфира (приём)';
+
+ @override
+ String get repeater_packetStatistics => 'Статистика пакетов';
+
+ @override
+ String get repeater_sent => 'Отправлено';
+
+ @override
+ String get repeater_received => 'Получено';
+
+ @override
+ String get repeater_duplicates => 'Дубликаты';
+
+ @override
+ String repeater_daysHoursMinsSecs(
+ int days,
+ int hours,
+ int minutes,
+ int seconds,
+ ) {
+ return '$days дн. $hoursч $minutesм $secondsс';
+ }
+
+ @override
+ String repeater_packetTxTotal(int total, String flood, String direct) {
+ return 'Всего: $total, Рассылка: $flood, Прямые: $direct';
+ }
+
+ @override
+ String repeater_packetRxTotal(int total, String flood, String direct) {
+ return 'Всего: $total, Рассылка: $flood, Прямые: $direct';
+ }
+
+ @override
+ String repeater_duplicatesFloodDirect(String flood, String direct) {
+ return 'Рассылка: $flood, Прямые: $direct';
+ }
+
+ @override
+ String repeater_duplicatesTotal(int total) {
+ return 'Всего: $total';
+ }
+
+ @override
+ String get repeater_settingsTitle => 'Настройки репитера';
+
+ @override
+ String get repeater_basicSettings => 'Основные настройки';
+
+ @override
+ String get repeater_repeaterName => 'Имя репитера';
+
+ @override
+ String get repeater_repeaterNameHelper => 'Отображаемое имя этого репитера';
+
+ @override
+ String get repeater_adminPassword => 'Пароль администратора';
+
+ @override
+ String get repeater_adminPasswordHelper => 'Пароль с полным доступом';
+
+ @override
+ String get repeater_guestPassword => 'Гостевой пароль';
+
+ @override
+ String get repeater_guestPasswordHelper =>
+ 'Пароль для доступа только для чтения';
+
+ @override
+ String get repeater_radioSettings => 'Настройки радио';
+
+ @override
+ String get repeater_frequencyMhz => 'Частота (МГц)';
+
+ @override
+ String get repeater_frequencyHelper => '300–2500 МГц';
+
+ @override
+ String get repeater_txPower => 'Мощность передачи';
+
+ @override
+ String get repeater_txPowerHelper => '1–30 дБм';
+
+ @override
+ String get repeater_bandwidth => 'Полоса пропускания';
+
+ @override
+ String get repeater_spreadingFactor => 'Коэффициент расширения';
+
+ @override
+ String get repeater_codingRate => 'Коэффициент кодирования';
+
+ @override
+ String get repeater_locationSettings => 'Настройки местоположения';
+
+ @override
+ String get repeater_latitude => 'Широта';
+
+ @override
+ String get repeater_latitudeHelper =>
+ 'В десятичных градусах (напр., 37.7749)';
+
+ @override
+ String get repeater_longitude => 'Долгота';
+
+ @override
+ String get repeater_longitudeHelper =>
+ 'В десятичных градусах (напр., -122.4194)';
+
+ @override
+ String get repeater_features => 'Функции';
+
+ @override
+ String get repeater_packetForwarding => 'Пересылка пакетов';
+
+ @override
+ String get repeater_packetForwardingSubtitle =>
+ 'Разрешить репитеру пересылать пакеты';
+
+ @override
+ String get repeater_guestAccess => 'Гостевой доступ';
+
+ @override
+ String get repeater_guestAccessSubtitle =>
+ 'Разрешить гостевой доступ только для чтения';
+
+ @override
+ String get repeater_privacyMode => 'Режим конфиденциальности';
+
+ @override
+ String get repeater_privacyModeSubtitle =>
+ 'Скрывать имя/местоположение в оповещениях';
+
+ @override
+ String get repeater_advertisementSettings => 'Настройки анонсирования';
+
+ @override
+ String get repeater_localAdvertInterval => 'Интервал локальных анонсирований';
+
+ @override
+ String repeater_localAdvertIntervalMinutes(int minutes) {
+ return '$minutes минут';
+ }
+
+ @override
+ String get repeater_floodAdvertInterval =>
+ 'Интервал анонсирований рассылкой (flood)';
+
+ @override
+ String repeater_floodAdvertIntervalHours(int hours) {
+ return '$hours часов';
+ }
+
+ @override
+ String get repeater_encryptedAdvertInterval =>
+ 'Интервал зашифрованных анонсирований';
+
+ @override
+ String get repeater_dangerZone => 'Опасная зона';
+
+ @override
+ String get repeater_rebootRepeater => 'Перезагрузить репитер';
+
+ @override
+ String get repeater_rebootRepeaterSubtitle =>
+ 'Перезапустить устройство репитера';
+
+ @override
+ String get repeater_rebootRepeaterConfirm =>
+ 'Вы уверены, что хотите перезагрузить этот репитер?';
+
+ @override
+ String get repeater_regenerateIdentityKey => 'Пересоздать ключ идентификации';
+
+ @override
+ String get repeater_regenerateIdentityKeySubtitle =>
+ 'Сгенерировать новую пару публичного/приватного ключей';
+
+ @override
+ String get repeater_regenerateIdentityKeyConfirm =>
+ 'Это создаст новую идентичность для репитера. Продолжить?';
+
+ @override
+ String get repeater_eraseFileSystem => 'Стереть файловую систему';
+
+ @override
+ String get repeater_eraseFileSystemSubtitle =>
+ 'Отформатировать файловую систему репитера';
+
+ @override
+ String get repeater_eraseFileSystemConfirm =>
+ 'ВНИМАНИЕ: это удалит все данные на репитере. Действие нельзя отменить!';
+
+ @override
+ String get repeater_eraseSerialOnly =>
+ 'Очистка доступна только через последовательную консоль.';
+
+ @override
+ String repeater_commandSent(String command) {
+ return 'Команда отправлена: $command';
+ }
+
+ @override
+ String repeater_errorSendingCommand(String error) {
+ return 'Ошибка отправки команды: $error';
+ }
+
+ @override
+ String get repeater_confirm => 'Подтвердить';
+
+ @override
+ String get repeater_settingsSaved => 'Настройки успешно сохранены';
+
+ @override
+ String repeater_errorSavingSettings(String error) {
+ return 'Ошибка сохранения настроек: $error';
+ }
+
+ @override
+ String get repeater_refreshBasicSettings => 'Обновить основные настройки';
+
+ @override
+ String get repeater_refreshRadioSettings => 'Обновить настройки радио';
+
+ @override
+ String get repeater_refreshTxPower => 'Обновить мощность передачи';
+
+ @override
+ String get repeater_refreshLocationSettings =>
+ 'Обновить настройки местоположения';
+
+ @override
+ String get repeater_refreshPacketForwarding => 'Обновить пересылку пакетов';
+
+ @override
+ String get repeater_refreshGuestAccess => 'Обновить гостевой доступ';
+
+ @override
+ String get repeater_refreshPrivacyMode => 'Обновить режим конфиденциальности';
+
+ @override
+ String get repeater_refreshAdvertisementSettings =>
+ 'Обновить настройки анонсирований';
+
+ @override
+ String repeater_refreshed(String label) {
+ return '$label обновлён';
+ }
+
+ @override
+ String repeater_errorRefreshing(String label) {
+ return 'Ошибка обновления $label';
+ }
+
+ @override
+ String get repeater_cliTitle => 'CLI репитера';
+
+ @override
+ String get repeater_debugNextCommand => 'Отладка следующей команды';
+
+ @override
+ String get repeater_commandHelp => 'Справка по командам';
+
+ @override
+ String get repeater_clearHistory => 'Очистить историю';
+
+ @override
+ String get repeater_noCommandsSent => 'Команды ещё не отправлялись';
+
+ @override
+ String get repeater_typeCommandOrUseQuick =>
+ 'Введите команду ниже или используйте быстрые команды';
+
+ @override
+ String get repeater_enterCommandHint => 'Введите команду...';
+
+ @override
+ String get repeater_previousCommand => 'Предыдущая команда';
+
+ @override
+ String get repeater_nextCommand => 'Следующая команда';
+
+ @override
+ String get repeater_enterCommandFirst => 'Сначала введите команду';
+
+ @override
+ String get repeater_cliCommandFrameTitle => 'Фрейм CLI-команды';
+
+ @override
+ String repeater_cliCommandError(String error) {
+ return 'Ошибка: $error';
+ }
+
+ @override
+ String get repeater_cliQuickGetName => 'Получить имя';
+
+ @override
+ String get repeater_cliQuickGetRadio => 'Получить радио';
+
+ @override
+ String get repeater_cliQuickGetTx => 'Получить TX';
+
+ @override
+ String get repeater_cliQuickNeighbors => 'Соседи';
+
+ @override
+ String get repeater_cliQuickVersion => 'Версия';
+
+ @override
+ String get repeater_cliQuickAdvertise => 'Анонсировать';
+
+ @override
+ String get repeater_cliQuickClock => 'Время';
+
+ @override
+ String get repeater_cliQuickClockSync => 'Синхронизация часов';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Обнаружить Соседей';
+
+ @override
+ String get repeater_cliHelpAdvert => 'Отправляет пакет анонсирования';
+
+ @override
+ String get repeater_cliHelpReboot =>
+ 'Перезагружает устройство. (обычно вы получите «Тайм-аут» — это нормально)';
+
+ @override
+ String get repeater_cliHelpClock =>
+ 'Показывает текущее время по часам устройства.';
+
+ @override
+ String get repeater_cliHelpPassword =>
+ 'Устанавливает новый пароль администратора для устройства.';
+
+ @override
+ String get repeater_cliHelpVersion =>
+ 'Показывает версию устройства и дату сборки прошивки.';
+
+ @override
+ String get repeater_cliHelpClearStats =>
+ 'Сбрасывает различные счётчики статистики в ноль.';
+
+ @override
+ String get repeater_cliHelpSetAf =>
+ 'Устанавливает коэффициент времени в эфире.';
+
+ @override
+ String get repeater_cliHelpSetTx =>
+ 'Устанавливает мощность передачи LoRa в дБм. (требуется перезагрузка)';
+
+ @override
+ String get repeater_cliHelpSetRepeat =>
+ 'Включает или отключает роль репитера для этой ноды.';
+
+ @override
+ String get repeater_cliHelpSetAllowReadOnly =>
+ '(Сервер комнат) Если «on», то вход без пароля разрешён, но публиковать в комнату нельзя (только чтение)';
+
+ @override
+ String get repeater_cliHelpSetFloodMax =>
+ 'Устанавливает максимальное число хопов для входящих пакетов в режиме рассылки (если >= макс., пакет не пересылается)';
+
+ @override
+ String get repeater_cliHelpSetIntThresh =>
+ 'Устанавливает порог интерференции (в дБ). По умолчанию 14. Установите 0, чтобы отключить обнаружение помех.';
+
+ @override
+ String get repeater_cliHelpSetAgcResetInterval =>
+ 'Устанавливает интервал сброса автоматической регулировки усиления. Установите 0, чтобы отключить.';
+
+ @override
+ String get repeater_cliHelpSetMultiAcks =>
+ 'Включает или отключает функцию «двойных ACK».';
+
+ @override
+ String get repeater_cliHelpSetAdvertInterval =>
+ 'Устанавливает интервал (в минутах) отправки локального (нулевой хоп) анонсирования. Установите 0, чтобы отключить.';
+
+ @override
+ String get repeater_cliHelpSetFloodAdvertInterval =>
+ 'Устанавливает интервал (в часах) отправки анонсирований рассылкой. Установите 0, чтобы отключить.';
+
+ @override
+ String get repeater_cliHelpSetGuestPassword =>
+ 'Устанавливает/обновляет гостевой пароль. (для репитеров гости могут отправлять запрос «Get Stats»)';
+
+ @override
+ String get repeater_cliHelpSetName => 'Устанавливает имя в оповещениях.';
+
+ @override
+ String get repeater_cliHelpSetLat =>
+ 'Устанавливает широту для карты в оповещениях. (десятичные градусы)';
+
+ @override
+ String get repeater_cliHelpSetLon =>
+ 'Устанавливает долготу для карты в оповещениях. (десятичные градусы)';
+
+ @override
+ String get repeater_cliHelpSetRadio =>
+ 'Устанавливает полностью новые параметры радио и сохраняет их в настройки. Требуется команда «reboot» для применения.';
+
+ @override
+ String get repeater_cliHelpSetRxDelay =>
+ 'Устанавливает (экспериментально) базовую задержку (>1 для эффекта) для принятых пакетов на основе качества сигнала. Установите 0, чтобы отключить.';
+
+ @override
+ String get repeater_cliHelpSetTxDelay =>
+ 'Устанавливает множитель времени в эфире для пакета в режиме рассылки и применяет случайную задержку перед пересылкой (чтобы уменьшить коллизии).';
+
+ @override
+ String get repeater_cliHelpSetDirectTxDelay =>
+ 'То же, что txdelay, но для случайной задержки пересылки пакетов в прямом режиме.';
+
+ @override
+ String get repeater_cliHelpSetBridgeEnabled => 'Включить/выключить мост.';
+
+ @override
+ String get repeater_cliHelpSetBridgeDelay =>
+ 'Установить задержку перед ретрансляцией пакетов.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSource =>
+ 'Выбрать, будет ли мост ретранслировать полученные или отправленные пакеты.';
+
+ @override
+ String get repeater_cliHelpSetBridgeBaud =>
+ 'Установить скорость последовательного соединения для мостов RS232.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSecret =>
+ 'Установить секрет моста для мостов ESP-NOW.';
+
+ @override
+ String get repeater_cliHelpSetAdcMultiplier =>
+ 'Устанавливает пользовательский коэффициент коррекции напряжения батареи (поддерживается только на некоторых платах).';
+
+ @override
+ String get repeater_cliHelpTempRadio =>
+ 'Устанавливает временные параметры радио на заданное число минут, затем возвращает исходные. (НЕ сохраняется в настройки).';
+
+ @override
+ String get repeater_cliHelpSetPerm =>
+ 'Изменяет ACL. Удаляет запись (по префиксу публичного ключа), если «permissions» равен нулю. Добавляет новую запись, если указан полный ключ и он отсутствует в ACL. Обновляет запись по совпадению префикса. Биты прав зависят от роли прошивки, но младшие 2 бита: 0 (Гость), 1 (Только чтение), 2 (Чтение/запись), 3 (Админ)';
+
+ @override
+ String get repeater_cliHelpGetBridgeType =>
+ 'Получает тип моста: none, rs232, espnow';
+
+ @override
+ String get repeater_cliHelpLogStart =>
+ 'Начинает запись пакетов в файловую систему.';
+
+ @override
+ String get repeater_cliHelpLogStop =>
+ 'Останавливает запись пакетов в файловую систему.';
+
+ @override
+ String get repeater_cliHelpLogErase =>
+ 'Удаляет журналы пакетов из файловой системы.';
+
+ @override
+ String get repeater_cliHelpNeighbors =>
+ 'Показывает список других репитеров, услышанных через оповещения нулевого хопа. Каждая строка: префикс-id-в-hex:временная-метка:snr×4';
+
+ @override
+ String get repeater_cliHelpNeighborRemove =>
+ 'Удаляет первую подходящую запись (по префиксу публичного ключа в hex) из списка соседей.';
+
+ @override
+ String get repeater_cliHelpRegion =>
+ '(только через последовательный порт) Показывает все определённые регионы и текущие права на рассылку.';
+
+ @override
+ String get repeater_cliHelpRegionLoad =>
+ 'ПРИМЕЧАНИЕ: это специальная многострочная команда. Каждая следующая строка — имя региона (с отступом пробелами для указания иерархии, минимум один пробел). Завершается пустой строкой.';
+
+ @override
+ String get repeater_cliHelpRegionGet =>
+ 'Ищет регион по префиксу имени (или «*» для глобальной области). Отвечает: «-> имя-региона (родитель) \'F\'»';
+
+ @override
+ String get repeater_cliHelpRegionPut =>
+ 'Добавляет или обновляет определение региона с заданным именем.';
+
+ @override
+ String get repeater_cliHelpRegionRemove =>
+ 'Удаляет определение региона с заданным именем. (должно точно совпадать и не иметь дочерних регионов)';
+
+ @override
+ String get repeater_cliHelpRegionAllowf =>
+ 'Разрешает рассылку («F»lood) для заданного региона. («*» для глобальной/устаревшей области)';
+
+ @override
+ String get repeater_cliHelpRegionDenyf =>
+ 'Запрещает рассылку («F»lood) для заданного региона. (НЕ рекомендуется для глобальной области!)';
+
+ @override
+ String get repeater_cliHelpRegionHome =>
+ 'Показывает текущий «домашний» регион. (Пока не используется, зарезервировано на будущее)';
+
+ @override
+ String get repeater_cliHelpRegionHomeSet =>
+ 'Устанавливает «домашний» регион.';
+
+ @override
+ String get repeater_cliHelpRegionSave =>
+ 'Сохраняет список/карту регионов в память.';
+
+ @override
+ String get repeater_cliHelpGps =>
+ 'Показывает статус GPS. Если GPS выключен — отвечает только «off». Если включён — показывает статус, фиксацию, количество спутников.';
+
+ @override
+ String get repeater_cliHelpGpsOnOff => 'Переключает состояние питания GPS.';
+
+ @override
+ String get repeater_cliHelpGpsSync =>
+ 'Синхронизирует время ноды с часами GPS.';
+
+ @override
+ String get repeater_cliHelpGpsSetLoc =>
+ 'Устанавливает позицию ноды по координатам GPS и сохраняет в настройки.';
+
+ @override
+ String get repeater_cliHelpGpsAdvert =>
+ 'Показывает конфигурацию передачи местоположения в анонсированиях:\n- none: не включать местоположение\n- share: передавать GPS-координаты (из SensorManager)\n- prefs: передавать координаты из настроек';
+
+ @override
+ String get repeater_cliHelpGpsAdvertSet =>
+ 'Устанавливает конфигурацию передачи местоположения.';
+
+ @override
+ String get repeater_commandsListTitle => 'Список команд';
+
+ @override
+ String get repeater_commandsListNote =>
+ 'ПРИМЕЧАНИЕ: для большинства команд «set ...» существуют соответствующие команды «get ...».';
+
+ @override
+ String get repeater_general => 'Общие';
+
+ @override
+ String get repeater_settingsCategory => 'Настройки';
+
+ @override
+ String get repeater_bridge => 'Мост';
+
+ @override
+ String get repeater_logging => 'Журналирование';
+
+ @override
+ String get repeater_neighborsRepeaterOnly => 'Соседи (только для репитеров)';
+
+ @override
+ String get repeater_regionManagementRepeaterOnly =>
+ 'Управление регионами (только для репитеров)';
+
+ @override
+ String get repeater_regionNote =>
+ 'Команды регионов введены для управления определениями регионов и правами доступа.';
+
+ @override
+ String get repeater_gpsManagement => 'Управление GPS';
+
+ @override
+ String get repeater_gpsNote =>
+ 'Команда gps введена для управления параметрами, связанными с местоположением.';
+
+ @override
+ String get telemetry_receivedData => 'Полученные телеметрические данные';
+
+ @override
+ String get telemetry_requestTimeout => 'Время ожидания телеметрии истекло.';
+
+ @override
+ String telemetry_errorLoading(String error) {
+ return 'Ошибка загрузки телеметрии: $error';
+ }
+
+ @override
+ String get telemetry_noData => 'Данные телеметрии недоступны.';
+
+ @override
+ String telemetry_channelTitle(int channel) {
+ return 'Канал $channel';
+ }
+
+ @override
+ String get telemetry_batteryLabel => 'Батарея';
+
+ @override
+ String get telemetry_voltageLabel => 'Напряжение';
+
+ @override
+ String get telemetry_mcuTemperatureLabel => 'Температура МК';
+
+ @override
+ String get telemetry_temperatureLabel => 'Температура';
+
+ @override
+ String get telemetry_currentLabel => 'Ток';
+
+ @override
+ String telemetry_batteryValue(int percent, String volts) {
+ return '$percent% / $voltsВ';
+ }
+
+ @override
+ String telemetry_voltageValue(String volts) {
+ return '$voltsВ';
+ }
+
+ @override
+ String telemetry_currentValue(String amps) {
+ return '$ampsА';
+ }
+
+ @override
+ String telemetry_temperatureValue(String celsius, String fahrenheit) {
+ return '$celsius°C / $fahrenheit°F';
+ }
+
+ @override
+ String get neighbors_receivedData => 'Полученные данные о соседях';
+
+ @override
+ String get neighbors_requestTimedOut =>
+ 'Время ожидания данных о соседях истекло.';
+
+ @override
+ String neighbors_errorLoading(String error) {
+ return 'Ошибка загрузки соседей: $error';
+ }
+
+ @override
+ String get neighbors_repeatersNeighbors => 'Соседи репитеров';
+
+ @override
+ String get neighbors_noData => 'Данные о соседях недоступны.';
+
+ @override
+ String neighbors_unknownContact(String pubkey) {
+ return 'Неизвестный $pubkey';
+ }
+
+ @override
+ String neighbors_heardAgo(String time) {
+ return 'Слушал(а): $time назад';
+ }
+
+ @override
+ String get channelPath_title => 'Путь пакета';
+
+ @override
+ String get channelPath_viewMap => 'Посмотреть на карте';
+
+ @override
+ String get channelPath_otherObservedPaths => 'Другие наблюдаемые пути';
+
+ @override
+ String get channelPath_repeaterHops => 'Хопы через репитеры';
+
+ @override
+ String get channelPath_noHopDetails =>
+ 'Детали хопов для этого пакета не предоставлены.';
+
+ @override
+ String get channelPath_messageDetails => 'Детали сообщения';
+
+ @override
+ String get channelPath_senderLabel => 'Отправитель';
+
+ @override
+ String get channelPath_timeLabel => 'Время';
+
+ @override
+ String get channelPath_repeatsLabel => 'Повторы';
+
+ @override
+ String channelPath_pathLabel(int index) {
+ return 'Путь $index';
+ }
+
+ @override
+ String get channelPath_observedLabel => 'Наблюдаемый';
+
+ @override
+ String channelPath_observedPathTitle(int index, String hops) {
+ return 'Наблюдаемый путь $index • $hops';
+ }
+
+ @override
+ String get channelPath_noLocationData => 'Нет данных о местоположении';
+
+ @override
+ String channelPath_timeWithDate(int day, int month, String time) {
+ return '$day/$month $time';
+ }
+
+ @override
+ String channelPath_timeOnly(String time) {
+ return '$time';
+ }
+
+ @override
+ String get channelPath_unknownPath => 'Неизвестный';
+
+ @override
+ String get channelPath_floodPath => 'Рассылка';
+
+ @override
+ String get channelPath_directPath => 'Прямой';
+
+ @override
+ String channelPath_observedZeroOf(int total) {
+ return '0 из $total хопов';
+ }
+
+ @override
+ String channelPath_observedSomeOf(int observed, int total) {
+ return '$observed из $total хопов';
+ }
+
+ @override
+ String get channelPath_mapTitle => 'Карта пути';
+
+ @override
+ String get channelPath_noRepeaterLocations =>
+ 'Нет данных о местоположении репитеров для этого пути.';
+
+ @override
+ String channelPath_primaryPath(int index) {
+ return 'Путь $index (Основной)';
+ }
+
+ @override
+ String get channelPath_pathLabelTitle => 'Путь';
+
+ @override
+ String get channelPath_observedPathHeader => 'Наблюдаемый путь';
+
+ @override
+ String channelPath_selectedPathLabel(String label, String prefixes) {
+ return '$label • $prefixes';
+ }
+
+ @override
+ String get channelPath_noHopDetailsAvailable =>
+ 'Детали хопов для этого пакета недоступны.';
+
+ @override
+ String get channelPath_unknownRepeater => 'Неизвестный репитер';
+
+ @override
+ String get community_title => 'Сообщество';
+
+ @override
+ String get community_create => 'Создать сообщество';
+
+ @override
+ String get community_createDesc =>
+ 'Создать новое сообщество и поделиться через QR-код.';
+
+ @override
+ String get community_join => 'Присоединиться';
+
+ @override
+ String get community_joinTitle => 'Присоединиться к сообществу';
+
+ @override
+ String community_joinConfirmation(String name) {
+ return 'Вы хотите присоединиться к сообществу \"$name\"?';
+ }
+
+ @override
+ String get community_scanQr => 'Сканировать QR-код сообщества';
+
+ @override
+ String get community_scanInstructions =>
+ 'Наведите камеру на QR-код сообщества';
+
+ @override
+ String get community_showQr => 'Показать QR-код';
+
+ @override
+ String get community_publicChannel => 'Публичный канал сообщества';
+
+ @override
+ String get community_hashtagChannel => 'Хэштег-канал сообщества';
+
+ @override
+ String get community_name => 'Имя сообщества';
+
+ @override
+ String get community_enterName => 'Введите имя сообщества';
+
+ @override
+ String community_created(String name) {
+ return 'Сообщество \"$name\" создано';
+ }
+
+ @override
+ String community_joined(String name) {
+ return 'Присоединились к сообществу \"$name\"';
+ }
+
+ @override
+ String get community_qrTitle => 'Поделиться сообществом';
+
+ @override
+ String community_qrInstructions(String name) {
+ return 'Отсканируйте этот QR-код, чтобы присоединиться к \"$name\"';
+ }
+
+ @override
+ String get community_hashtagPrivacyHint =>
+ 'Хэштег-каналы сообщества доступны только его участникам';
+
+ @override
+ String get community_invalidQrCode => 'Недопустимый QR-код сообщества';
+
+ @override
+ String get community_alreadyMember => 'Уже участник';
+
+ @override
+ String community_alreadyMemberMessage(String name) {
+ return 'Вы уже участник сообщества \"$name\".';
+ }
+
+ @override
+ String get community_addPublicChannel =>
+ 'Добавить публичный канал сообщества';
+
+ @override
+ String get community_addPublicChannelHint =>
+ 'Автоматически добавить публичный канал для этого сообщества';
+
+ @override
+ String get community_noCommunities =>
+ 'Вы ещё не присоединились ни к одному сообществу';
+
+ @override
+ String get community_scanOrCreate =>
+ 'Отсканируйте QR-код или создайте сообщество, чтобы начать';
+
+ @override
+ String get community_manageCommunities => 'Управление сообществами';
+
+ @override
+ String get community_delete => 'Покинуть сообщество';
+
+ @override
+ String community_deleteConfirm(String name) {
+ return 'Покинуть \"$name\"?';
+ }
+
+ @override
+ String community_deleteChannelsWarning(int count) {
+ return 'Это также удалит $count канал(ов) и их сообщения.';
+ }
+
+ @override
+ String community_deleted(String name) {
+ return 'Покинули сообщество \"$name\"';
+ }
+
+ @override
+ String get community_regenerateSecret => 'Пересоздать секрет';
+
+ @override
+ String community_regenerateSecretConfirm(String name) {
+ return 'Пересоздать секретный ключ для \"$name\"? Все участники должны будут отсканировать новый QR-код для продолжения общения.';
+ }
+
+ @override
+ String get community_regenerate => 'Пересоздать';
+
+ @override
+ String community_secretRegenerated(String name) {
+ return 'Секрет пересоздан для \"$name\"';
+ }
+
+ @override
+ String get community_updateSecret => 'Обновить секрет';
+
+ @override
+ String community_secretUpdated(String name) {
+ return 'Секрет обновлён для \"$name\"';
+ }
+
+ @override
+ String community_scanToUpdateSecret(String name) {
+ return 'Отсканируйте новый QR-код, чтобы обновить секрет для \"$name\"';
+ }
+
+ @override
+ String get community_addHashtagChannel => 'Добавить хэштег-канал сообщества';
+
+ @override
+ String get community_addHashtagChannelDesc =>
+ 'Добавить хэштег-канал для этого сообщества';
+
+ @override
+ String get community_selectCommunity => 'Выбрать сообщество';
+
+ @override
+ String get community_regularHashtag => 'Обычный хэштег';
+
+ @override
+ String get community_regularHashtagDesc =>
+ 'Публичный хэштег (любой может присоединиться)';
+
+ @override
+ String get community_communityHashtag => 'Хэштег сообщества';
+
+ @override
+ String get community_communityHashtagDesc =>
+ 'Доступен только участникам сообщества';
+
+ @override
+ String community_forCommunity(String name) {
+ return 'Для $name';
+ }
+
+ @override
+ String get listFilter_tooltip => 'Фильтр и сортировка';
+
+ @override
+ String get listFilter_sortBy => 'Сортировка по';
+
+ @override
+ String get listFilter_latestMessages => 'Последние сообщения';
+
+ @override
+ String get listFilter_heardRecently => 'Слышали недавно';
+
+ @override
+ String get listFilter_az => 'По алфавиту';
+
+ @override
+ String get listFilter_filters => 'Фильтры';
+
+ @override
+ String get listFilter_all => 'Все';
+
+ @override
+ String get listFilter_favorites => 'Избранное';
+
+ @override
+ String get listFilter_addToFavorites => 'Добавить в избранное';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Удалить из избранного';
+
+ @override
+ String get listFilter_users => 'Пользователи';
+
+ @override
+ String get listFilter_repeaters => 'Репитеры';
+
+ @override
+ String get listFilter_roomServers => 'Серверы комнат';
+
+ @override
+ String get listFilter_unreadOnly => 'Только непрочитанные';
+
+ @override
+ String get listFilter_newGroup => 'Новая группа';
+
+ @override
+ String get pathTrace_you => 'Вы';
+
+ @override
+ String get pathTrace_failed => 'Путь трассировки не выполнен.';
+
+ @override
+ String get pathTrace_notAvailable => 'Трассировка пути недоступна.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Обновить Path Trace';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Одному или нескольким хмелям не указано местоположение!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Очистить путь';
+
+ @override
+ String get losSelectStartEnd => 'Выберите начальный и конечный узлы для LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Проверка прямой видимости не удалась: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Очистить все точки';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Запустите LOS, чтобы просмотреть профиль высот.';
+
+ @override
+ String get losMenuTitle => 'ЛОС Меню';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Коснитесь узлов или нажмите и удерживайте карту для выбора пользовательских точек.';
+
+ @override
+ String get losShowDisplayNodes => 'Показать узлы отображения';
+
+ @override
+ String get losCustomPoints => 'Пользовательские точки';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Пользовательский $index';
+ }
+
+ @override
+ String get losPointA => 'Точка А';
+
+ @override
+ String get losPointB => 'Точка Б';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Антенна А: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Антенна Б: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Запустить ЛОС';
+
+ @override
+ String get losNoElevationData => 'Нет данных о высоте';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, свободная зона видимости, минимальный зазор $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, заблокирован $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'ЛОС: проверяю...';
+
+ @override
+ String get losStatusNoData => 'ЛОС: нет данных';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total очищено, $blocked заблокировано, $unknown неизвестно.';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Данные о высоте недоступны для одного или нескольких образцов.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Неверные данные о точках/высоте для расчета LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Переименовать пользовательскую точку';
+
+ @override
+ String get losPointName => 'Имя точки';
+
+ @override
+ String get losShowPanelTooltip => 'Показать панель LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Скрыть панель LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Данные о высоте: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Радиогоризонт';
+
+ @override
+ String get losLegendLosBeam => 'Линия прямой видимости';
+
+ @override
+ String get losLegendTerrain => 'Рельеф';
+
+ @override
+ String get losFrequencyLabel => 'Частота';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Просмотреть детали расчёта';
+
+ @override
+ String get losFrequencyDialogTitle => 'Расчёт радиогоризонта';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Начиная с k=$baselineK на частоте $baselineFreq МГц, расчет корректирует коэффициент k для текущего диапазона $frequencyMHz МГц, который определяет изогнутую границу радиогоризонта.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Трассировка пути';
+
+ @override
+ String get contacts_ping => 'Пинговать';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Отследить путь к ретранслятору';
+
+ @override
+ String get contacts_repeaterPing => 'Пинговать повторитель';
+
+ @override
+ String get contacts_roomPathTrace => 'Трассировка пути к серверу комнаты';
+
+ @override
+ String get contacts_roomPing => 'Пинговать сервер комнаты';
+
+ @override
+ String get contacts_chatTraceRoute => 'Трассировка маршрута';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Показать маршрут к $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Буфер обмена пуст.';
+
+ @override
+ String get contacts_invalidAdvertFormat =>
+ 'Недействительные контактные данные';
+
+ @override
+ String get contacts_contactImported => 'Контакт был импортирован';
+
+ @override
+ String get contacts_contactImportFailed => 'Контакт не удалось импортировать';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Реклама Zero Hop';
+
+ @override
+ String get contacts_floodAdvert => 'Рекламный поток';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Копировать рекламу в буфер обмена';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Добавить контакт из буфера обмена';
+
+ @override
+ String get contacts_ShareContact => 'Копировать контакт в буфер обмена';
+
+ @override
+ String get contacts_ShareContactZeroHop =>
+ 'Поделиться контактом по объявлению';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Отправлено сообщение по объявлению.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Не удалось отправить контакт.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Реклама скопирована в буфер обмена.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Копирование рекламы в буфер обмена не удалось.';
+
+ @override
+ String get notification_activityTitle => 'Активность MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'сообщений',
+ many: 'сообщений',
+ few: 'сообщения',
+ one: 'сообщение',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'сообщений канала',
+ many: 'сообщений канала',
+ few: 'сообщения канала',
+ one: 'сообщение канала',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'новых узлов',
+ many: 'новых узлов',
+ few: 'новых узла',
+ one: 'новый узел',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Обнаружен новый $contactType';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Получено новое сообщение';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Экспортировать рипитеры / сервер комнаты в GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Экспортирует ретрансляторы / сервер комнат с местоположением в файл GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Экспортировать спутников в GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Экспортирует спутников с местоположением в файл GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Экспортировать все контакты в GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Экспортирует все контакты с местоположением в файл GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Успешно экспортирован файл GPX.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Нет контактов для экспорта.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Не поддерживается на вашем устройстве/ОС';
+
+ @override
+ String get settings_gpxExportError => 'Произошла ошибка при экспорте.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Местоположения повторителей и серверов комнат';
+
+ @override
+ String get settings_gpxExportChat => 'Местоположения спутников';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Все местоположения контактов';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Данные карты экспортированы из meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open экспорт данных карты GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Ближайшие ретрансляторы';
+
+ @override
+ String get snrIndicator_lastSeen => 'Последний раз видели';
+
+ @override
+ String get contactsSettings_title => 'Настройки контактов';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Автоматическое обнаружение';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Другие настройки, связанные с контактами';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Автоматически добавлять пользователей';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Разрешить компаньону автоматически добавлять обнаруженных пользователей';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Автоматически добавлять ретрансляторы';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Разрешить спутнику автоматически добавлять обнаруженные ретрансляторы';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Автоматически добавлять серверы комнат';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Разрешить компаньону автоматически добавлять обнаруженные сервера комнат.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Автоматически добавлять датчики';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Разрешить компаньону автоматически добавлять обнаруженные датчики';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle =>
+ 'Перезаписать самое старое';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Когда список контактов заполнен, будет заменен самый старый контакт, который не находится в избранном.';
+
+ @override
+ String get discoveredContacts_Title => 'Обнаруженные контакты';
+
+ @override
+ String get discoveredContacts_noMatching => 'Нет совпадающих контактов';
+
+ @override
+ String get discoveredContacts_searchHint => 'Найденные контакты поиска';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Контакт добавлен';
+
+ @override
+ String get discoveredContacts_addContact => 'Добавить контакт';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Копировать контакт в буфер обмена';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Удалить контакт';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Удалить Все Обнаруженные Контакты';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Вы уверены, что хотите удалить все обнаруженные контакты?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Пожалуйста, подождите немного, прежде чем отправлять сообщение снова.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Перейти к самому старому непрочитанному сообщению';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'При открытии чата с непрочитанными сообщениями, прокрутите страницу, чтобы увидеть первое непрочитанное сообщение, а не последнее.';
+
+ @override
+ String get appSettings_languageHu => 'Венгерский';
+
+ @override
+ String get appSettings_languageJa => 'Японский';
+
+ @override
+ String get appSettings_languageKo => 'Корейский';
+
+ @override
+ String get radioStats_tooltip => 'Статистика радио и беспроводной сети';
+
+ @override
+ String get radioStats_screenTitle => 'Статистика радиовещания';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Подключитесь к устройству, чтобы просмотреть статистику радио.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Для работы радиостатистики требуется установленная версия прошивки v8 или более новая.';
+
+ @override
+ String get radioStats_waiting => 'Ожидаем данных…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Уровень шума: $noiseDbm дБм';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Последнее значение RSSI: $rssiDbm дБм';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Последнее значение SNR: $snr дБ';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Время эфира на телеканале TX (общее): $seconds секунд';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Общее время использования RX (в секундах): $seconds с';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Уровень шума (дБм) на основе последних измерений.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Уровень шума: $noiseDbm дБм';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Получение данных о радио…';
+
+ @override
+ String get radioStats_settingsTile => 'Статистика радиовещания';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Уровень шума, RSSI, SNR и время передачи';
+
+ @override
+ String get translation_title => 'Перевод';
+
+ @override
+ String get translation_enableTitle => 'Включить перевод';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Переводить входящие сообщения и позволять предварительный перевод перед отправкой.';
+
+ @override
+ String get translation_composerTitle => 'Переводить перед отправкой';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Управляет исходным состоянием значка перевода, предоставляемого редактором.';
+
+ @override
+ String get translation_targetLanguage => 'Целевой язык';
+
+ @override
+ String get translation_useAppLanguage => 'Используйте язык приложения';
+
+ @override
+ String get translation_downloadedModelLabel => 'Загруженная модель';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Предопределенная модель от Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'Ссылка на руководство';
+
+ @override
+ String get translation_downloadModel => 'Скачать модель';
+
+ @override
+ String get translation_downloading => 'Загрузка...';
+
+ @override
+ String get translation_working => 'Работа...';
+
+ @override
+ String get translation_stop => 'Прекратите';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Объединение скачанных фрагментов в один финальный файл...';
+
+ @override
+ String get translation_downloadedModels => 'Загруженные модели';
+
+ @override
+ String get translation_deleteModel => 'Удалить модель';
+
+ @override
+ String get translation_modelDownloaded => 'Модель перевода загружена.';
+
+ @override
+ String get translation_downloadStopped => 'Процесс загрузки был прерван.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Не удалось скачать: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Сначала введите URL модели.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Показать PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Скрыть PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'PIN‑код сопряжения Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Введите PIN‑код для $deviceName (оставьте пустым, если нет).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Перевод сообщения';
+
+ @override
+ String get translation_translateBeforeSending => 'Перевести перед отправкой';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Сообщения будут переведены перед отправкой.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Отправляйте сообщения на языке, в котором они были изначально набраны.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Перевести на $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Варианты перевода';
+
+ @override
+ String get translation_systemLanguage => 'Язык системы';
+}
diff --git a/lib/l10n/app_localizations_sk.dart b/lib/l10n/app_localizations_sk.dart
index 81bf16a..c42e024 100644
--- a/lib/l10n/app_localizations_sk.dart
+++ b/lib/l10n/app_localizations_sk.dart
@@ -38,6 +38,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get common_delete => 'Odstrániť';
+ @override
+ String get common_deleteAll => 'Zmazať všetko';
+
@override
String get common_close => 'Zavrieť';
@@ -108,6 +111,135 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Spojte sa pomocou protokolu TCP';
+
+ @override
+ String get tcpHostLabel => 'IP adresa';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Zadajte cieľovú adresu a pripojte sa.';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Pripojenie k $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Je potrebné zadať IP adresu.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Číslo portu musí byť medzi 1 a 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Prevoz prostredníctvom protokolu TCP nie je na tejto platforme podporovaný.';
+
+ @override
+ String get tcpErrorTimedOut => 'Pripojenie TCP vypršalo.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Neúspešné vytvorenie TCP spojenia: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Pripojte cez USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Vyberte detekovaný sériový zariadenie a pripojte ho priamo k vašej MeshCore uzlu.';
+
+ @override
+ String get usbScreenStatus => 'Vyberte USB zariadenie';
+
+ @override
+ String get usbScreenNote =>
+ 'USB sériová komunikácia je aktívna na podporovaných zariadeniach s Androidom a na desktopových platformách.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Nenašli sa žiadne USB zariadenia. Pripojte jedno a obnovte.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Žiadosť o prístup cez USB bola zamietnutá.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Vybrané USB zariadenie už nie je dostupné.';
+
+ @override
+ String get usbErrorInvalidPort => 'Vyberte platné USB zariadenie.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Ďalšia požiadavka na pripojenie cez USB je aktuálne v procese.';
+
+ @override
+ String get usbErrorNotConnected => 'Nie je pripojené žiadne USB zariadenie.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Nepodarilo sa otvoriť vybrané USB zariadenie.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Nepodarilo sa sa sa pripojiť k vybranému USB zariadeniu.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Podpora USB sériového rozhrania nie je na tejto platforme dostupná.';
+
+ @override
+ String get usbErrorAlreadyActive => 'Pripojenie cez USB je už aktivované.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Nebolo vybrané žiadne USB zariadenie.';
+
+ @override
+ String get usbErrorPortClosed => 'Pripojenie cez USB nie je aktivované.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Pripojenie nebolo úspešné. Uistite sa, že zariadenie má nainštalovaný firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName => 'Webový sériový zariadenie';
+
+ @override
+ String get usbStatus_notConnected => 'Vyberte USB zariadenie';
+
+ @override
+ String get usbStatus_connecting => 'Pripojenie k USB zariadeniu...';
+
+ @override
+ String get usbStatus_searching => 'Hľadanie USB zariadení...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Neúspešné pripojenie cez USB: $error';
+ }
+
@override
String get scanner_scanning => 'Skrívania zariadení...';
@@ -143,6 +275,23 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get scanner_scan => 'Skončiť';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth je vypnutý';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Prosím, zapnite Bluetooth, aby ste mohli skenovať pre zariadenia.';
+
+ @override
+ String get scanner_chromeRequired => 'Vyžaduje sa prehliadač Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Táto webová aplikácia vyžaduje Google Chrome alebo prehliadač založený na Chromium pre podporu Bluetooth.';
+
+ @override
+ String get scanner_enableBluetooth => 'Povolte Bluetooth';
+
@override
String get device_quickSwitch => 'Rýchle prepínač';
@@ -223,6 +372,13 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get settings_longitude => 'Dĺžka';
+ @override
+ String get settings_contactSettings => 'Nastavenia kontaktov';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Nastavenia pre pridávanie kontaktov.';
+
@override
String get settings_privacyMode => 'Režim ochrany súkromia';
@@ -239,6 +395,49 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Ochranný režim je vypnutý';
+ @override
+ String get settings_privacy => 'Nastavenia súkromia';
+
+ @override
+ String get settings_privacySubtitle => 'Ovládni, aké informácie sa zdieľajú.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Vyberte, ktoré informácie váš zariadenie zdieľa s ostatnými.';
+
+ @override
+ String get settings_denyAll => 'Zamietnuť všetko';
+
+ @override
+ String get settings_allowByContact => 'Povoliť podľa kontaktových vlajok';
+
+ @override
+ String get settings_allowAll => 'Povoliť všetko';
+
+ @override
+ String get settings_telemetryBaseMode => 'Základný režim telemetrie';
+
+ @override
+ String get settings_telemetryLocationMode => 'Režim umiestnenia telemetrie';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Režim prostredia telemetrie';
+
+ @override
+ String get settings_advertLocation => 'Umiestnenie inzerátu';
+
+ @override
+ String get settings_advertLocationSubtitle => 'Zahrnúť polohu do inzerátu';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Viaceré ACK: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated =>
+ 'Režim telemetrie bol aktualizovaný';
+
@override
String get settings_actions => 'Možné akcie';
@@ -310,6 +509,10 @@ class AppLocalizationsSk extends AppLocalizations {
String get settings_aboutDescription =>
'Otvorený zdrojový Flutter klient pre MeshCore LoRa sieťové zariadenia.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Údaje o nadmorskej výške LOS: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Meno';
@@ -334,15 +537,6 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get settings_presets => 'Prednastavenia';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frekvencia (MHz)';
@@ -371,10 +565,15 @@ class AppLocalizationsSk extends AppLocalizations {
String get settings_txPowerInvalid => 'Neplatná hodnota výkonu TX (0-22 dBm)';
@override
- String get settings_longRange => 'Dlhý dosah';
+ String get settings_clientRepeat => 'Opätovné použitie bez elektrickej siete';
@override
- String get settings_fastSpeed => 'Rýchla rýchlosť';
+ String get settings_clientRepeatSubtitle =>
+ 'Umožnite, aby toto zariadenie opakovávalo siete pre ostatných.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Použitie off-grid systému vyžaduje frekvencie 433, 869 alebo 918 MHz.';
@override
String settings_error(String message) {
@@ -444,6 +643,19 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Ruština';
+
+ @override
+ String get appSettings_languageUk => 'Ukrajinská';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Povoliť sledovanie správ';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Zobraziť podrobné metadáta o smerovaní a časovaní správ';
+
@override
String get appSettings_notifications => 'Upozornenia';
@@ -518,6 +730,48 @@ class AppLocalizationsSk extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Automatické prekladanie trás pozastavené';
+ @override
+ String get appSettings_maxRouteWeight => 'Maximálna hmotnosť trasy';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Maximálna hmotnosť, ktorú môže trás prenášať vďaka úspešným zásielkam.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Počiatočná váha trasy';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Počiatočná váha pre nové, objavené cesty';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement => 'Zvyšenie váhy úspechu';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Hmotnosť pridaná k trase po úspešnej doručení';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Sníženie váhy, ktorá sa používa na odhad rizika.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Hmotnosť odstránená z cesty po neúspešnej doručenie';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Maximalný počet pokusov o doručenie správ';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Počet pokusov o odošleť pred označením správy ako neúspešnej';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batéria';
@@ -602,6 +856,15 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Offline Mapa Pamäť';
+ @override
+ String get appSettings_unitsTitle => 'Jednotky';
+
+ @override
+ String get appSettings_unitsMetric => 'Metrické (m / km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperiálne (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'Neoznačila sa žiadna oblasť';
@@ -639,7 +902,35 @@ class AppLocalizationsSk extends AppLocalizations {
'Kontakty sa zobrazia, keď zariadenia spúšťajú reklamu.';
@override
- String get contacts_searchContacts => 'Vyhľadávajte kontakty...';
+ String get contacts_unread => 'Neprečítané';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Hľadať kontakty...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Vyhľadávajte kontakty...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Hľadať $number$str obľúbené...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Hľadať $number$str používateľov...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Hľadať $number$str opakovače...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Hľadaj $number$str serverov miestností...';
+ }
@override
String get contacts_noUnreadContacts => 'Žiadne neprečítané kontakty';
@@ -688,6 +979,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Skupina musí mať názov.';
+ @override
+ String get contacts_groupNameReserved => 'Tento názov skupiny je rezervovaný';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Skupina \"$name\" už existuje';
@@ -729,6 +1023,41 @@ class AppLocalizationsSk extends AppLocalizations {
return 'Posledné zobrazenie $days dní dozadu';
}
+ @override
+ String get contact_info => 'Kontaktné informácie';
+
+ @override
+ String get contact_settings => 'Nastavenia kontaktov';
+
+ @override
+ String get contact_telemetry => 'Telemetria';
+
+ @override
+ String get contact_lastSeen => 'Naposledy videný';
+
+ @override
+ String get contact_clearChat => 'Vymazať chat';
+
+ @override
+ String get contact_teleBase => 'Báza telemetrie';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Povoliť zdieľanie úrovne batérie a základnej telemetrie';
+
+ @override
+ String get contact_teleLoc => 'Lokácia telemetrie';
+
+ @override
+ String get contact_teleLocSubtitle => 'Povoliť zdieľanie údajov o lokalite';
+
+ @override
+ String get contact_teleEnv => 'Prostredie telemetrie';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Povoliť zdieľanie údajov senzorov prostredia';
+
@override
String get channels_title => 'Kanály';
@@ -767,6 +1096,12 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get channels_editChannel => 'Upraviť kanál';
+ @override
+ String get channels_muteChannel => 'Stlmiť kanál';
+
+ @override
+ String get channels_unmuteChannel => 'Zrušiť stlmenie kanála';
+
@override
String get channels_deleteChannel => 'Odstrániť kanál';
@@ -775,6 +1110,11 @@ class AppLocalizationsSk extends AppLocalizations {
return 'Odstrániť \"$name\"? To sa nedá zrušiť.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Kanál \"$name\" sa nepodarilo odstrániť';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Kanál \"$name\" bol odstránený';
@@ -886,6 +1226,14 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get chat_noMessages => 'Zatiaľ žiadne správy.';
+ @override
+ String get chat_sendMessage => 'Odoslať správu';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Pošli správu $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Pošlite správu na začiatok';
@@ -905,11 +1253,6 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get chat_location => 'Lokalita';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Pošli správu $contactName';
- }
-
@override
String get chat_typeMessage => 'Napište správu...';
@@ -1062,6 +1405,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get chat_pathManagement => 'Správa ciest';
+ @override
+ String get chat_ShowAllPaths => 'Zobraziť všetky cesty';
+
@override
String get chat_routingMode => 'Režim trasy';
@@ -1200,9 +1546,33 @@ class AppLocalizationsSk extends AppLocalizations {
return 'Nezriadené: $count';
}
+ @override
+ String get chat_openLink => 'Otvoriť odkaz?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Chcete otvoriť tento odkaz v prehliadači?';
+
+ @override
+ String get chat_open => 'Otvoriť';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Nepodarilo sa otvoriť odkaz: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Neplatný formát odkazu';
+
@override
String get map_title => 'Mapa uzlov';
+ @override
+ String get map_lineOfSight => 'Line of Sight';
+
+ @override
+ String get map_losScreenTitle => 'Line of Sight';
+
@override
String get map_noNodesWithLocation => 'Žiadne uzly s údajmi o polohe';
@@ -1260,6 +1630,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Zdieľte značku tu';
+ @override
+ String get map_setAsMyLocation => 'Nastavte ako moju polohu';
+
@override
String get map_pinLabel => 'Označka upozornenia';
@@ -1305,6 +1678,9 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get map_otherNodes => 'Ostatné uzly';
+ @override
+ String get map_showOverlaps => 'Prekrývanie opakovača kľúča';
+
@override
String get map_keyPrefix => 'Päťciferné predpona';
@@ -1320,6 +1696,16 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Zobraziť zdieľané značky';
+ @override
+ String get map_showGuessedLocations =>
+ 'Zobraziť umiestnenia odhadnutých uzlov';
+
+ @override
+ String get map_showDiscoveryContacts => 'Zobraziť kontakty objavov';
+
+ @override
+ String get map_guessedLocation => 'Odhadnutá lokalita';
+
@override
String get map_lastSeenTime => 'Posledný čas sledovania';
@@ -1332,6 +1718,21 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get map_manageRepeater => 'Spravovať Opakovanie';
+ @override
+ String get map_tapToAdd => 'Kliknite na uzly, aby ste ich pridali k ceste.';
+
+ @override
+ String get map_runTrace => 'Spustiť trasovaním cesty';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Vráťte sa späť po tej istej ceste.';
+
+ @override
+ String get map_removeLast => 'Odstrániť posledný';
+
+ @override
+ String get map_pathTraceCancelled => 'Zrušenie stopáže cesty bolo zrušené.';
+
@override
String get mapCache_title => 'Offline Mapa Pamäť';
@@ -1603,9 +2004,18 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get room_management => 'Správa servera miestnosti';
+ @override
+ String get repeater_guest => 'Informácie o opakovači';
+
+ @override
+ String get room_guest => 'Informácie o serveri';
+
@override
String get repeater_managementTools => 'Nástroje na správu';
+ @override
+ String get repeater_guestTools => 'Nástroje pre hostí';
+
@override
String get repeater_status => 'Status';
@@ -1627,10 +2037,10 @@ class AppLocalizationsSk extends AppLocalizations {
String get repeater_cliSubtitle => 'Pošlite príkazy opakovaču';
@override
- String get repeater_neighbours => 'Súsezný';
+ String get repeater_neighbors => 'Súsezný';
@override
- String get repeater_neighboursSubtitle => 'Zobraziť susedné body bez skokov.';
+ String get repeater_neighborsSubtitle => 'Zobraziť susedné body bez skokov.';
@override
String get repeater_settings => 'Nastavenia';
@@ -1638,6 +2048,14 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Konfigurujte parametre opakovača';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Synchronizácia hodiniek po prihlávení';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automaticky posielajte notifikáciu \"synchronizácia času\" po úspešnom prihládení.';
+
@override
String get repeater_statusTitle => 'Status opakého zboru';
@@ -2008,6 +2426,12 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Hodiny';
+ @override
+ String get repeater_cliQuickClockSync => 'Synchronizácia hodin';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Objaviť susedov';
+
@override
String get repeater_cliHelpAdvert => 'Odosiela reklamnú balíček.';
@@ -2321,7 +2745,7 @@ class AppLocalizationsSk extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Opakovadlá Súsezná';
+ String get neighbors_repeatersNeighbors => 'Opakovadlá Súsezná';
@override
String get neighbors_noData =>
@@ -2553,32 +2977,32 @@ class AppLocalizationsSk extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Zobraziť nový tajný kód';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Znovu vygenerovať tajný kľúč pre \"$name\"? Všetci členovia budú musieť skanovať nový QR kód, aby mohli nadviazať komunikáciu.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Znovu vygenerovať';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Záznam pre \"$name\" bol regenerovaný tajne';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Aktualizovať tajné heslo';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Zmena tajnej slova pre \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Skáňte nový QR kód na aktualizáciu tajného hesla pre \"$name\"';
}
@override
@@ -2630,6 +3054,15 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get listFilter_all => 'Všetko';
+ @override
+ String get listFilter_favorites => 'Obľúbené';
+
+ @override
+ String get listFilter_addToFavorites => 'Pridaj do obľúbených';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Odstrániť z označení';
+
@override
String get listFilter_users => 'Používatelia';
@@ -2644,4 +3077,607 @@ class AppLocalizationsSk extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nová skupina';
+
+ @override
+ String get pathTrace_you => 'Vy';
+
+ @override
+ String get pathTrace_failed => 'Sledovanie cesty zlyhalo.';
+
+ @override
+ String get pathTrace_notAvailable => 'Path trace nie je k dispozícii.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Obnoviť Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Jedna alebo viac chmeľov chýba lokalita!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Zmazať cestu';
+
+ @override
+ String get losSelectStartEnd => 'Vyberte počiatočný a koncový uzol pre LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Kontrola priamej viditeľnosti zlyhala: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Vymazať všetky body';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Ak chcete zobraziť výškový profil, spustite LOS';
+
+ @override
+ String get losMenuTitle => 'Menu LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Klepnutím na uzly alebo dlhým stlačením mapy získate vlastné body';
+
+ @override
+ String get losShowDisplayNodes => 'Zobraziť uzly zobrazenia';
+
+ @override
+ String get losCustomPoints => 'Vlastné body';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Vlastné $index';
+ }
+
+ @override
+ String get losPointA => 'Bod A';
+
+ @override
+ String get losPointB => 'Bod B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Anténa A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Anténa B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Spustite LOS';
+
+ @override
+ String get losNoElevationData => 'Žiadne údaje o nadmorskej výške';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, vymazať LOS, min. vôľa $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blokovaný $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: kontrolujem...';
+
+ @override
+ String get losStatusNoData => 'LOS: žiadne údaje';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total vymazané, $blocked blokované, $unknown neznáme';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Údaje o nadmorskej výške nie sú k dispozícii pre jednu alebo viacero vzoriek.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Neplatné body/údaje o nadmorskej výške pre výpočet LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Premenovať vlastný bod';
+
+ @override
+ String get losPointName => 'Názov bodu';
+
+ @override
+ String get losShowPanelTooltip => 'Zobraziť panel LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Skryť panel LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Rádiový horizont';
+
+ @override
+ String get losLegendLosBeam => 'Priama viditeľnosť';
+
+ @override
+ String get losLegendTerrain => 'Terén';
+
+ @override
+ String get losFrequencyLabel => 'Frekvencia';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Zobraziť podrobnosti výpočtu';
+
+ @override
+ String get losFrequencyDialogTitle => 'Výpočet rádiového horizontu';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Počnúc od k=$baselineK pri $baselineFreq MHz výpočet upraví k-faktor pre aktuálne pásmo $frequencyMHz MHz, ktorý definuje zakrivený strop rádiového horizontu.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Sledovanie lúčov';
+
+ @override
+ String get contacts_ping => 'Pingovať';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Sledovanie cesty k opakovaču';
+
+ @override
+ String get contacts_repeaterPing => 'Pingovať opakovač';
+
+ @override
+ String get contacts_roomPathTrace => 'Sledovanie cesty k serveru miestnosti';
+
+ @override
+ String get contacts_roomPing => 'Ping server miestnosti';
+
+ @override
+ String get contacts_chatTraceRoute => 'Sledovať trasu lúča';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Sledovať trasu k $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Schránka je prázdna.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Neplatné kontaktné údaje';
+
+ @override
+ String get contacts_contactImported => 'Kontakt bol importovaný.';
+
+ @override
+ String get contacts_contactImportFailed =>
+ 'Kontakt sa nepodarilo importovať.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Inzerát Zero Hop';
+
+ @override
+ String get contacts_floodAdvert => 'Inzerát povodní';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Kopírovať reklamu do schránky';
+
+ @override
+ String get contacts_addContactFromClipboard => 'Pridať kontakt z schránky';
+
+ @override
+ String get contacts_ShareContact => 'Kopírovať kontakt do schránky';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Zdieľať kontakt cez inzerát';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Poslal kontakt cez inzerát.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Zlyhalo odoslanie kontaktu.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Inzerát bol skopírovaný do schránky.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopírovanie inzerátu do schránky zlyhalo.';
+
+ @override
+ String get notification_activityTitle => 'Aktivita MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'správ',
+ few: 'správy',
+ one: 'správa',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'správ kanálu',
+ few: 'správy kanálu',
+ one: 'správa kanálu',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nových uzlov',
+ few: 'nové uzly',
+ one: 'nový uzol',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Nový $contactType objavený';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Prijatá nová správa';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exportovať repeater / server miestnosti do GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exportuje repeater / roomserver s lokalitou do súboru GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Export sprievodcov do GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exportuje sprievodcov s umiestnením do súboru GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Exportovať všetky kontakty do GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exportuje všetky kontakty s lokalitou do súboru GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Úspešne exportovaný súbor GPX.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Žiadne kontakty na export.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Nie je podporované na vašom zariadení/operáciomnom systéme';
+
+ @override
+ String get settings_gpxExportError => 'Vyskytol sa chyba počas exportu.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Umiestnenia opakovačov a serverov miestností';
+
+ @override
+ String get settings_gpxExportChat => 'Lokácie sprievodcov';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Všetky kontaktné lokality';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Mapové údaje exportované z meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open export dát GPX mapových údajov';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Miestne opakovače';
+
+ @override
+ String get snrIndicator_lastSeen => 'Naposledy videný';
+
+ @override
+ String get contactsSettings_title => 'Nastavenia kontaktov';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatické zisťovanie';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Ďalšie nastavenia súvisiace s kontaktami';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Automaticky pridávať užívateľov';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Povoliť spoločníkovi automaticky pridávať objavených užívateľov.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Automaticky pridávať opakovače';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Povoliť spoločníkovi automaticky pridávať objavené repeater.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Automaticky pridávať server miestnosti';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Povoliť spoločníkovi automaticky pridať objavené serverové miestnosti.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Automaticky pridávať senzory';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Povoliť spoločníkovi automaticky pridávať objavené senzory.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Prepísať najstaršie';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Keď je zoznam kontaktov plný, bude nahradený najstarší neoznačený kontakt.';
+
+ @override
+ String get discoveredContacts_Title => 'Objavené kontakty';
+
+ @override
+ String get discoveredContacts_noMatching => 'Žiadne zhodné kontakty';
+
+ @override
+ String get discoveredContacts_searchHint => 'Vyhľadať objavené kontakty';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kontakt bol pridaný';
+
+ @override
+ String get discoveredContacts_addContact => 'Pridať kontakt';
+
+ @override
+ String get discoveredContacts_copyContact => 'Kopírovať kontakt do schránky';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Zmazať kontakt';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Zmazať všetky objavené kontakty';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Ste si istí, že chcete zmazať všetky objavené kontakty?';
+
+ @override
+ String get chat_sendCooldown => 'Prosím, počkajte chvíľu, než zašlete znova.';
+
+ @override
+ String get appSettings_jumpToOldestUnread => 'Presk oceň';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Pri otvorení chatu s neprečítanými správami, prejdite do prvého neprečítaného, namiesto poslednej.';
+
+ @override
+ String get appSettings_languageHu => 'Maďarský';
+
+ @override
+ String get appSettings_languageJa => 'Japonský';
+
+ @override
+ String get appSettings_languageKo => 'Kórejský';
+
+ @override
+ String get radioStats_tooltip => 'Statistiky rádiových a sieťových kanálov';
+
+ @override
+ String get radioStats_screenTitle => 'Štatistiky rádiových vysielaní';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Pripojte sa k zariadeniu, aby ste mohli sledovať štatistiky rádiového vysielania.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Statistické údaje z rádia vyžadujú sprievodný softvér verzie v8 alebo novšej.';
+
+ @override
+ String get radioStats_waiting => 'Čakám na údaje…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Úroveň hluku: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Posledný údaj RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Posledná hodnota SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Čas vysielania na TX (celkový): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Čas RX (celkový): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Úroveň šumu (dBm) pre posledné vzorky.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Úroveň hluku: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Získavanie údajov o rádiu…';
+
+ @override
+ String get radioStats_settingsTile => 'Štatistiky rádiových vysielaní';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Úroveň hluku, RSSI, SNR a časové rozloženie';
+
+ @override
+ String get translation_title => 'Preklad';
+
+ @override
+ String get translation_enableTitle => 'Aktivovať preklad';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Prekladajte prichádzajúce správy a umožnite ich preklad pred odoslaním.';
+
+ @override
+ String get translation_composerTitle => 'Preložte pred odeslaním';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Riadi výchoce stav ikony pre preklad, ktorú používa program.';
+
+ @override
+ String get translation_targetLanguage => 'Cieľový jazyk';
+
+ @override
+ String get translation_useAppLanguage => 'Použite jazyk aplikácie';
+
+ @override
+ String get translation_downloadedModelLabel => 'Stiahnutý model';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Prednastavený model od Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel =>
+ 'Odkaz na manuál (v elektronickej forme)';
+
+ @override
+ String get translation_downloadModel => 'Stiahnuť model';
+
+ @override
+ String get translation_downloading => 'Stiahnutie...';
+
+ @override
+ String get translation_working => 'Práca...';
+
+ @override
+ String get translation_stop => 'Zastavte';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Sliečenie stiahnutých častí do konečného súboru...';
+
+ @override
+ String get translation_downloadedModels => 'Stiahnuté modely';
+
+ @override
+ String get translation_deleteModel => 'Odstrániť model';
+
+ @override
+ String get translation_modelDownloaded => 'Model pre preklad bol stiahnutý.';
+
+ @override
+ String get translation_downloadStopped => 'Stiahnutie bolo prerušené.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Neúspešné stiahnutie: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Najprv zadajte URL pre konkrétny model.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Zobraziť PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Skryť PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'PIN pre párovanie cez Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Zadajte PIN pre $deviceName (ak neexistuje, nechajte prázdne).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Preklad textu';
+
+ @override
+ String get translation_translateBeforeSending => 'Preložte pred odeslaním';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Správy budú preložené, než budú odoslané.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Posielajte správy v pôvodnej písanom jazyku.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Preložte do $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Možnosti prekladania';
+
+ @override
+ String get translation_systemLanguage => 'Jazyk systému';
}
diff --git a/lib/l10n/app_localizations_sl.dart b/lib/l10n/app_localizations_sl.dart
index cdcd23c..2d89aa4 100644
--- a/lib/l10n/app_localizations_sl.dart
+++ b/lib/l10n/app_localizations_sl.dart
@@ -12,7 +12,7 @@ class AppLocalizationsSl extends AppLocalizations {
String get appTitle => 'MeshCore Open';
@override
- String get nav_contacts => 'Kontakti';
+ String get nav_contacts => 'Stiki';
@override
String get nav_channels => 'Kanali';
@@ -38,6 +38,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get common_delete => 'Izbrisati';
+ @override
+ String get common_deleteAll => 'Izbriši vse';
+
@override
String get common_close => 'Zapri';
@@ -108,6 +111,133 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Komunicirajte preko protokola TCP';
+
+ @override
+ String get tcpHostLabel => 'IP naslov';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Vrata';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Vnesite končni naslov in se povežite';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Povezava z $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Potrebna je IP-naslov.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Port mora biti med 1 in 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Transport preko protokola TCP ni podprt na tej platformi.';
+
+ @override
+ String get tcpErrorTimedOut => 'Povezava TCP je presegla časovno obdobje.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Napaka pri povezavi TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Povežite preko USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Izberite zaznano serijsko napravo in se neposredno povežite z vašo MeshCore napravo.';
+
+ @override
+ String get usbScreenStatus => 'Izberite USB naprave';
+
+ @override
+ String get usbScreenNote =>
+ 'USB serijska povezava je aktivna na podprtih napravah Android in na desktop platformah.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Niti en USB naprave niso najdeni. Povežite eno in posodobite.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Dovoljenje za dostop preko USB-ja je bilo zavrnjeno.';
+
+ @override
+ String get usbErrorDeviceMissing => 'Izbrani USB napravej je več ne.';
+
+ @override
+ String get usbErrorInvalidPort => 'Izberite veljavno USB naprave.';
+
+ @override
+ String get usbErrorBusy => 'Že je v teku zahteva za povezavo preko USB.';
+
+ @override
+ String get usbErrorNotConnected => 'Ni priklopljenih USB naprave.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Uspešno ni bilo mogo, da se odpre izbran naprave USB.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Niso bilo mogoče uskladiti povezave z izbranim USB napom.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'USB serijska komunikacija ni podprta na tej platformi.';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB povezava je že aktivirana.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Ni bilo izbranega USB naprave.';
+
+ @override
+ String get usbErrorPortClosed => 'USB povezava ni aktivirana.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Vzpostavitve ni bilo mogo. Prosimo, da se prepričate, da ima naprave trenutno nameštan firmware USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Naprave za serijsko komunikacijo preko spleta';
+
+ @override
+ String get usbStatus_notConnected => 'Izberite USB naprave.';
+
+ @override
+ String get usbStatus_connecting => 'Povezava z USB napravo...';
+
+ @override
+ String get usbStatus_searching => 'Iskanje USB naprav...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Napaka pri povezavi preko USB: $error';
+ }
+
@override
String get scanner_scanning => 'Skeniram za naprave...';
@@ -144,7 +274,24 @@ class AppLocalizationsSl extends AppLocalizations {
String get scanner_scan => 'Skeniraj';
@override
- String get device_quickSwitch => 'Hitro preklopiti';
+ String get scanner_bluetoothOff => 'Bluetooth je izklopljen';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Prosimo, vklopite Bluetooth, da lahko poiščete naprave.';
+
+ @override
+ String get scanner_chromeRequired => 'Zahtevan brskalnik Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Ta spletna aplikacija za podporo Bluetooth zahteva Google Chrome ali brskalnik na osnovi Chromiuma.';
+
+ @override
+ String get scanner_enableBluetooth => 'Omogočite Bluetooth';
+
+ @override
+ String get device_quickSwitch => 'Hitro preklop';
@override
String get device_meshcore => 'MeshCore';
@@ -163,16 +310,16 @@ class AppLocalizationsSl extends AppLocalizations {
'Obveščanja, sporoščanje in zemljevidi.';
@override
- String get settings_nodeSettings => 'Nastavitve časa';
+ String get settings_nodeSettings => 'Nastavitev časa';
@override
- String get settings_nodeName => 'Ime omrežno mesto';
+ String get settings_nodeName => 'Ime node-a';
@override
- String get settings_nodeNameNotSet => 'Nezavedeno';
+ String get settings_nodeNameNotSet => 'Ni nastavljeno';
@override
- String get settings_nodeNameHint => 'Vnesite ime časa';
+ String get settings_nodeNameHint => 'Vnesite ime node-a';
@override
String get settings_nodeNameUpdated => 'Ime posodobljeno';
@@ -182,7 +329,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get settings_radioSettingsSubtitle =>
- 'Frekvenca, moč, razširni faktor';
+ 'Frekvenca, moč, razširitveni faktor';
@override
String get settings_radioSettingsUpdated => 'Radio nastavitve posodobljene';
@@ -201,7 +348,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get settings_locationInvalid =>
- 'Neveljna zemeljska širina ali dolžina.';
+ 'Neveljavna zemeljska širina ali dolžina.';
@override
String get settings_locationGPSEnable => 'Omogoči GPS';
@@ -224,7 +371,14 @@ class AppLocalizationsSl extends AppLocalizations {
String get settings_longitude => 'Dolžina';
@override
- String get settings_privacyMode => 'Mod podjetja';
+ String get settings_contactSettings => 'Nastavitve stika';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Nastavitve za dodajanje stikov.';
+
+ @override
+ String get settings_privacyMode => 'Zasebnost';
@override
String get settings_privacyModeSubtitle => 'Skrita imena/lokacije v oglasih';
@@ -234,10 +388,54 @@ class AppLocalizationsSl extends AppLocalizations {
'Omogoči način zasebnosti, da skrijemo tvoje ime in lokacijo v oglasih.';
@override
- String get settings_privacyModeEnabled => 'Privatni režim je omogočen.';
+ String get settings_privacyModeEnabled => 'Privatni način je omogočen.';
@override
- String get settings_privacyModeDisabled => 'Privatni režim je onemogočen.';
+ String get settings_privacyModeDisabled => 'Privatni način je onemogočen.';
+
+ @override
+ String get settings_privacy => 'Nastavitve zasebnosti';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Kontrolirajte, katere informacije so deljene.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Izberite, katere informacije vaš naprava deli z drugimi.';
+
+ @override
+ String get settings_denyAll => 'Zavrniti vse';
+
+ @override
+ String get settings_allowByContact => 'Dovoli po kontaktnih zastavah';
+
+ @override
+ String get settings_allowAll => 'Dovoli vse';
+
+ @override
+ String get settings_telemetryBaseMode => 'Osnovni način telemetrije';
+
+ @override
+ String get settings_telemetryLocationMode => 'Način delovanja telemetrije';
+
+ @override
+ String get settings_telemetryEnvironmentMode =>
+ 'Način delovanja okolja telemetrije';
+
+ @override
+ String get settings_advertLocation => 'Lokacija oglasa';
+
+ @override
+ String get settings_advertLocationSubtitle => 'Vključi lokacijo v oglas.';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Večkratni potrditvi: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Način telemetrije posodobljen';
@override
String get settings_actions => 'Akcije';
@@ -253,47 +451,46 @@ class AppLocalizationsSl extends AppLocalizations {
String get settings_advertisementSent => 'Oglas poslan';
@override
- String get settings_syncTime => 'Ugasniti čas';
+ String get settings_syncTime => 'Nastavi uro';
@override
- String get settings_syncTimeSubtitle => 'Nastavi uro naprave v čas telefona';
+ String get settings_syncTimeSubtitle => 'Nastavi uro naprave na čas telefona';
@override
- String get settings_timeSynchronized => 'Sinhronizirano po času';
+ String get settings_timeSynchronized => 'Ura sinhronizirana';
@override
String get settings_refreshContacts => 'Ponovno obišči kontakte';
@override
String get settings_refreshContactsSubtitle =>
- 'Ponovno naloži seznam kontaktov iz naprave';
+ 'Ponovno naloži seznam stikov v napravi';
@override
- String get settings_rebootDevice => 'Restart Naprave';
+ String get settings_rebootDevice => 'Ponovni zagon naprave';
@override
- String get settings_rebootDeviceSubtitle =>
- 'Ponovite zažetek naprave MeshCore';
+ String get settings_rebootDeviceSubtitle => 'Ponovno zaženi MeshCore napravo';
@override
String get settings_rebootDeviceConfirm =>
- 'Ste prepričani, da želite ponovno zagon napravke? Boste odvisni od omrežja.';
+ 'Ste prepričani, da želite ponovno zagnati napravo? Povezava bo prekinjena.';
@override
- String get settings_debug => 'Napravi popravek';
+ String get settings_debug => 'Debug';
@override
- String get settings_bleDebugLog => 'Logarjev zapis BLE';
+ String get settings_bleDebugLog => 'BLE debug log (razhroščevanje)';
@override
String get settings_bleDebugLogSubtitle =>
- 'Navodila BLE, odgovori in surovo podatkovno';
+ 'BLE ukazi, odgovori in surovi podatki';
@override
- String get settings_appDebugLog => 'Log zapiske aplikacije';
+ String get settings_appDebugLog => 'Logi aplikacije';
@override
- String get settings_appDebugLogSubtitle => 'Prijavni sporočila aplikacije';
+ String get settings_appDebugLogSubtitle => 'Debug sporočila aplikacije';
@override
String get settings_about => 'Oglejte si';
@@ -304,11 +501,15 @@ class AppLocalizationsSl extends AppLocalizations {
}
@override
- String get settings_aboutLegalese => 'MeshCore Odprtokodni Projekt 2024';
+ String get settings_aboutLegalese => 'Odprtokodni projekt MeshCore 2024';
@override
String get settings_aboutDescription =>
- 'Odprtokodni Flutter kličnik za naprave za LoRa mrežo MeshCore.';
+ 'Odprtokodni Flutter klient za naprave za LoRa omrežje MeshCore.';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Podatki o višini LOS: Open-Meteo (CC BY 4.0)';
@override
String get settings_infoName => 'Ime';
@@ -323,10 +524,10 @@ class AppLocalizationsSl extends AppLocalizations {
String get settings_infoBattery => 'Baterija';
@override
- String get settings_infoPublicKey => 'Ključ javnega tipa';
+ String get settings_infoPublicKey => 'Javni ključ';
@override
- String get settings_infoContactsCount => 'Število kontaktov';
+ String get settings_infoContactsCount => 'Število stikov';
@override
String get settings_infoChannelCount => 'Število kanalov';
@@ -334,15 +535,6 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get settings_presets => 'Prednastavitve';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frekvenca (MHz)';
@@ -350,7 +542,7 @@ class AppLocalizationsSl extends AppLocalizations {
String get settings_frequencyHelper => '300,00 - 2500,00';
@override
- String get settings_frequencyInvalid => 'Neveljčna frekvenca (300-2500 MHz)';
+ String get settings_frequencyInvalid => 'Neveljavna frekvenca (300-2500 MHz)';
@override
String get settings_bandwidth => 'Pasovna širina';
@@ -368,13 +560,18 @@ class AppLocalizationsSl extends AppLocalizations {
String get settings_txPowerHelper => '0 - 22';
@override
- String get settings_txPowerInvalid => 'Neveljaven TX moč (0-22 dBm)';
+ String get settings_txPowerInvalid => 'Neveljavna TX moč (0-22 dBm)';
@override
- String get settings_longRange => 'Dolenje območje';
+ String get settings_clientRepeat => 'Neovadno ponavljanje';
@override
- String get settings_fastSpeed => 'Hitra hitrost';
+ String get settings_clientRepeatSubtitle =>
+ 'Omogočite temu naprave, da ponavlja paketne sporočila za druge.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Za ponovni prenos na brezžični način so potrebne frekvence 433, 869 ali 918 MHz.';
@override
String settings_error(String message) {
@@ -391,10 +588,10 @@ class AppLocalizationsSl extends AppLocalizations {
String get appSettings_theme => 'Tema';
@override
- String get appSettings_themeSystem => 'Predpomnilnik sistema';
+ String get appSettings_themeSystem => 'Sistemska tema';
@override
- String get appSettings_themeLight => 'Luč';
+ String get appSettings_themeLight => 'Svetlo';
@override
String get appSettings_themeDark => 'Temno';
@@ -445,10 +642,23 @@ class AppLocalizationsSl extends AppLocalizations {
String get appSettings_languageBg => 'Български';
@override
- String get appSettings_notifications => 'Obveščanja';
+ String get appSettings_languageRu => 'Ruščina';
@override
- String get appSettings_enableNotifications => 'Omogoči obveščanje';
+ String get appSettings_languageUk => 'Ukrajinsko';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Omogoči sledenje sporočilom';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Prikaži podrobne metapodatke o usmerjanju in časovnem usklajevanju sporočil';
+
+ @override
+ String get appSettings_notifications => 'Obvestila';
+
+ @override
+ String get appSettings_enableNotifications => 'Omogoči obvestila';
@override
String get appSettings_enableNotificationsSubtitle =>
@@ -484,7 +694,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get appSettings_advertisementNotificationsSubtitle =>
- 'Pokaži obvestilo, ko so novi vozlišči odkrivljeni.';
+ 'Pokaži obvestilo, ko so najdene nove naprave.';
@override
String get appSettings_messaging => 'Komuniciranje';
@@ -499,18 +709,19 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get appSettings_pathsWillBeCleared =>
- 'Potnice bodo očiščene po 5 neuspešnih poskusih.';
+ 'Počisti pot po 5 neuspešnih poskusih.';
@override
String get appSettings_pathsWillNotBeCleared =>
- 'Potniški poti ne bodo samodejno čiščeni.';
+ 'Poti ne bodo samodejno čiščene.';
@override
- String get appSettings_autoRouteRotation => 'Avtomatsko Občutke in Rotacije';
+ String get appSettings_autoRouteRotation =>
+ 'Avtomatsko rotacija prenosne poti';
@override
String get appSettings_autoRouteRotationSubtitle =>
- 'Med spreminjanjem med najboljšimi potmi in plovilnim načinom';
+ 'Menjaj med boljšo potjo in flood načinom';
@override
String get appSettings_autoRouteRotationEnabled =>
@@ -520,20 +731,63 @@ class AppLocalizationsSl extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Samodejno krmilno rotiranje je onemogočeno';
+ @override
+ String get appSettings_maxRouteWeight => 'Največja dovoljena teža poti';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Največja teža, ki jo lahko pot doseže s uspešnimi dostavnami.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Izvirna teža poti';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Izguba teže za nove, odkriti poti';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Učinkovitost: povečanje';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Težava, dodana poti po uspešni dostavi';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Zmanjšanje teže, ki je povezana s pomanjkanjem';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Težo, ki ni bila uspešno dostavljena, odstranili s poti.';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Najve število poskusov pošiljanja sporočil';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Število poskusov ponovnega poslanja, preden se sporočilo označuje kot neuspešno';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Baterija';
@override
- String get appSettings_batteryChemistry => 'Razem z možnostmi';
+ String get appSettings_batteryChemistry => 'Kemija baterije';
@override
String appSettings_batteryChemistryPerDevice(String deviceName) {
- return 'Nastavitve za naprave ($deviceName)';
+ return 'Nastavitev za napravo ($deviceName)';
}
@override
String get appSettings_batteryChemistryConnectFirst =>
- 'Povežite se z napravo za izbiro';
+ 'Za izbiro se poveži z napravo';
@override
String get appSettings_batteryNmc => '18650 NMC (3,0-4,2V)';
@@ -545,52 +799,51 @@ class AppLocalizationsSl extends AppLocalizations {
String get appSettings_batteryLipo => 'LiPo (3,0-4,2V)';
@override
- String get appSettings_mapDisplay => 'Prikaz zemljevide';
+ String get appSettings_mapDisplay => 'Prikaz zemljevida';
@override
- String get appSettings_showRepeaters => 'Prikaži ponovitve';
+ String get appSettings_showRepeaters => 'Prikaži repetitorje';
@override
- String get appSettings_showRepeatersSubtitle =>
- 'Prikaži ponovljalne notranjosti na zemljeploscu';
+ String get appSettings_showRepeatersSubtitle => 'Prikaži repetitorje na mapi';
@override
- String get appSettings_showChatNodes => 'Prikaži čakalne notranjosti';
+ String get appSettings_showChatNodes => 'Prikaži naprave za klepet';
@override
String get appSettings_showChatNodesSubtitle =>
- 'Prikaži pogovorni pike na zemljeploscu';
+ 'Prikaži naprave na zemljevidu';
@override
- String get appSettings_showOtherNodes => 'Pokaži druge vozlišča';
+ String get appSettings_showOtherNodes => 'Pokaži druge naprave';
@override
String get appSettings_showOtherNodesSubtitle =>
- 'Pokaži druge vrste notranjih elementov na zemljevalu.';
+ 'Pokaži druge vrste naprav na zemljevidu.';
@override
- String get appSettings_timeFilter => 'Filtri po času';
+ String get appSettings_timeFilter => 'Filter po času';
@override
- String get appSettings_timeFilterShowAll => 'Pokaži vse notranje elemente';
+ String get appSettings_timeFilterShowAll => 'Pokaži vse naprave';
@override
String appSettings_timeFilterShowLast(int hours) {
- return 'Pokaži notranjosti iz zadnjih $hours ur';
+ return 'Pokaži naprave v zadnjih $hours urah';
}
@override
- String get appSettings_mapTimeFilter => 'Filtri časa zemljevida';
+ String get appSettings_mapTimeFilter => 'Filter časa na zemljevidu';
@override
String get appSettings_showNodesDiscoveredWithin =>
- 'Pokaži notranje čepke, odkrivene v:';
+ 'Pokaži naprave odkrite v:';
@override
- String get appSettings_allTime => 'Vse čase';
+ String get appSettings_allTime => 'Brez omejitev';
@override
- String get appSettings_lastHour => 'Minuto nazaj';
+ String get appSettings_lastHour => 'V zadnji uri';
@override
String get appSettings_last6Hours => 'Zadnjih 6 ur';
@@ -599,13 +852,22 @@ class AppLocalizationsSl extends AppLocalizations {
String get appSettings_last24Hours => 'Zadnjih 24 ur';
@override
- String get appSettings_lastWeek => 'Lepošno';
+ String get appSettings_lastWeek => 'Prejšnji teden';
@override
- String get appSettings_offlineMapCache => 'Omrezni Poudni Arhiv';
+ String get appSettings_offlineMapCache => 'Shramba zemljevidov brez povezave';
@override
- String get appSettings_noAreaSelected => 'Nizkana označena površina';
+ String get appSettings_unitsTitle => 'Enote';
+
+ @override
+ String get appSettings_unitsMetric => 'Metrična (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperialno (ft / mi)';
+
+ @override
+ String get appSettings_noAreaSelected => 'Območje ni izbrano';
@override
String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
@@ -613,136 +875,201 @@ class AppLocalizationsSl extends AppLocalizations {
}
@override
- String get appSettings_debugCard => 'Napravi popravek';
+ String get appSettings_debugCard => 'Razhroščevanje';
@override
- String get appSettings_appDebugLogging => 'Programski Log';
+ String get appSettings_appDebugLogging => 'Programski dnevnik';
@override
String get appSettings_appDebugLoggingSubtitle =>
- 'Log aplikacijske debug sporočila za odpravljanje težav';
+ 'Dnevnik debug sporočil za odpravljanje težav';
@override
String get appSettings_appDebugLoggingEnabled =>
- 'Omogočeno zaznamovanje napak v aplikaciji';
+ 'Beleženje napak v aplikaciji omogočeno';
@override
String get appSettings_appDebugLoggingDisabled =>
- 'Programski logi aplikacije so onemogočeni.';
+ 'Beleženje napak v aplikacije onemogočeno.';
@override
- String get contacts_title => 'Kontakti';
+ String get contacts_title => 'Stiki';
@override
- String get contacts_noContacts => 'Še ni kontaktov.';
+ String get contacts_noContacts => 'Ni stikov.';
@override
String get contacts_contactsWillAppear =>
- 'Kontakti se bodo prikazali, ko naprave oglasijo.';
+ 'Stiki se bodo prikazali, ko se naprave oglasijo.';
@override
- String get contacts_searchContacts => 'Iskanje kontaktov...';
+ String get contacts_unread => 'Neprebrano';
@override
- String get contacts_noUnreadContacts => 'Nerešeno kontaktov.';
+ String get contacts_searchContactsNoNumber => 'Iskanje stikov...';
@override
- String get contacts_noContactsFound =>
- 'Niti ena oseba ali skupine ni najdena.';
-
- @override
- String get contacts_deleteContact => 'Izbrisati Kontakt';
-
- @override
- String contacts_removeConfirm(String contactName) {
- return 'Izbrisati $contactName iz kontaktov?';
+ String contacts_searchContacts(int number, String str) {
+ return 'Iskanje stikov...';
}
@override
- String get contacts_manageRepeater => 'Upravljajte Ponovitve';
+ String contacts_searchFavorites(int number, String str) {
+ return 'Iskanje $number$str priljubljenih...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Išči $number$str uporabnikov...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Išči $number$str ponavljalnike...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Išči $number$str strežnikov sob...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => 'Ne prebrani stiki.';
+
+ @override
+ String get contacts_noContactsFound => 'Stiki niso najdeni.';
+
+ @override
+ String get contacts_deleteContact => 'Izbriši stik';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return 'Izbrišem $contactName iz stikov?';
+ }
+
+ @override
+ String get contacts_manageRepeater => 'Upravljaj Ponovitve';
@override
String get contacts_manageRoom => 'Upravljajte strežnik sobe';
@override
- String get contacts_roomLogin => 'Vnos v sobo';
+ String get contacts_roomLogin => 'Prijava v sobo';
@override
- String get contacts_openChat => 'Odprta kleta';
+ String get contacts_openChat => 'Odpri klepet';
@override
- String get contacts_editGroup => 'Uredi Skupino';
+ String get contacts_editGroup => 'Uredi skupino';
@override
- String get contacts_deleteGroup => 'Izbrisati Skupino';
+ String get contacts_deleteGroup => 'Izbriši skupino';
@override
String contacts_deleteGroupConfirm(String groupName) {
- return 'Odpovedati $groupName?';
+ return 'Izbriši $groupName?';
}
@override
- String get contacts_newGroup => 'Novo skupino';
+ String get contacts_newGroup => 'Nova skupina';
@override
- String get contacts_groupName => 'Skupina imena';
+ String get contacts_groupName => 'Ime skupine';
@override
String get contacts_groupNameRequired => 'Ime skupine je obvezno.';
+ @override
+ String get contacts_groupNameReserved => 'To ime skupine je rezervirano';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Skupina \"$name\" že obstaja';
}
@override
- String get contacts_filterContacts => 'Filtri kontakt\\,...';
+ String get contacts_filterContacts => 'Filtriraj stik\\,...';
@override
String get contacts_noContactsMatchFilter =>
- 'Niti ena oseba ne ustreza vašemu kriteriju.';
+ 'Noben stik ne ustreza vašemu kriteriju.';
@override
- String get contacts_noMembers => 'Nič članov.';
+ String get contacts_noMembers => 'Ni članov.';
@override
- String get contacts_lastSeenNow => 'Datum zadnjega vpisa zdaj';
+ String get contacts_lastSeenNow => 'Nazadnje viden zdaj';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return 'Zadnjič videti $minutes minut nazaj';
+ return 'Zadnjič viden pred $minutes minutami';
}
@override
- String get contacts_lastSeenHourAgo => 'Zadnjič ogledan pred 1 uro.';
+ String get contacts_lastSeenHourAgo => 'Zadnjič viden pred 1 uro.';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return 'Zadnjič videti $hours ur nazaj';
+ return 'Zadnjič viden pred $hours urami';
}
@override
- String get contacts_lastSeenDayAgo => 'Zadnjič ogledan pred 1 dnem';
+ String get contacts_lastSeenDayAgo => 'Zadnjič viden pred 1 dnem';
@override
String contacts_lastSeenDaysAgo(int days) {
- return 'Zadnjič videti $days dni nazaj';
+ return 'Zadnjič viden pred $days dnem';
}
+ @override
+ String get contact_info => 'Kontaktni podatki';
+
+ @override
+ String get contact_settings => 'Nastavitve stika';
+
+ @override
+ String get contact_telemetry => 'Telemetrija';
+
+ @override
+ String get contact_lastSeen => 'Zadnjič videno';
+
+ @override
+ String get contact_clearChat => 'Počisti klepet';
+
+ @override
+ String get contact_teleBase => 'Baza telemetrije';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Dovoli deljenje stanja baterije in osnovne telemetrije';
+
+ @override
+ String get contact_teleLoc => 'Lokacija telemetrije';
+
+ @override
+ String get contact_teleLocSubtitle => 'Dovoli deljenje podatkov o lokaciji';
+
+ @override
+ String get contact_teleEnv => 'Okolje telemetrije';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Dovoli deljenje podatkov okoljskih senzorjev';
+
@override
String get channels_title => 'Kanali';
@override
- String get channels_noChannelsConfigured => 'Nekonfigurirane kanale';
+ String get channels_noChannelsConfigured => 'Kanali še niso konfigurirani';
@override
- String get channels_addPublicChannel => 'Dodaj Objavni Kanal';
+ String get channels_addPublicChannel => 'Dodaj javni kanal';
@override
String get channels_searchChannels => 'Poišči kanale...';
@override
- String get channels_noChannelsFound => 'Niti kanalov najti ni.';
+ String get channels_noChannelsFound => 'Ne najdem kanalov.';
@override
String channels_channelIndex(int index) {
@@ -753,26 +1080,37 @@ class AppLocalizationsSl extends AppLocalizations {
String get channels_hashtagChannel => 'Hashtag kanal';
@override
- String get channels_public => 'javno';
+ String get channels_public => 'Javni';
@override
- String get channels_private => 'Zasebno';
+ String get channels_private => 'Zasebni';
@override
- String get channels_publicChannel => 'Ogljišna skupina';
+ String get channels_publicChannel => 'Javni kanal';
@override
- String get channels_privateChannel => 'Zatemniščen kanal';
+ String get channels_privateChannel => 'Zasebni kanal';
@override
String get channels_editChannel => 'Uredi kanal';
+ @override
+ String get channels_muteChannel => 'Utišaj kanal';
+
+ @override
+ String get channels_unmuteChannel => 'Vklopi obvestila kanala';
+
@override
String get channels_deleteChannel => 'Pošlji kanal';
@override
String channels_deleteChannelConfirm(String name) {
- return 'Izbrisati \"$name\"? To se ne da povrniti.';
+ return 'Izbrišem \"$name\"? To se ne da povrniti.';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Kanala $name ni bilo mogoče izbrisati';
}
@override
@@ -886,6 +1224,14 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get chat_noMessages => 'Še ni sporočil.';
+ @override
+ String get chat_sendMessage => 'Pošlji sporočilo';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Pošlji sporočilo $contactName';
+ }
+
@override
String get chat_sendMessageToStart => 'Pošlji sporočilo za začetek.';
@@ -907,26 +1253,21 @@ class AppLocalizationsSl extends AppLocalizations {
String get chat_location => 'Lokacija';
@override
- String chat_sendMessageTo(String contactName) {
- return 'Pošlji sporočilo $contactName';
- }
-
- @override
- String get chat_typeMessage => 'Vnesite sporočilo...';
+ String get chat_typeMessage => 'Vnesi sporočilo...';
@override
String chat_messageTooLong(int maxBytes) {
- return 'Pošiljanje sporočila je onemogočeno, saj je preveliko (maksimalno $maxBytes bajt).';
+ return 'Pošiljanje sporočila je onemogočeno, saj je preveliko (maksimalno $maxBytes byte-ov).';
}
@override
- String get chat_messageCopied => 'Pošljeno sporočilo';
+ String get chat_messageCopied => 'Sporočilo poslano';
@override
- String get chat_messageDeleted => 'Pošiljanje sporočila izbrisano';
+ String get chat_messageDeleted => 'Sporočilo izbrisano';
@override
- String get chat_retryingMessage => 'Ponovna poskus.';
+ String get chat_retryingMessage => 'Ponovni poskus.';
@override
String chat_retryCount(int current, int max) {
@@ -937,10 +1278,10 @@ class AppLocalizationsSl extends AppLocalizations {
String get chat_sendGif => 'Pošlji GIF';
@override
- String get chat_reply => 'Odpošlji';
+ String get chat_reply => 'Odgovori';
@override
- String get chat_addReaction => 'Dodaj Reakcijo';
+ String get chat_addReaction => 'Dodaj reakcijo';
@override
String get chat_me => 'jaz';
@@ -961,19 +1302,19 @@ class AppLocalizationsSl extends AppLocalizations {
String get gifPicker_title => 'Izberi GIF';
@override
- String get gifPicker_searchHint => 'Iskalite GIF-e...';
+ String get gifPicker_searchHint => 'Išči GIF-e...';
@override
- String get gifPicker_poweredBy => 'Naprodno z GIPHY';
+ String get gifPicker_poweredBy => 'Napredno z GIPHY';
@override
- String get gifPicker_noGifsFound => 'Niti GIF-jev najti ni.';
+ String get gifPicker_noGifsFound => 'Ne najdem GIF-ov.';
@override
- String get gifPicker_failedLoad => 'Neuspešno je naložilo GIF-e';
+ String get gifPicker_failedLoad => 'Neuspešno nalaganje GIF-a';
@override
- String get gifPicker_failedSearch => 'Posodobit neuspešno.';
+ String get gifPicker_failedSearch => 'Iskanje neuspešno.';
@override
String get gifPicker_noInternet => 'Ni internetne povezave';
@@ -982,35 +1323,35 @@ class AppLocalizationsSl extends AppLocalizations {
String get debugLog_appTitle => 'Log zapiske aplikacije';
@override
- String get debugLog_bleTitle => 'Logarjev zapis BLE';
+ String get debugLog_bleTitle => 'Log zapis BLE';
@override
- String get debugLog_copyLog => 'Kopiraj zapiske';
+ String get debugLog_copyLog => 'Kopiraj dnevnik';
@override
- String get debugLog_clearLog => 'Pasters log';
+ String get debugLog_clearLog => 'Briši log';
@override
- String get debugLog_copied => 'Kopirana belež poteka.';
+ String get debugLog_copied => 'Beležka kopirana.';
@override
- String get debugLog_bleCopied => 'Kopirana beležke iz BLE';
+ String get debugLog_bleCopied => 'Kopirana beležka iz BLE';
@override
- String get debugLog_noEntries => 'Še ni ustvarjenih debug zapisov.';
+ String get debugLog_noEntries => 'Ni ustvarjenih debug zapisov.';
@override
String get debugLog_enableInSettings =>
- 'Omogoči beleženje napak v aplikaciji v nastavitvah';
+ 'Omogoči beleženje napak v nastavitvah aplikacije';
@override
- String get debugLog_frames => 'Okna';
+ String get debugLog_frames => 'Okvirji';
@override
String get debugLog_rawLogRx => 'Svež Log-RX';
@override
- String get debugLog_noBleActivity => 'Šele začnite z aktivnostjo BLE.';
+ String get debugLog_noBleActivity => 'Ni BLE aktivnosti.';
@override
String debugFrame_length(int count) {
@@ -1062,6 +1403,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get chat_pathManagement => 'Upravljanje poti';
+ @override
+ String get chat_ShowAllPaths => 'Prikaži vse poti';
+
@override
String get chat_routingMode => 'Navodilo za usmerjevalni način';
@@ -1079,10 +1423,10 @@ class AppLocalizationsSl extends AppLocalizations {
'Zapiske o poti so popolni. Izbriši vnose, da dodaš nove.';
@override
- String get chat_hopSingular => 'skoč';
+ String get chat_hopSingular => 'skok';
@override
- String get chat_hopPlural => 'škrabec';
+ String get chat_hopPlural => 'skokov';
@override
String chat_hopsCount(int count) {
@@ -1103,7 +1447,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get chat_noPathHistoryYet =>
- 'Še ni shranjenih poti.\nPošlji sporočilo za odkrivanje poti.';
+ 'Ni shranjenih poti.\nPošlji sporočilo za odkrivanje poti.';
@override
String get chat_pathActions => 'Potni ukazi:';
@@ -1115,7 +1459,7 @@ class AppLocalizationsSl extends AppLocalizations {
String get chat_setCustomPathSubtitle => 'Ročno določite potniško pot.';
@override
- String get chat_clearPath => 'Čista pot';
+ String get chat_clearPath => 'Počisti pot';
@override
String get chat_clearPathSubtitle => 'Ob naslednji pošiljanju znova zbrati.';
@@ -1133,7 +1477,7 @@ class AppLocalizationsSl extends AppLocalizations {
'Narejena je bila omrežna modaliteta. Vklopi jo znova preko ikone v meniju aplikacije.';
@override
- String get chat_fullPath => 'Polni pot';
+ String get chat_fullPath => 'Polna pot';
@override
String get chat_pathDetailsNotAvailable =>
@@ -1197,9 +1541,33 @@ class AppLocalizationsSl extends AppLocalizations {
return 'Nerešeno: $count';
}
+ @override
+ String get chat_openLink => 'Odpreti povezavo?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Ali želite odpreti to povezavo v brskalniku?';
+
+ @override
+ String get chat_open => 'Odpri';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Povezave ni bilo mogoče odpreti: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Neveljavna oblika povezave';
+
@override
String get map_title => 'Mapa omrežja';
+ @override
+ String get map_lineOfSight => 'Linija vida';
+
+ @override
+ String get map_losScreenTitle => 'Linija vida';
+
@override
String get map_noNodesWithLocation =>
'Nihče od notranjih elementov nima podatkov o lokaciji.';
@@ -1258,6 +1626,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Delite točke tukaj.';
+ @override
+ String get map_setAsMyLocation => 'Nastavite to kot mojo lokacijo';
+
@override
String get map_pinLabel => 'Oznaka za pritrditev';
@@ -1303,6 +1674,9 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get map_otherNodes => 'Druge vozlišča';
+ @override
+ String get map_showOverlaps => 'Prekrivanje ključa ponovnega predvajanja';
+
@override
String get map_keyPrefix => 'Predpona ključa';
@@ -1318,6 +1692,15 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Pokaži skupno označenja';
+ @override
+ String get map_showGuessedLocations => 'Pokaži lokacije domnevnih not.';
+
+ @override
+ String get map_showDiscoveryContacts => 'Prikaži odkritja kontaktov';
+
+ @override
+ String get map_guessedLocation => 'Predpostavljena lokacija';
+
@override
String get map_lastSeenTime => 'Datum zadnjega vpogleda';
@@ -1330,6 +1713,21 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get map_manageRepeater => 'Upravljajte Ponovitve';
+ @override
+ String get map_tapToAdd => 'Pritisnite na vozlišča, da jih dodate poti.';
+
+ @override
+ String get map_runTrace => 'Zaženi sledenje poti';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Vrni se nazaj po isti poti.';
+
+ @override
+ String get map_removeLast => 'Odstrani Zadnji';
+
+ @override
+ String get map_pathTraceCancelled => 'Spremljanje poti je prekinjeno.';
+
@override
String get mapCache_title =>
'Omrezni predpomnilnik zemljeških zemljejevskih slik';
@@ -1603,9 +2001,18 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get room_management => 'Upravljanje stremlišča';
+ @override
+ String get repeater_guest => 'Informacije o ponovljalniku';
+
+ @override
+ String get room_guest => 'Informacije o strežniku';
+
@override
String get repeater_managementTools => 'Upravne orodje';
+ @override
+ String get repeater_guestTools => 'Naložila za goste';
+
@override
String get repeater_status => 'Status';
@@ -1628,10 +2035,10 @@ class AppLocalizationsSl extends AppLocalizations {
'Pošlji ukazne povelje na ponovitveno enoto.';
@override
- String get repeater_neighbours => 'Sosedi';
+ String get repeater_neighbors => 'Sosedi';
@override
- String get repeater_neighboursSubtitle => 'Pogledati nič sosednjih hopjev.';
+ String get repeater_neighborsSubtitle => 'Pogledati nič sosednjih hopjev.';
@override
String get repeater_settings => 'Nastavitve';
@@ -1640,6 +2047,13 @@ class AppLocalizationsSl extends AppLocalizations {
String get repeater_settingsSubtitle =>
'Konfigurirajte parametre ponovitelja';
+ @override
+ String get repeater_clockSyncAfterLogin => 'Sinhronizacija ure po prijavi';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Samodejno po uspešnem vstopu pošljite obvestilo o sinhronizaciji časa.';
+
@override
String get repeater_statusTitle => 'Status ponovitelja';
@@ -1994,13 +2408,13 @@ class AppLocalizationsSl extends AppLocalizations {
}
@override
- String get repeater_cliQuickGetName => 'Dobiti ime';
+ String get repeater_cliQuickGetName => 'Pridobi ime';
@override
String get repeater_cliQuickGetRadio => 'Dobiti Radiopravo';
@override
- String get repeater_cliQuickGetTx => 'Dobiti TX';
+ String get repeater_cliQuickGetTx => 'Pridobi TX';
@override
String get repeater_cliQuickNeighbors => 'Sosedi';
@@ -2012,7 +2426,13 @@ class AppLocalizationsSl extends AppLocalizations {
String get repeater_cliQuickAdvertise => 'Oglasite';
@override
- String get repeater_cliQuickClock => 'Urnik';
+ String get repeater_cliQuickClock => 'Ura';
+
+ @override
+ String get repeater_cliQuickClockSync => 'Usklajevanje ure';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Odkrijte sosede';
@override
String get repeater_cliHelpAdvert => 'Pošlje paket oglasov';
@@ -2135,7 +2555,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get repeater_cliHelpSetPerm =>
- 'Modificira ACL. Odstrani ustreznu vnos (po predponi pubkeyja), če je \"permissions\" enako nič. Dodaja nov vnos, če je pubkey-hex v celoti in trenutno ni v ACL. Posodobi vnos po ustreznem predponi pubkeyja. Bitje dovoljenj se razlikuje glede na firmware vlogo, vendar so prvi dve bitki: 0 (Gost), 1 (Lezenje samo), 2 (Lezenje in pisanje), 3 (Administrator).';
+ 'Modificira ACL. Odstrani ustrezen vnos (po predponi pubkeyja), če je \"permissions\" enako nič. Dodaja nov vnos, če je pubkey-hex v celoti in trenutno ni v ACL. Posodobi vnos po ustreznem predponi pubkeyja. Bitje dovoljenj se razlikuje glede na firmware vlogo, vendar so prvi dve bitki: 0 (Gost), 1 (Lezenje samo), 2 (Lezenje in pisanje), 3 (Administrator).';
@override
String get repeater_cliHelpGetBridgeType =>
@@ -2243,11 +2663,11 @@ class AppLocalizationsSl extends AppLocalizations {
String get repeater_logging => 'Logiranje';
@override
- String get repeater_neighborsRepeaterOnly => 'Sosedi (le za ponovitelja)';
+ String get repeater_neighborsRepeaterOnly => 'Sosedi (le za repetitorje)';
@override
String get repeater_regionManagementRepeaterOnly =>
- 'Upravljanje regij (zgolj za ponovitve)';
+ 'Upravljanje regij (zgolj za repetitorje)';
@override
String get repeater_regionNote =>
@@ -2327,7 +2747,7 @@ class AppLocalizationsSl extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Ponovitve Sosedi';
+ String get neighbors_repeatersNeighbors => 'Ponovitve Sosedi';
@override
String get neighbors_noData => 'Niso na voljo podatki o sosedih.';
@@ -2362,13 +2782,13 @@ class AppLocalizationsSl extends AppLocalizations {
String get channelPath_messageDetails => 'Podrobnosti sporočila';
@override
- String get channelPath_senderLabel => 'Pošiljalec';
+ String get channelPath_senderLabel => 'Pošiljatelj';
@override
- String get channelPath_timeLabel => 'Čas';
+ String get channelPath_timeLabel => 'Ura';
@override
- String get channelPath_repeatsLabel => 'Ponovi';
+ String get channelPath_repeatsLabel => 'Ponovitve';
@override
String channelPath_pathLabel(int index) {
@@ -2533,17 +2953,17 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get community_scanOrCreate =>
- 'Skenirajte QR kodo ali ustvarite skupnost za začetek.';
+ 'Skeniraj QR kodo ali ustvari skupnost za začetek.';
@override
- String get community_manageCommunities => 'Upravljajte skupnosti';
+ String get community_manageCommunities => 'Upravljanje skupnosti';
@override
String get community_delete => 'Opusti skupnost';
@override
String community_deleteConfirm(String name) {
- return 'Zapustiti \"$name\"?';
+ return 'Zapusti \"$name\"?';
}
@override
@@ -2557,36 +2977,36 @@ class AppLocalizationsSl extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Ponovno ustvari geslo';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Preberite novo tajno geslo za \"$name\"? Vsi članici morajo prebrati novo QR kodo, da lahko nadaljujejo s komunikacijo.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Preberi znova';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Geslo za \"$name\" ponovno ustvarjeno';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Ažuriraj ključ';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Skrivnostno spremembo za \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Skeniraj novo QR kodo za posodabljanje ključa za $name';
}
@override
- String get community_addHashtagChannel => 'Dodaj Oznako Obštnine';
+ String get community_addHashtagChannel => 'Dodaj hashtag kanal';
@override
String get community_addHashtagChannelDesc =>
@@ -2600,7 +3020,7 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get community_regularHashtagDesc =>
- 'javna oznaka (kateri koli lahko sodelujejo)';
+ 'javna oznaka (kdorkoli lahko sodeluje)';
@override
String get community_communityHashtag => 'Skupnostni hashtag';
@@ -2635,6 +3055,15 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get listFilter_all => 'Vse';
+ @override
+ String get listFilter_favorites => 'Priljubljene';
+
+ @override
+ String get listFilter_addToFavorites => 'Dodaj v priljubljene';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Odstrani iz priljubljenih';
+
@override
String get listFilter_users => 'Uporabniki';
@@ -2649,4 +3078,610 @@ class AppLocalizationsSl extends AppLocalizations {
@override
String get listFilter_newGroup => 'Nova skupina';
+
+ @override
+ String get pathTrace_you => 'Ti';
+
+ @override
+ String get pathTrace_failed => 'Sledenje poti ni uspelo.';
+
+ @override
+ String get pathTrace_notAvailable => 'Potni sled ni na voljo.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Osveži Path Trace.';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Ena ali več hmelju manjka lokacija!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Počisti pot';
+
+ @override
+ String get losSelectStartEnd => 'Izberite začetno in končno vozlišče za LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Preverjanje vidnega polja ni uspelo: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Počisti vse točke';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Zaženite LOS za ogled višinskega profila';
+
+ @override
+ String get losMenuTitle => 'LOS meni';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Tapnite vozlišča ali dolgo pritisnite na zemljevid za točke po meri';
+
+ @override
+ String get losShowDisplayNodes => 'Pokaži prikazna vozlišča';
+
+ @override
+ String get losCustomPoints => 'Točke po meri';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Po meri $index';
+ }
+
+ @override
+ String get losPointA => 'Točka A';
+
+ @override
+ String get losPointB => 'Točka B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antena A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antena B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Zaženi LOS';
+
+ @override
+ String get losNoElevationData => 'Ni podatkov o višini';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, čisti LOS, najmanjša razdalja $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blokiral $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: preverjam ...';
+
+ @override
+ String get losStatusNoData => 'LOS: ni podatkov';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total jasno, $blocked blokirano, $unknown neznano';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Podatki o nadmorski višini niso na voljo za enega ali več vzorcev.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Neveljavni podatki o točkah/višini za izračun LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Preimenujte točko po meri';
+
+ @override
+ String get losPointName => 'Ime točke';
+
+ @override
+ String get losShowPanelTooltip => 'Pokaži ploščo LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Skrij ploščo LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Podatki o višini: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Radijski horizont';
+
+ @override
+ String get losLegendLosBeam => 'Linija vidnosti';
+
+ @override
+ String get losLegendTerrain => 'Teren';
+
+ @override
+ String get losFrequencyLabel => 'Frekvenca';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Prikaži podrobnosti izračuna';
+
+ @override
+ String get losFrequencyDialogTitle => 'Izračun radijskega horizonta';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Začenši od k=$baselineK pri $baselineFreq MHz, izračun prilagodi k-faktor za trenutni pas $frequencyMHz MHz, ki določa ukrivljeno zgornjo mejo radijskega horizonta.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Sledenje poti';
+
+ @override
+ String get contacts_ping => 'Pingati';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Sledi poti do ponavljalnika';
+
+ @override
+ String get contacts_repeaterPing => 'Pinguj ponavljalnik';
+
+ @override
+ String get contacts_roomPathTrace => 'Sledenje poti do strežnika sobe';
+
+ @override
+ String get contacts_roomPing => 'Ping strežnik sobe';
+
+ @override
+ String get contacts_chatTraceRoute => 'Slediti poti žarkov';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Trace route to $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Odložišče je prazno.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Neveljavni kontaktne podatke';
+
+ @override
+ String get contacts_contactImported => 'Kontakt je bil uvožen.';
+
+ @override
+ String get contacts_contactImportFailed => 'Kontakt ni bil uspešno uvožen.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Reklama brez posrednikov';
+
+ @override
+ String get contacts_floodAdvert => 'Poplavna oglás';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Kopiraj oglas v odložišče';
+
+ @override
+ String get contacts_addContactFromClipboard => 'Dodaj stik iz odložišča';
+
+ @override
+ String get contacts_ShareContact => 'Kopiraj stik v Odložišče';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Deliti kontakt prek oglasa';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Poslano po oglasu.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Pošiljanje kontakta ni uspelo.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Oglas je bil kopiran v odložišče.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopiranje oglasa v odložišče je spodletelo.';
+
+ @override
+ String get notification_activityTitle => 'Aktivnost MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'sporočil',
+ few: 'sporočila',
+ two: 'sporočili',
+ one: 'sporočilo',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'sporočil kanala',
+ few: 'sporočila kanala',
+ two: 'sporočili kanala',
+ one: 'sporočilo kanala',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'novih vozlišč',
+ few: 'nova vozlišča',
+ two: 'novi vozlišči',
+ one: 'novo vozlišče',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Odkrito novo $contactType';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Prejeto novo sporočilo';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Izvoz ponoviteljev / strežnika sobe v GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Izvozi ponovljene oddajnike / strežnik sobe z lokacijo v datoteko GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Izvoz spremljevalcev v GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Izvozi spremljevalce z lokacijo v datoteko GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Izvozi vse kontakte v GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Izvozi vse kontakte z lokacijo v datoteko GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Uspešno izvoz GPX datoteke.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Ni stikov za izvoz.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Ni podprto na vašem napravi/operacijskem sistemu';
+
+ @override
+ String get settings_gpxExportError => 'Pri izvozu je prišlo do napake.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Lokacije ponovljivca in strežnika sobe';
+
+ @override
+ String get settings_gpxExportChat => 'Lokacije spremljevalcev';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Lokacije vseh stikov';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Podatki kart izvoženi iz meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open izvoz podatkov GPX karte';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Bližnji ponovitelji';
+
+ @override
+ String get snrIndicator_lastSeen => 'Zadnjič videno';
+
+ @override
+ String get contactsSettings_title => 'Nastavitve stikov';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Avtomatsko odkrivanje';
+
+ @override
+ String get contactsSettings_otherTitle => 'Druge nastavitve v zvezi s stiki';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Avtomatsko dodaj uporabnike';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Dovoli spremljevalcu, da samodejno doda odkrite uporabnike.';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Avtomatsko dodaj ponovitelje';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Dovoli spremljevalcu, da samodejno doda odkrite ponovitelje.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Avtomatsko dodaj strežnike sob';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Dovoli spremljevalcu, da samodejno doda odkrite strežnike sob.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Avtomatsko dodaj senzorje';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Dovoli spremljevalcu, da samodejno doda odkrite senzorje.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Prepiši najstarejše';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Ko je seznam stikov poln, bo najstarejši nestarševski stik zamenjan.';
+
+ @override
+ String get discoveredContacts_Title => 'Odkriti stiki';
+
+ @override
+ String get discoveredContacts_noMatching => 'Ni ujemajočih stikov';
+
+ @override
+ String get discoveredContacts_searchHint => 'Najdeni stiki po iskanju';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kontakt dodan';
+
+ @override
+ String get discoveredContacts_addContact => 'Dodaj stik';
+
+ @override
+ String get discoveredContacts_copyContact => 'Kopiraj stik v odložišče';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Izbriši stik';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Izbriši vse odkrite kontakte';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Ste prepričani, da želite izbrisati vse odkrite kontakte?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Prosimo, počakajte trenutek, preden pošljete ponovno.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Pritisnite za najstarejše nepročitano sporočilo';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'Ko odpirate klepet z neprebranimi sporočili, se premaknite na prvo neprebrano sporočilo, namesto najnovejšega.';
+
+ @override
+ String get appSettings_languageHu => 'Madžarski';
+
+ @override
+ String get appSettings_languageJa => 'Japonski';
+
+ @override
+ String get appSettings_languageKo => 'Korejski';
+
+ @override
+ String get radioStats_tooltip => 'Statistike za radio in mrežo';
+
+ @override
+ String get radioStats_screenTitle => 'Radijske statistike';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Povežite se z napravo, da si ogledate statistiko o radiju.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Statistika za radio zahteva združljivo programsko opremo v8 ali kasnejše.';
+
+ @override
+ String get radioStats_waiting => 'Čakam na podatke…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Število šuma: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Najkasnejše vrednost RSSI: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Najkasnejše vrednost SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Čas na TX (skupno): $seconds s';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Čas, namenjen RX-ju (skupno): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Ravnovredna raven šuma (dBm) za nedavne vzorce.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Število šuma: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Prejemanje statistike o radiju…';
+
+ @override
+ String get radioStats_settingsTile => 'Radijske statistike';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Število šumov, RSSI, SNR in čas, ki ga je napolnila oprema';
+
+ @override
+ String get translation_title => 'Prevod';
+
+ @override
+ String get translation_enableTitle => 'Omogočite prevod';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Prevedite vstopne sporočila in omogočite predhodno prevajanje.';
+
+ @override
+ String get translation_composerTitle => 'Preprištejte, preden pošljete';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Ureja privzeto stanje ikone za prevod, ki jo uporablja avtor.';
+
+ @override
+ String get translation_targetLanguage => 'Ciljna jezika';
+
+ @override
+ String get translation_useAppLanguage => 'Uporabite jezik aplikacije';
+
+ @override
+ String get translation_downloadedModelLabel => 'Naložen model';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Prednastavljeni model Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel => 'URL za ročni model';
+
+ @override
+ String get translation_downloadModel => 'Prenesite model';
+
+ @override
+ String get translation_downloading => 'Izvajanje...';
+
+ @override
+ String get translation_working => 'Delo...';
+
+ @override
+ String get translation_stop => 'Prekliji';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Sklapljanje prenesenih delov v končni datoteko...';
+
+ @override
+ String get translation_downloadedModels => 'Naloženi modeli';
+
+ @override
+ String get translation_deleteModel => 'Izbrisati model';
+
+ @override
+ String get translation_modelDownloaded =>
+ 'Model za prevajanje je bil naložen.';
+
+ @override
+ String get translation_downloadStopped => 'Prenos je bil prekinjen.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Izgovoritev ni bila uspešna: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Najprej vnesite URL model.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Prikaži PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Skrij PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth PIN za seznanjanje';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Vnesite PIN za $deviceName (pustite prazno, če ga ni).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Prevod sporočila';
+
+ @override
+ String get translation_translateBeforeSending =>
+ 'Preprištejte, preden pošljete';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Vsebina sporočil bo prevedena, preden jih pošljemo.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Pošljite sporočila v originalnem tipkanem jeziku.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Prevesti v $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Možnosti prevoda';
+
+ @override
+ String get translation_systemLanguage => 'Jezik sistema';
}
diff --git a/lib/l10n/app_localizations_sv.dart b/lib/l10n/app_localizations_sv.dart
index 8b36b97..38e0893 100644
--- a/lib/l10n/app_localizations_sv.dart
+++ b/lib/l10n/app_localizations_sv.dart
@@ -38,6 +38,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get common_delete => 'Radera';
+ @override
+ String get common_deleteAll => 'Ta bort alla';
+
@override
String get common_close => 'Stänga';
@@ -108,6 +111,133 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get scanner_title => 'MeshCore Open';
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'Anslut via TCP';
+
+ @override
+ String get tcpHostLabel => 'IP-adress';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Port';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Ange slutpunkt och anslut';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Anslutning till $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'IP-adress krävs.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Porten måste vara mellan 1 och 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'TCP-transport fungerar inte på denna plattform.';
+
+ @override
+ String get tcpErrorTimedOut => 'TCP-anslutningen har tidsut gått.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Fel vid TCP-anslutning: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Anslut via USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Välj en detekterad seriell enhet och anslut direkt till din MeshCore-nod.';
+
+ @override
+ String get usbScreenStatus => 'Välj en USB-enhet';
+
+ @override
+ String get usbScreenNote =>
+ 'USB-seriell kommunikation är aktiv på stödda Android-enheter och på skrivbordsplattformar.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Inga USB-enheter hittades. Anslut en och uppdatera.';
+
+ @override
+ String get usbErrorPermissionDenied => 'Tillgången via USB nekas.';
+
+ @override
+ String get usbErrorDeviceMissing =>
+ 'Den valda USB-enheten är inte längre tillgänglig.';
+
+ @override
+ String get usbErrorInvalidPort => 'Välj en giltig USB-enhet.';
+
+ @override
+ String get usbErrorBusy =>
+ 'En annan förfrågan om USB-anslutning är redan pågående.';
+
+ @override
+ String get usbErrorNotConnected => 'Ingen USB-enhet är ansluten.';
+
+ @override
+ String get usbErrorOpenFailed =>
+ 'Misslyckades med att öppna det valda USB-enheten.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Kunde inte ansluta till det valda USB-enheten.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'USB-seriell kommunikation stöds inte på denna plattform.';
+
+ @override
+ String get usbErrorAlreadyActive => 'En USB-anslutning är redan aktiv.';
+
+ @override
+ String get usbErrorNoDeviceSelected => 'Ingen USB-enhet valdes.';
+
+ @override
+ String get usbErrorPortClosed => 'USB-anslutningen är inte aktiv.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'Anslutningen har tidsutgått. Se till att enheten har rätt USB-firmware.';
+
+ @override
+ String get usbFallbackDeviceName => 'Web-serieenhet';
+
+ @override
+ String get usbStatus_notConnected => 'Välj en USB-enhet';
+
+ @override
+ String get usbStatus_connecting => 'Anslutning till USB-enhet...';
+
+ @override
+ String get usbStatus_searching => 'Söker efter USB-enheter...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Fel vid USB-anslutning: $error';
+ }
+
@override
String get scanner_scanning => 'Söker efter enheter...';
@@ -142,6 +272,23 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get scanner_scan => 'Skanna';
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth är avstängt';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Vänligen aktivera Bluetooth för att söka efter enheter.';
+
+ @override
+ String get scanner_chromeRequired => 'Chrome-webbläsare krävs';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Denna webbapplikation kräver Google Chrome oder en Chromium-baserader webbläsare för Bluetooth-stöd.';
+
+ @override
+ String get scanner_enableBluetooth => 'Aktivera Bluetooth';
+
@override
String get device_quickSwitch => 'Snabb växling';
@@ -222,6 +369,13 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get settings_longitude => 'Längdgrad';
+ @override
+ String get settings_contactSettings => 'Kontaktinställningar';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Inställningar för hur kontakter läggs till.';
+
@override
String get settings_privacyMode => 'Privatläge';
@@ -238,6 +392,49 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get settings_privacyModeDisabled => 'Privatläge är avstängt';
+ @override
+ String get settings_privacy => 'Inställningar för sekretess';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Kontrollera vilken information som delas.';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Välj vilken information din enhet delar med andra.';
+
+ @override
+ String get settings_denyAll => 'Neka alla';
+
+ @override
+ String get settings_allowByContact => 'Tillåt via kontaktflaggor';
+
+ @override
+ String get settings_allowAll => 'Tillåt alla';
+
+ @override
+ String get settings_telemetryBaseMode => 'Telemetribasläge';
+
+ @override
+ String get settings_telemetryLocationMode => 'Telemetritillstånd för plats';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Telemetri miljöläge';
+
+ @override
+ String get settings_advertLocation => 'Annonsplacering';
+
+ @override
+ String get settings_advertLocationSubtitle => 'Inkludera plats i annonsen';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Multi-ACKs: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Telemetri-läge uppdaterat';
+
@override
String get settings_actions => 'Åtgärder';
@@ -307,6 +504,10 @@ class AppLocalizationsSv extends AppLocalizations {
String get settings_aboutDescription =>
'En öppen källkods Flutter-klient för MeshCore LoRa meshnätverksenheter.';
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS-höjddata: Open-Meteo (CC BY 4.0)';
+
@override
String get settings_infoName => 'Namn';
@@ -331,15 +532,6 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get settings_presets => 'Fördefinierade inställningar';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => 'Frekvens (MHz)';
@@ -368,10 +560,15 @@ class AppLocalizationsSv extends AppLocalizations {
String get settings_txPowerInvalid => 'Ogiltig TX-effekt (0-22 dBm)';
@override
- String get settings_longRange => 'Lång räckvidd';
+ String get settings_clientRepeat => 'Upprepa utan elnät';
@override
- String get settings_fastSpeed => 'Snabb hastighet';
+ String get settings_clientRepeatSubtitle =>
+ 'Låt enheten repetera nätpaket för andra användare.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'För att kunna kommunicera utanför elnätet krävs frekvenserna 433, 869 eller 918 MHz.';
@override
String settings_error(String message) {
@@ -441,6 +638,19 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get appSettings_languageBg => 'Български';
+ @override
+ String get appSettings_languageRu => 'Ryska';
+
+ @override
+ String get appSettings_languageUk => 'Ukrainska';
+
+ @override
+ String get appSettings_enableMessageTracing => 'Aktivera meddelandespårning';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Visa detaljerade metadata om dirigering och tidsinställningar för meddelanden';
+
@override
String get appSettings_notifications => 'Meddelanden';
@@ -515,6 +725,48 @@ class AppLocalizationsSv extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled =>
'Automatisk ruttrotation är avstängd';
+ @override
+ String get appSettings_maxRouteWeight => 'Maximalt tillåtet vikt för rutten';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Maximal vikt som en leveransväg kan ackumulera från framgångsrika leveranser.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Initial vikt för rutt';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Initial vikt för nyligen upptäckta vägar';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Ökning av vikt för framgång';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Vikt läggs till en väg efter en lyckad leverans.';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Minskning av vikten för misslyckande';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Vikt som tagits bort från en väg efter ett misslyckat leveransförsök';
+
+ @override
+ String get appSettings_maxMessageRetries => 'Maximalt antal försök';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Antal försök att skicka om ett meddelande innan det markeras som misslyckat.';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
@override
String get appSettings_battery => 'Batteri';
@@ -598,6 +850,15 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get appSettings_offlineMapCache => 'Offline Kartcache';
+ @override
+ String get appSettings_unitsTitle => 'Enheter';
+
+ @override
+ String get appSettings_unitsMetric => 'Metriskt (m/km)';
+
+ @override
+ String get appSettings_unitsImperial => 'Imperialt (ft / mi)';
+
@override
String get appSettings_noAreaSelected => 'Ingen area markerad';
@@ -635,7 +896,35 @@ class AppLocalizationsSv extends AppLocalizations {
'Kontakter kommer att visas när enheter annonserar.';
@override
- String get contacts_searchContacts => 'Sök kontakter...';
+ String get contacts_unread => 'Oläst';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Sök kontakter...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Sök kontakter...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Sök $number$str Favoriter...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Sök $number$str användare...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Sök $number$str upprepningsenheter...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Sök $number$str Room-servrar...';
+ }
@override
String get contacts_noUnreadContacts => 'Inga oinlästa kontakter';
@@ -684,6 +973,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get contacts_groupNameRequired => 'Gruppnamnet är obligatoriskt';
+ @override
+ String get contacts_groupNameReserved => 'Detta gruppnamn är reserverat';
+
@override
String contacts_groupAlreadyExists(String name) {
return 'Gruppen \"$name\" finns redan.';
@@ -723,6 +1015,40 @@ class AppLocalizationsSv extends AppLocalizations {
return 'Senast synlig $days dagar sedan';
}
+ @override
+ String get contact_info => 'Kontaktinformation';
+
+ @override
+ String get contact_settings => 'Kontaktinställningar';
+
+ @override
+ String get contact_telemetry => 'Telemetri';
+
+ @override
+ String get contact_lastSeen => 'Senast sedd';
+
+ @override
+ String get contact_clearChat => 'Rensa Chatt';
+
+ @override
+ String get contact_teleBase => 'Telemetribas';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Tillåt delning av batterinivå och grundläggande telemetri';
+
+ @override
+ String get contact_teleLoc => 'Telemetridata plats';
+
+ @override
+ String get contact_teleLocSubtitle => 'Tillåt delning av platsdata';
+
+ @override
+ String get contact_teleEnv => 'Telemetri Miljö';
+
+ @override
+ String get contact_teleEnvSubtitle => 'Tillåt delning av miljösensordata';
+
@override
String get channels_title => 'Kanaler';
@@ -761,6 +1087,12 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get channels_editChannel => 'Redigera kanal';
+ @override
+ String get channels_muteChannel => 'Tysta kanal';
+
+ @override
+ String get channels_unmuteChannel => 'Slå på ljud för kanal';
+
@override
String get channels_deleteChannel => 'Ta bort kanal';
@@ -769,6 +1101,11 @@ class AppLocalizationsSv extends AppLocalizations {
return 'Radera \"$name\"? Detta kan inte ångras.';
}
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Det gick inte att ta bort kanalen \"$name\"';
+ }
+
@override
String channels_channelDeleted(String name) {
return 'Kanalen \"$name\" raderad';
@@ -880,6 +1217,14 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get chat_noMessages => 'Inga meddelanden ännu';
+ @override
+ String get chat_sendMessage => 'Skicka meddelande';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Skicka ett meddelande till $contactName';
+ }
+
@override
String get chat_sendMessageToStart =>
'Skicka ett meddelande för att komma igång';
@@ -901,11 +1246,6 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get chat_location => 'Plats';
- @override
- String chat_sendMessageTo(String contactName) {
- return 'Skicka ett meddelande till $contactName';
- }
-
@override
String get chat_typeMessage => 'Skriv ett meddelande...';
@@ -1057,6 +1397,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get chat_pathManagement => 'Stigarhantering';
+ @override
+ String get chat_ShowAllPaths => 'Visa alla vägar';
+
@override
String get chat_routingMode => 'Ruttläge';
@@ -1192,9 +1535,33 @@ class AppLocalizationsSv extends AppLocalizations {
return 'Olästa: $count';
}
+ @override
+ String get chat_openLink => 'Öppna länk?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Vill du öppna den här länken i din webbläsare?';
+
+ @override
+ String get chat_open => 'Öppna';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Kunde inte öppna länken: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Ogiltigt länkformat';
+
@override
String get map_title => 'Nodkarta';
+ @override
+ String get map_lineOfSight => 'Synlinje';
+
+ @override
+ String get map_losScreenTitle => 'Synlinje';
+
@override
String get map_noNodesWithLocation => 'Inga noder med platsinformation';
@@ -1252,6 +1619,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get map_shareMarkerHere => 'Dela markeringen här';
+ @override
+ String get map_setAsMyLocation => 'Ange som min plats';
+
@override
String get map_pinLabel => 'Fästetikett';
@@ -1297,6 +1667,9 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get map_otherNodes => 'Andra noder';
+ @override
+ String get map_showOverlaps => 'Repeater-nyckelöverlappningar';
+
@override
String get map_keyPrefix => 'Nyckelprefix';
@@ -1312,6 +1685,16 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get map_showSharedMarkers => 'Visa delade markörer';
+ @override
+ String get map_showGuessedLocations =>
+ 'Visa upp de antagna nodernas placeringar';
+
+ @override
+ String get map_showDiscoveryContacts => 'Visa Discovery-kontakter';
+
+ @override
+ String get map_guessedLocation => 'Gissad plats';
+
@override
String get map_lastSeenTime => 'Senaste Visats Tid';
@@ -1324,6 +1707,21 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get map_manageRepeater => 'Hantera Upprepare';
+ @override
+ String get map_tapToAdd => 'Tryck på noder för att lägga till dem i banan.';
+
+ @override
+ String get map_runTrace => 'Kör spårsökning';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Gå tillbaka på samma väg';
+
+ @override
+ String get map_removeLast => 'Ta bort sista';
+
+ @override
+ String get map_pathTraceCancelled => 'Sökvägsspårning avbruten.';
+
@override
String get mapCache_title => 'Offline Kartcache';
@@ -1592,9 +1990,18 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get room_management => 'Rumserverhantering';
+ @override
+ String get repeater_guest => 'Information om repetorer';
+
+ @override
+ String get room_guest => 'Information om servern';
+
@override
String get repeater_managementTools => 'Administrationsverktyg';
+ @override
+ String get repeater_guestTools => 'Gästverktyg';
+
@override
String get repeater_status => 'Status';
@@ -1616,10 +2023,10 @@ class AppLocalizationsSv extends AppLocalizations {
String get repeater_cliSubtitle => 'Skicka kommandon till repetitorn';
@override
- String get repeater_neighbours => 'Grannar';
+ String get repeater_neighbors => 'Grannar';
@override
- String get repeater_neighboursSubtitle => 'Visa noll hoppgrannar.';
+ String get repeater_neighborsSubtitle => 'Visa noll hoppgrannar.';
@override
String get repeater_settings => 'Inställningar';
@@ -1627,6 +2034,14 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get repeater_settingsSubtitle => 'Konfigurera återspolarparametrar';
+ @override
+ String get repeater_clockSyncAfterLogin =>
+ 'Synkronisera klockan efter inloggning';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Automatiskt skicka \"klocksynkronisering\" efter en lyckad inloggning.';
+
@override
String get repeater_statusTitle => 'Återspelsstatus';
@@ -1999,6 +2414,12 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get repeater_cliQuickClock => 'Klocka';
+ @override
+ String get repeater_cliQuickClockSync => 'Synkronisera klocka';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Upptäck grannar';
+
@override
String get repeater_cliHelpAdvert => 'Skickar ett annonspaket';
@@ -2310,7 +2731,7 @@ class AppLocalizationsSv extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => 'Upprepar grannar';
+ String get neighbors_repeatersNeighbors => 'Upprepar grannar';
@override
String get neighbors_noData => 'Inga grannuppgifter finns tillgängliga.';
@@ -2541,32 +2962,32 @@ class AppLocalizationsSv extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => 'Regenerera hemlig kod';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return 'Regenerera den hemliga nyckeln för \"$name\"? Alla medlemmar måste scanna den nya QR-koden för att fortsätta kommunicera.';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => 'Regenerera';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return 'Lösenord återskapad för \"$name\"';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => 'Uppdatera hemlighet';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return 'Hemlighet uppdaterad för \"$name\"';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return 'Skanna den nya QR-koden för att uppdatera hemligheten för \"$name\"';
}
@override
@@ -2618,6 +3039,15 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get listFilter_all => 'Alla';
+ @override
+ String get listFilter_favorites => 'Favoriter';
+
+ @override
+ String get listFilter_addToFavorites => 'Lägg till i favoriter';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Ta bort från favoriter';
+
@override
String get listFilter_users => 'Användare';
@@ -2632,4 +3062,604 @@ class AppLocalizationsSv extends AppLocalizations {
@override
String get listFilter_newGroup => 'Ny grupp';
+
+ @override
+ String get pathTrace_you => 'Du';
+
+ @override
+ String get pathTrace_failed => 'Sökvägsföljning misslyckades.';
+
+ @override
+ String get pathTrace_notAvailable => 'Path trace ej tillgänglig.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Uppdatera Path Trace';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'En eller flera av humlen saknar en plats!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Rensa väg';
+
+ @override
+ String get losSelectStartEnd => 'Välj start- och slutnoder för LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Synlinjekontroll misslyckades: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Rensa alla punkter';
+
+ @override
+ String get losRunToViewElevationProfile => 'Kör LOS för att se höjdprofil';
+
+ @override
+ String get losMenuTitle => 'LOS-menyn';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Tryck på noder eller tryck länge på kartan för anpassade punkter';
+
+ @override
+ String get losShowDisplayNodes => 'Visa displaynoder';
+
+ @override
+ String get losCustomPoints => 'Anpassade poäng';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Anpassad $index';
+ }
+
+ @override
+ String get losPointA => 'Punkt A';
+
+ @override
+ String get losPointB => 'Punkt B';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Antenn A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Antenn B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Kör LOS';
+
+ @override
+ String get losNoElevationData => 'Inga höjddata';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, rensa LOS, min clearance $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, blockerad av $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: kollar...';
+
+ @override
+ String get losStatusNoData => 'LOS: inga data';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total rensa, $blocked blockerad, $unknown okänd';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Höjddata är inte tillgänglig för ett eller flera prover.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Ogiltiga poäng/höjddata för LOS-beräkning.';
+
+ @override
+ String get losRenameCustomPoint => 'Byt namn på anpassad punkt';
+
+ @override
+ String get losPointName => 'Punktnamn';
+
+ @override
+ String get losShowPanelTooltip => 'Visa LOS-panelen';
+
+ @override
+ String get losHidePanelTooltip => 'Dölj LOS-panelen';
+
+ @override
+ String get losElevationAttribution => 'Höjddata: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Radiohorisont';
+
+ @override
+ String get losLegendLosBeam => 'Siktlinje';
+
+ @override
+ String get losLegendTerrain => 'Terräng';
+
+ @override
+ String get losFrequencyLabel => 'Frekvens';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Visa detaljer om beräkningen';
+
+ @override
+ String get losFrequencyDialogTitle => 'Beräkning av radiohorisonten';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Med start från k=$baselineK vid $baselineFreq MHz, justerar beräkningen k-faktorn för det aktuella $frequencyMHz MHz-bandet, som definierar den böjda radiohorisonten.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Path Trace';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Vägspårning till repeater';
+
+ @override
+ String get contacts_repeaterPing => 'Ping-repeater';
+
+ @override
+ String get contacts_roomPathTrace => 'Vägspårning till rumserver';
+
+ @override
+ String get contacts_roomPing => 'Ping rumsserver';
+
+ @override
+ String get contacts_chatTraceRoute => 'Spåra rutt';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Spåra rutt till $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Urklipp är tomt.';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Ogiltiga kontaktuppgifter';
+
+ @override
+ String get contacts_contactImported => 'Kontakt har importerats.';
+
+ @override
+ String get contacts_contactImportFailed => 'Kontakt kunde inte importeras.';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Reklam med nollhopp';
+
+ @override
+ String get contacts_floodAdvert => 'Översvämningsannons';
+
+ @override
+ String get contacts_copyAdvertToClipboard => 'Kopiera annons till urklipp';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Lägg till kontakt från urklipp';
+
+ @override
+ String get contacts_ShareContact => 'Kopiera kontakt till Urklipp';
+
+ @override
+ String get contacts_ShareContactZeroHop => 'Dela kontakt via annons';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => 'Skickat kontakt via annons.';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Misslyckades med att skicka kontakt.';
+
+ @override
+ String get contacts_contactAdvertCopied => 'Annons kopierad till Urklipp.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Kopiering av annons till Urklipp misslyckades.';
+
+ @override
+ String get notification_activityTitle => 'MeshCore Aktivitet';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'meddelanden',
+ one: 'meddelande',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'kanalmeddelanden',
+ one: 'kanalmeddelande',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'nya noder',
+ one: 'ny nod',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Ny $contactType upptäckt';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Nytt meddelande mottaget';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Exportera repeater / rumsservrar till GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Exporterar repeater / roomserver med plats till GPX-fil.';
+
+ @override
+ String get settings_gpxExportContacts => 'Exportera följeslagare till GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Exporterar följeslagare med en plats till GPX-fil.';
+
+ @override
+ String get settings_gpxExportAll => 'Exportera alla kontakter till GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Exporterar alla kontakter med en plats till GPX-fil.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Har exporterat GPX-fil med framgång';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Inga kontakter att exportera.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Stöds inte på din enhet/operativsystem';
+
+ @override
+ String get settings_gpxExportError =>
+ 'Det uppstod ett fel när data exporterades.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Repeater- och rumsserverplatser';
+
+ @override
+ String get settings_gpxExportChat => 'Medhjälparplatser';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Alla kontakters platser';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Kartdata exporterad från meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'meshcore-open export av GPX-kartdata';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Närliggande uppreparstationer';
+
+ @override
+ String get snrIndicator_lastSeen => 'Senast sedd';
+
+ @override
+ String get contactsSettings_title => 'Kontaktinställningar';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Automatisk upptäckt';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Andra inställningar relaterade till kontakt';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Lägg till användare automatiskt';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Tillåt kompanjonen att automatiskt lägga till upptäckta användare';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Lägg till upprepande enheter automatiskt';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Tillåt kompanjonen att automatiskt lägga till upptäckta repeater.';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Lägg automatiskt till rumsservrar';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Tillåt kompanjonen att automatiskt lägga till upptäckta rumsservrar.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Lägg till sensorer automatiskt';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Tillåt kompanjonen att automatiskt lägga till upptäckta sensorer.';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Skriv över äldst';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'När kontaktlistan är full ersätts den äldsta icke-favoriterade kontakten.';
+
+ @override
+ String get discoveredContacts_Title => 'Upptäckta kontakter';
+
+ @override
+ String get discoveredContacts_noMatching => 'Inga matchande kontakter';
+
+ @override
+ String get discoveredContacts_searchHint => 'Sök uppfunna kontakter';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Kontakt tillagd';
+
+ @override
+ String get discoveredContacts_addContact => 'Lägg till kontakt';
+
+ @override
+ String get discoveredContacts_copyContact => 'Kopiera kontakt till urklipp';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Ta bort kontakt';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Ta bort alla upptäckta kontakter';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Är du säker på att du vill ta bort alla upptäckta kontakter?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Vänligen vänta en stund innan du skickar igen.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Gå direkt till det äldsta, obesvarade meddelandet';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'När du öppnar en chatt med oinlästa meddelanden, scrolla till det första oinlästa meddelandet istället för det senaste.';
+
+ @override
+ String get appSettings_languageHu => 'Ungerskt';
+
+ @override
+ String get appSettings_languageJa => 'Japanska';
+
+ @override
+ String get appSettings_languageKo => 'Koreanska';
+
+ @override
+ String get radioStats_tooltip => 'Radio- och mesh-statistik';
+
+ @override
+ String get radioStats_screenTitle => 'Radiostation';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Anslut till en enhet för att visa radiostatistik.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Radio statistik kräver kompatibel firmware version 8 eller senare.';
+
+ @override
+ String get radioStats_waiting => 'Väntar på data…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Bakgrundsnivå: $noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Senaste RSSI-värde: $rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Senaste SNR: $snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX-tid (total): $seconds sekunder';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX-tid (total): $seconds s';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Ljudnivå (dBm) baserat på de senaste mätningarna.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Bakgrundsnivå: $noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Hämtar radiostatistik…';
+
+ @override
+ String get radioStats_settingsTile => 'Radiostation';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Bakgrundsnivå, RSSI, SNR och tillgänglig tid';
+
+ @override
+ String get translation_title => 'Översättning';
+
+ @override
+ String get translation_enableTitle => 'Aktivera översättning';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Översätt inkommande meddelanden och möjliggör översättning före avsändning.';
+
+ @override
+ String get translation_composerTitle => 'Översätt innan du skickar';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Styr standardtillståndet för kompositorns översättningsikon.';
+
+ @override
+ String get translation_targetLanguage => 'Målmedvetet språk';
+
+ @override
+ String get translation_useAppLanguage => 'Använd appens språk';
+
+ @override
+ String get translation_downloadedModelLabel => 'Nedladdad modell';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Fördefinierat Hugging Face-modell';
+
+ @override
+ String get translation_manualUrlLabel => 'Manualens URL';
+
+ @override
+ String get translation_downloadModel => 'Ladda ner modellen';
+
+ @override
+ String get translation_downloading => 'Nedladdning...';
+
+ @override
+ String get translation_working => 'Arbeta...';
+
+ @override
+ String get translation_stop => 'Stopp';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Slå samman de nedladdade delarna till en slutlig fil...';
+
+ @override
+ String get translation_downloadedModels => 'Nedladdade modeller';
+
+ @override
+ String get translation_deleteModel => 'Ta bort modell';
+
+ @override
+ String get translation_modelDownloaded =>
+ 'Översättningsmodellen har laddats ner.';
+
+ @override
+ String get translation_downloadStopped => 'Nedladdningen avbruten.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Nedladdning misslyckades: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst =>
+ 'Ange först en URL för en specifik modell.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Visa PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Dölj PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'Bluetooth‑parnings‑PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Ange PIN för $deviceName (lämna tomt om ingen).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Meddelandets översättning';
+
+ @override
+ String get translation_translateBeforeSending => 'Översätt innan du skickar';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Meddelandena kommer att översättas innan de skickas.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Skicka meddelanden på det ursprungliga, stavade språket.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Översätt till $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Översättningsalternativ';
+
+ @override
+ String get translation_systemLanguage => 'Språk för systemet';
}
diff --git a/lib/l10n/app_localizations_uk.dart b/lib/l10n/app_localizations_uk.dart
new file mode 100644
index 0000000..8ed4b9f
--- /dev/null
+++ b/lib/l10n/app_localizations_uk.dart
@@ -0,0 +1,3721 @@
+// ignore: unused_import
+import 'package:intl/intl.dart' as intl;
+import 'app_localizations.dart';
+
+// ignore_for_file: type=lint
+
+/// The translations for Ukrainian (`uk`).
+class AppLocalizationsUk extends AppLocalizations {
+ AppLocalizationsUk([String locale = 'uk']) : super(locale);
+
+ @override
+ String get appTitle => 'MeshCore Open';
+
+ @override
+ String get nav_contacts => 'Контакти';
+
+ @override
+ String get nav_channels => 'Канали';
+
+ @override
+ String get nav_map => 'Карта';
+
+ @override
+ String get common_cancel => 'Скасувати';
+
+ @override
+ String get common_ok => 'ОК';
+
+ @override
+ String get common_connect => 'Підключити';
+
+ @override
+ String get common_unknownDevice => 'Невідомий пристрій';
+
+ @override
+ String get common_save => 'Зберегти';
+
+ @override
+ String get common_delete => 'Видалити';
+
+ @override
+ String get common_deleteAll => 'Видалити все';
+
+ @override
+ String get common_close => 'Закрити';
+
+ @override
+ String get common_edit => 'Редагувати';
+
+ @override
+ String get common_add => 'Додати';
+
+ @override
+ String get common_settings => 'Налаштування';
+
+ @override
+ String get common_disconnect => 'Відключити';
+
+ @override
+ String get common_connected => 'Підключено';
+
+ @override
+ String get common_disconnected => 'Відключено';
+
+ @override
+ String get common_create => 'Створити';
+
+ @override
+ String get common_continue => 'Продовжити';
+
+ @override
+ String get common_share => 'Поділитися';
+
+ @override
+ String get common_copy => 'Копіювати';
+
+ @override
+ String get common_retry => 'Повторити';
+
+ @override
+ String get common_hide => 'Приховати';
+
+ @override
+ String get common_remove => 'Прибрати';
+
+ @override
+ String get common_enable => 'Увімкнути';
+
+ @override
+ String get common_disable => 'Вимкнути';
+
+ @override
+ String get common_reboot => 'Перезавантажити';
+
+ @override
+ String get common_loading => 'Завантаження...';
+
+ @override
+ String get common_notAvailable => '—';
+
+ @override
+ String common_voltageValue(String volts) {
+ return '$volts В';
+ }
+
+ @override
+ String common_percentValue(int percent) {
+ return '$percent%';
+ }
+
+ @override
+ String get scanner_title => 'MeshCore Open';
+
+ @override
+ String get connectionChoiceUsbLabel => 'USB';
+
+ @override
+ String get connectionChoiceBluetoothLabel => 'Bluetooth';
+
+ @override
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => 'З\'єднатися через протокол TCP';
+
+ @override
+ String get tcpHostLabel => 'IP-адреса';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => 'Порт';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => 'Введіть кінцеву точку та підключіться';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return 'Підключення до $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => 'Необхідно вказати IP-адресу.';
+
+ @override
+ String get tcpErrorPortInvalid => 'Порт повинен бути в межах від 1 до 65535.';
+
+ @override
+ String get tcpErrorUnsupported =>
+ 'Транспорт TCP не підтримується на цій платформі.';
+
+ @override
+ String get tcpErrorTimedOut =>
+ 'З\'єднання TCP завершилося через закінчення часу очікування.';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'Не вдалося встановити з\'єднання TCP: $error';
+ }
+
+ @override
+ String get usbScreenTitle => 'Підключити через USB';
+
+ @override
+ String get usbScreenSubtitle =>
+ 'Виберіть виявлене серійне пристрій і підключіть його безпосередньо до вашого вузла MeshCore.';
+
+ @override
+ String get usbScreenStatus => 'Виберіть пристрій USB';
+
+ @override
+ String get usbScreenNote =>
+ 'USB-серіальний інтерфейс активний на підтримуваних пристроях на базі Android та на десктопних платформах.';
+
+ @override
+ String get usbScreenEmptyState =>
+ 'Не знайдено жодних пристроїв USB. Підключіть один і перезавантажте.';
+
+ @override
+ String get usbErrorPermissionDenied =>
+ 'Було відмовлено у наданні дозволу на використання USB.';
+
+ @override
+ String get usbErrorDeviceMissing => 'Вибране USB-пристрій більше недоступне.';
+
+ @override
+ String get usbErrorInvalidPort => 'Виберіть дійсний USB-пристрій.';
+
+ @override
+ String get usbErrorBusy =>
+ 'Ще один запит на підключення через USB вже обробляється.';
+
+ @override
+ String get usbErrorNotConnected => 'Немає підключених пристроїв USB.';
+
+ @override
+ String get usbErrorOpenFailed => 'Не вдалося відкрити вибране USB-пристрій.';
+
+ @override
+ String get usbErrorConnectFailed =>
+ 'Не вдалося підключитися до вибраного USB-пристрою.';
+
+ @override
+ String get usbErrorUnsupported =>
+ 'Підтримка USB-серіального інтерфейсу не реалізована на цій платформі.';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB-з\'єднання вже встановлено.';
+
+ @override
+ String get usbErrorNoDeviceSelected =>
+ 'Не було вибрано жодного пристрою USB.';
+
+ @override
+ String get usbErrorPortClosed => 'З\'єднання USB не встановлено.';
+
+ @override
+ String get usbErrorConnectTimedOut =>
+ 'З\'єднання не вдалося встановити. Переконайтеся, що пристрій має встановлене програмне забезпечення USB Companion.';
+
+ @override
+ String get usbFallbackDeviceName =>
+ 'Пристрій для передачі даних по веб-серіалах';
+
+ @override
+ String get usbStatus_notConnected => 'Виберіть пристрій USB';
+
+ @override
+ String get usbStatus_connecting => 'Підключення до USB-пристрою...';
+
+ @override
+ String get usbStatus_searching => 'Пошук пристроїв USB...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'Не вдалося встановити з\'єднання через USB: $error';
+ }
+
+ @override
+ String get scanner_scanning => 'Пошук пристроїв...';
+
+ @override
+ String get scanner_connecting => 'Підключення...';
+
+ @override
+ String get scanner_disconnecting => 'Відключення...';
+
+ @override
+ String get scanner_notConnected => 'Не підключено';
+
+ @override
+ String scanner_connectedTo(String deviceName) {
+ return 'Підключено до $deviceName';
+ }
+
+ @override
+ String get scanner_searchingDevices => 'Пошук пристроїв MeshCore...';
+
+ @override
+ String get scanner_tapToScan =>
+ 'Натисніть «Сканувати», щоб знайти пристрої MeshCore';
+
+ @override
+ String scanner_connectionFailed(String error) {
+ return 'Помилка підключення: $error';
+ }
+
+ @override
+ String get scanner_stop => 'Стоп';
+
+ @override
+ String get scanner_scan => 'Сканувати';
+
+ @override
+ String get scanner_bluetoothOff => 'Bluetooth вимкнено';
+
+ @override
+ String get scanner_bluetoothOffMessage =>
+ 'Будь ласка, увімкніть Bluetooth, щоб сканувати пристрої.';
+
+ @override
+ String get scanner_chromeRequired => 'Потрібен браузер Chrome';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ 'Для підтримки Bluetooth у цьому веб-додатку потрібен Google Chrome або браузер на базі Chromium.';
+
+ @override
+ String get scanner_enableBluetooth => 'Увімкніть Bluetooth';
+
+ @override
+ String get device_quickSwitch => 'Швидке перемикання';
+
+ @override
+ String get device_meshcore => 'MeshCore';
+
+ @override
+ String get settings_title => 'Налаштування';
+
+ @override
+ String get settings_deviceInfo => 'Інформація про пристрій';
+
+ @override
+ String get settings_appSettings => 'Налаштування програми';
+
+ @override
+ String get settings_appSettingsSubtitle =>
+ 'Сповіщення, повідомлення та налаштування карти';
+
+ @override
+ String get settings_nodeSettings => 'Налаштування вузла';
+
+ @override
+ String get settings_nodeName => 'Ім\'я вузла';
+
+ @override
+ String get settings_nodeNameNotSet => 'Не встановлено';
+
+ @override
+ String get settings_nodeNameHint => 'Введіть ім\'я вузла';
+
+ @override
+ String get settings_nodeNameUpdated => 'Ім\'я оновлено';
+
+ @override
+ String get settings_radioSettings => 'Налаштування радіо';
+
+ @override
+ String get settings_radioSettingsSubtitle =>
+ 'Частота, потужність, коефіцієнт розширення';
+
+ @override
+ String get settings_radioSettingsUpdated => 'Налаштування радіо оновлено';
+
+ @override
+ String get settings_location => 'Розташування';
+
+ @override
+ String get settings_locationSubtitle => 'GPS координати';
+
+ @override
+ String get settings_locationUpdated => 'Розташування оновлено';
+
+ @override
+ String get settings_locationBothRequired => 'Введіть широту та довготу.';
+
+ @override
+ String get settings_locationInvalid => 'Некоректна широта або довгота.';
+
+ @override
+ String get settings_locationGPSEnable => 'Увімкнути GPS';
+
+ @override
+ String get settings_locationGPSEnableSubtitle =>
+ 'Вмикає автоматичне оновлення місцезнаходження через GPS.';
+
+ @override
+ String get settings_locationIntervalSec => 'Інтервал для GPS (Секунди)';
+
+ @override
+ String get settings_locationIntervalInvalid =>
+ 'Інтервал має бути не менше 60 секунд і менше 86400 секунд.';
+
+ @override
+ String get settings_latitude => 'Широта';
+
+ @override
+ String get settings_longitude => 'Довгота';
+
+ @override
+ String get settings_contactSettings => 'Налаштування контактів';
+
+ @override
+ String get settings_contactSettingsSubtitle =>
+ 'Налаштування для додавання контактів';
+
+ @override
+ String get settings_privacyMode => 'Режим приватності';
+
+ @override
+ String get settings_privacyModeSubtitle =>
+ 'Приховати ім\'я/розташування в оголошеннях';
+
+ @override
+ String get settings_privacyModeToggle =>
+ 'Увімкніть режим приватності, щоб приховати своє ім\'я та місцезнаходження в оголошеннях.';
+
+ @override
+ String get settings_privacyModeEnabled => 'Режим приватності увімкнено';
+
+ @override
+ String get settings_privacyModeDisabled => 'Режим приватності вимкнено';
+
+ @override
+ String get settings_privacy => 'Налаштування приватності';
+
+ @override
+ String get settings_privacySubtitle =>
+ 'Керуйте інформацією, яку буде спільно використовуватися';
+
+ @override
+ String get settings_privacySettingsDescription =>
+ 'Виберіть, яку інформацію ваш пристрій буде передавати іншим.';
+
+ @override
+ String get settings_denyAll => 'Відхилити все';
+
+ @override
+ String get settings_allowByContact => 'Дозволити за контактними прапорцями';
+
+ @override
+ String get settings_allowAll => 'Дозволити все';
+
+ @override
+ String get settings_telemetryBaseMode => 'Режим базової телеметрії';
+
+ @override
+ String get settings_telemetryLocationMode => 'Режим місця телеметрії';
+
+ @override
+ String get settings_telemetryEnvironmentMode => 'Режим середовища телеметрії';
+
+ @override
+ String get settings_advertLocation => 'Розміщення реклами';
+
+ @override
+ String get settings_advertLocationSubtitle =>
+ 'Включити місце розташування в оголошення';
+
+ @override
+ String settings_multiAck(String value) {
+ return 'Багатократне підтвердження: $value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => 'Режим телеметрії оновлено';
+
+ @override
+ String get settings_actions => 'Дії';
+
+ @override
+ String get settings_sendAdvertisement => 'Оголосити себе';
+
+ @override
+ String get settings_sendAdvertisementSubtitle =>
+ 'Транслювати присутність зараз';
+
+ @override
+ String get settings_advertisementSent => 'Оголошення надіслано';
+
+ @override
+ String get settings_syncTime => 'Синхронізація часу';
+
+ @override
+ String get settings_syncTimeSubtitle =>
+ 'Встановити час пристрою відповідно до часу телефону.';
+
+ @override
+ String get settings_timeSynchronized => 'Час синхронізовано';
+
+ @override
+ String get settings_refreshContacts => 'Оновити контакти';
+
+ @override
+ String get settings_refreshContactsSubtitle =>
+ 'Перезавантажити список контактів з пристрою';
+
+ @override
+ String get settings_rebootDevice => 'Перезавантажити пристрій';
+
+ @override
+ String get settings_rebootDeviceSubtitle =>
+ 'Перезавантажити пристрій MeshCore';
+
+ @override
+ String get settings_rebootDeviceConfirm =>
+ 'Ви впевнені, що хочете перезавантажити пристрій? Вас буде відключено.';
+
+ @override
+ String get settings_debug => 'Налагодження';
+
+ @override
+ String get settings_bleDebugLog => 'Журнал налагодження BLE';
+
+ @override
+ String get settings_bleDebugLogSubtitle =>
+ 'Команди BLE, відповіді та необроблені дані';
+
+ @override
+ String get settings_appDebugLog => 'Журнал налагодження програми';
+
+ @override
+ String get settings_appDebugLogSubtitle =>
+ 'Повідомлення налагодження програми';
+
+ @override
+ String get settings_about => 'Про програму';
+
+ @override
+ String settings_aboutVersion(String version) {
+ return 'MeshCore Open v$version';
+ }
+
+ @override
+ String get settings_aboutLegalese => 'Проєкт MeshCore Open Source 2026';
+
+ @override
+ String get settings_aboutDescription =>
+ 'Клієнт Flutter з відкритим вихідним кодом для пристроїв мережі MeshCore LoRa.';
+
+ @override
+ String get settings_aboutOpenMeteoAttribution =>
+ 'Дані про висоту LOS: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get settings_infoName => 'Ім\'я';
+
+ @override
+ String get settings_infoId => 'ID';
+
+ @override
+ String get settings_infoStatus => 'Статус';
+
+ @override
+ String get settings_infoBattery => 'Батарея';
+
+ @override
+ String get settings_infoPublicKey => 'Відкритий ключ';
+
+ @override
+ String get settings_infoContactsCount => 'Кількість контактів';
+
+ @override
+ String get settings_infoChannelCount => 'Кількість каналів';
+
+ @override
+ String get settings_presets => 'Попередні налаштування';
+
+ @override
+ String get settings_frequency => 'Частота (МГц)';
+
+ @override
+ String get settings_frequencyHelper => '300.0 - 2500.0';
+
+ @override
+ String get settings_frequencyInvalid => 'Некоректна частота (300-2500 МГц)';
+
+ @override
+ String get settings_bandwidth => 'Смуга пропускання';
+
+ @override
+ String get settings_spreadingFactor => 'Коефіцієнт розширення';
+
+ @override
+ String get settings_codingRate => 'Швидкість кодування';
+
+ @override
+ String get settings_txPower => 'Потужність TX (дБм)';
+
+ @override
+ String get settings_txPowerHelper => '0 - 22';
+
+ @override
+ String get settings_txPowerInvalid => 'Некоректна потужність TX (0-22 дБм)';
+
+ @override
+ String get settings_clientRepeat => 'Автономна система';
+
+ @override
+ String get settings_clientRepeatSubtitle =>
+ 'Дозвольте цьому пристрою повторювати пакети даних для інших пристроїв.';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ 'Повтор без підключення до мережі вимагає частоти 433, 869 або 918 МГц.';
+
+ @override
+ String settings_error(String message) {
+ return 'Помилка: $message';
+ }
+
+ @override
+ String get appSettings_title => 'Налаштування програми';
+
+ @override
+ String get appSettings_appearance => 'Вигляд';
+
+ @override
+ String get appSettings_theme => 'Тема';
+
+ @override
+ String get appSettings_themeSystem => 'Системна';
+
+ @override
+ String get appSettings_themeLight => 'Світла';
+
+ @override
+ String get appSettings_themeDark => 'Темна';
+
+ @override
+ String get appSettings_language => 'Мова';
+
+ @override
+ String get appSettings_languageSystem => 'Як у системі';
+
+ @override
+ String get appSettings_languageEn => 'English';
+
+ @override
+ String get appSettings_languageFr => 'Français';
+
+ @override
+ String get appSettings_languageEs => 'Español';
+
+ @override
+ String get appSettings_languageDe => 'Deutsch';
+
+ @override
+ String get appSettings_languagePl => 'Polski';
+
+ @override
+ String get appSettings_languageSl => 'Slovenščina';
+
+ @override
+ String get appSettings_languagePt => 'Português';
+
+ @override
+ String get appSettings_languageIt => 'Italiano';
+
+ @override
+ String get appSettings_languageZh => '中文';
+
+ @override
+ String get appSettings_languageSv => 'Svenska';
+
+ @override
+ String get appSettings_languageNl => 'Nederlands';
+
+ @override
+ String get appSettings_languageSk => 'Slovenčina';
+
+ @override
+ String get appSettings_languageBg => 'Български';
+
+ @override
+ String get appSettings_languageRu => 'Російська';
+
+ @override
+ String get appSettings_languageUk => 'Українська';
+
+ @override
+ String get appSettings_enableMessageTracing =>
+ 'Увімкнути відстеження повідомлень';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle =>
+ 'Показувати детальні метадані про маршрутизацію та час для повідомлень';
+
+ @override
+ String get appSettings_notifications => 'Сповіщення';
+
+ @override
+ String get appSettings_enableNotifications => 'Увімкнути сповіщення';
+
+ @override
+ String get appSettings_enableNotificationsSubtitle =>
+ 'Отримувати сповіщення про повідомлення та оголошення';
+
+ @override
+ String get appSettings_notificationPermissionDenied =>
+ 'У доступі до сповіщень відмовлено';
+
+ @override
+ String get appSettings_notificationsEnabled => 'Сповіщення увімкнено';
+
+ @override
+ String get appSettings_notificationsDisabled => 'Сповіщення вимкнено';
+
+ @override
+ String get appSettings_messageNotifications => 'Сповіщення про повідомлення';
+
+ @override
+ String get appSettings_messageNotificationsSubtitle =>
+ 'Показувати сповіщення при отриманні нових повідомлень';
+
+ @override
+ String get appSettings_channelMessageNotifications => 'Сповіщення каналів';
+
+ @override
+ String get appSettings_channelMessageNotificationsSubtitle =>
+ 'Показувати сповіщення при отриманні повідомлень каналу';
+
+ @override
+ String get appSettings_advertisementNotifications =>
+ 'Сповіщення про оголошення';
+
+ @override
+ String get appSettings_advertisementNotificationsSubtitle =>
+ 'Показувати сповіщення при виявленні нових вузлів';
+
+ @override
+ String get appSettings_messaging => 'Обмін повідомленнями';
+
+ @override
+ String get appSettings_clearPathOnMaxRetry =>
+ 'Очищати шлях після макс. спроб';
+
+ @override
+ String get appSettings_clearPathOnMaxRetrySubtitle =>
+ 'Скидати шлях до контакту після 5 невдалих спроб надсилання';
+
+ @override
+ String get appSettings_pathsWillBeCleared =>
+ 'Шляхи будуть очищені після 5 невдалих спроб.';
+
+ @override
+ String get appSettings_pathsWillNotBeCleared =>
+ 'Шляхи не будуть очищатися автоматично.';
+
+ @override
+ String get appSettings_autoRouteRotation => 'Авторотація маршруту';
+
+ @override
+ String get appSettings_autoRouteRotationSubtitle =>
+ 'Чергувати найкращі шляхи та режим «на всю мережу» (flood)';
+
+ @override
+ String get appSettings_autoRouteRotationEnabled =>
+ 'Авторотація маршрутизації увімкнена';
+
+ @override
+ String get appSettings_autoRouteRotationDisabled =>
+ 'Авторотація маршрутизації вимкнена';
+
+ @override
+ String get appSettings_maxRouteWeight => 'Максимальна вага маршруту';
+
+ @override
+ String get appSettings_maxRouteWeightSubtitle =>
+ 'Максимальна вага, яку може накопичити маршрут завдяки успішним доставкам.';
+
+ @override
+ String get appSettings_initialRouteWeight => 'Початкова вартість маршруту';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle =>
+ 'Початкова вага для нових відкритих шляхів';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement =>
+ 'Збільшення ваги успіху';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ 'Вага, додана до маршруту після успішної доставки';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement =>
+ 'Зменшення ваги помилки';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ 'Вага, яка була знята з маршруту після невдалої доставки';
+
+ @override
+ String get appSettings_maxMessageRetries =>
+ 'Максимальна кількість повторних спроб надсилання повідомлення';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle =>
+ 'Кількість спроб повторного відправлення повідомлення перед тим, як позначити його як невдале';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
+ }
+
+ @override
+ String get appSettings_battery => 'Батарея';
+
+ @override
+ String get appSettings_batteryChemistry => 'Хімія батареї';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return 'Встановити для пристрою ($deviceName)';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst =>
+ 'Підключіть пристрій, щоб вибрати';
+
+ @override
+ String get appSettings_batteryNmc => '18650 NMC (3.0-4.2В)';
+
+ @override
+ String get appSettings_batteryLifepo4 => 'LiFePO4 (2.6-3.65В)';
+
+ @override
+ String get appSettings_batteryLipo => 'LiPo (3.0-4.2В)';
+
+ @override
+ String get appSettings_mapDisplay => 'Відображення карти';
+
+ @override
+ String get appSettings_showRepeaters => 'Показувати ретранслятори';
+
+ @override
+ String get appSettings_showRepeatersSubtitle =>
+ 'Відображати вузли-ретранслятори на карті';
+
+ @override
+ String get appSettings_showChatNodes => 'Показувати вузли чату';
+
+ @override
+ String get appSettings_showChatNodesSubtitle =>
+ 'Відображати вузли чату на карті';
+
+ @override
+ String get appSettings_showOtherNodes => 'Показувати інші вузли';
+
+ @override
+ String get appSettings_showOtherNodesSubtitle =>
+ 'Відображати інші типи вузлів на карті';
+
+ @override
+ String get appSettings_timeFilter => 'Фільтр часу';
+
+ @override
+ String get appSettings_timeFilterShowAll => 'Показати всі вузли';
+
+ @override
+ String appSettings_timeFilterShowLast(int hours) {
+ return 'Показати вузли за останні $hours год';
+ }
+
+ @override
+ String get appSettings_mapTimeFilter => 'Фільтр часу карти';
+
+ @override
+ String get appSettings_showNodesDiscoveredWithin =>
+ 'Показувати вузли, виявлені за:';
+
+ @override
+ String get appSettings_allTime => 'Весь час';
+
+ @override
+ String get appSettings_lastHour => 'Останню годину';
+
+ @override
+ String get appSettings_last6Hours => 'Останні 6 годин';
+
+ @override
+ String get appSettings_last24Hours => 'Останні 24 години';
+
+ @override
+ String get appSettings_lastWeek => 'Минулий тиждень';
+
+ @override
+ String get appSettings_offlineMapCache => 'Офлайн-кеш карти';
+
+ @override
+ String get appSettings_unitsTitle => 'одиниці';
+
+ @override
+ String get appSettings_unitsMetric => 'Метричний (м / км)';
+
+ @override
+ String get appSettings_unitsImperial => 'Імперська (ft / mi)';
+
+ @override
+ String get appSettings_noAreaSelected => 'Область не вибрано';
+
+ @override
+ String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
+ return 'Вибрана область (зум $minZoom-$maxZoom)';
+ }
+
+ @override
+ String get appSettings_debugCard => 'Налагодження';
+
+ @override
+ String get appSettings_appDebugLogging => 'Логування налагодження програми';
+
+ @override
+ String get appSettings_appDebugLoggingSubtitle =>
+ 'Записувати повідомлення налагодження програми в лог для усунення несправностей.';
+
+ @override
+ String get appSettings_appDebugLoggingEnabled =>
+ 'Логування налагодження програми увімкнено';
+
+ @override
+ String get appSettings_appDebugLoggingDisabled =>
+ 'Налагодження програми вимкнено.';
+
+ @override
+ String get contacts_title => 'Контакти';
+
+ @override
+ String get contacts_noContacts => 'Контактів не знайдено.';
+
+ @override
+ String get contacts_contactsWillAppear =>
+ 'Контакти з\'являться, коли пристрої надішлють оголошення.';
+
+ @override
+ String get contacts_unread => 'Непрочитане';
+
+ @override
+ String get contacts_searchContactsNoNumber => 'Пошук контактів...';
+
+ @override
+ String contacts_searchContacts(int number, String str) {
+ return 'Пошук контактів...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return 'Пошук $number$str улюблених...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return 'Пошук $number$str користувачів...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return 'Пошук $number$str ретрансляторів...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return 'Пошук $number$str серверів кімнат...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => 'Немає непрочитаних контактів';
+
+ @override
+ String get contacts_noContactsFound => 'Контактів або груп не знайдено.';
+
+ @override
+ String get contacts_deleteContact => 'Видалити контакт';
+
+ @override
+ String contacts_removeConfirm(String contactName) {
+ return 'Видалити $contactName з контактів?';
+ }
+
+ @override
+ String get contacts_manageRepeater => 'Керувати ретранслятором';
+
+ @override
+ String get contacts_manageRoom => 'Керувати сервером кімнати';
+
+ @override
+ String get contacts_roomLogin => 'Вхід у кімнату';
+
+ @override
+ String get contacts_openChat => 'Відкрити чат';
+
+ @override
+ String get contacts_editGroup => 'Редагувати групу';
+
+ @override
+ String get contacts_deleteGroup => 'Видалити групу';
+
+ @override
+ String contacts_deleteGroupConfirm(String groupName) {
+ return 'Видалити $groupName?';
+ }
+
+ @override
+ String get contacts_newGroup => 'Нова група';
+
+ @override
+ String get contacts_groupName => 'Назва групи';
+
+ @override
+ String get contacts_groupNameRequired => 'Назва групи обов\'язкова.';
+
+ @override
+ String get contacts_groupNameReserved => 'Ця назва групи зарезервована';
+
+ @override
+ String contacts_groupAlreadyExists(String name) {
+ return 'Група «$name» вже існує.';
+ }
+
+ @override
+ String get contacts_filterContacts => 'Фільтрувати контакти...';
+
+ @override
+ String get contacts_noContactsMatchFilter =>
+ 'Жоден контакт не відповідає фільтру.';
+
+ @override
+ String get contacts_noMembers => 'Немає учасників';
+
+ @override
+ String get contacts_lastSeenNow => 'В мережі';
+
+ @override
+ String contacts_lastSeenMinsAgo(int minutes) {
+ return 'В мережі $minutes хв. тому';
+ }
+
+ @override
+ String get contacts_lastSeenHourAgo => 'В мережі 1 годину тому';
+
+ @override
+ String contacts_lastSeenHoursAgo(int hours) {
+ return 'В мережі $hours год. тому';
+ }
+
+ @override
+ String get contacts_lastSeenDayAgo => 'В мережі 1 день тому';
+
+ @override
+ String contacts_lastSeenDaysAgo(int days) {
+ return 'В мережі $days дн. тому';
+ }
+
+ @override
+ String get contact_info => 'Контактна інформація';
+
+ @override
+ String get contact_settings => 'Налаштування контактів';
+
+ @override
+ String get contact_telemetry => 'Телеметрія';
+
+ @override
+ String get contact_lastSeen => 'Останній раз бачили';
+
+ @override
+ String get contact_clearChat => 'Очистити чат';
+
+ @override
+ String get contact_teleBase => 'Базовий телебачення';
+
+ @override
+ String get contact_teleBaseSubtitle =>
+ 'Дозволити спільний доступ до рівня заряду батареї та базової телеметрії';
+
+ @override
+ String get contact_teleLoc => 'Розташування телеметрії';
+
+ @override
+ String get contact_teleLocSubtitle =>
+ 'Дозволити спільне використання даних про місцеположення';
+
+ @override
+ String get contact_teleEnv => 'Середовище телеметрії';
+
+ @override
+ String get contact_teleEnvSubtitle =>
+ 'Дозволити спільний доступ до даних датчиків середовища';
+
+ @override
+ String get channels_title => 'Канали';
+
+ @override
+ String get channels_noChannelsConfigured => 'Канали не налаштовані';
+
+ @override
+ String get channels_addPublicChannel => 'Додати публічний канал';
+
+ @override
+ String get channels_searchChannels => 'Пошук каналів...';
+
+ @override
+ String get channels_noChannelsFound => 'Каналів не знайдено';
+
+ @override
+ String channels_channelIndex(int index) {
+ return 'Канал $index';
+ }
+
+ @override
+ String get channels_hashtagChannel => 'Канал з хештегом';
+
+ @override
+ String get channels_public => 'Публічний';
+
+ @override
+ String get channels_private => 'Приватний';
+
+ @override
+ String get channels_publicChannel => 'Публічний канал';
+
+ @override
+ String get channels_privateChannel => 'Приватний канал';
+
+ @override
+ String get channels_editChannel => 'Редагувати канал';
+
+ @override
+ String get channels_muteChannel => 'Вимкнути сповіщення каналу';
+
+ @override
+ String get channels_unmuteChannel => 'Увімкнути сповіщення каналу';
+
+ @override
+ String get channels_deleteChannel => 'Видалити канал';
+
+ @override
+ String channels_deleteChannelConfirm(String name) {
+ return 'Видалити $name? Це не можна скасувати.';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return 'Не вдалося видалити канал \"$name\"';
+ }
+
+ @override
+ String channels_channelDeleted(String name) {
+ return 'Канал «$name» видалено';
+ }
+
+ @override
+ String get channels_addChannel => 'Додати канал';
+
+ @override
+ String get channels_channelIndexLabel => 'Індекс каналу';
+
+ @override
+ String get channels_channelName => 'Назва каналу';
+
+ @override
+ String get channels_usePublicChannel => 'Використовувати публічний канал';
+
+ @override
+ String get channels_standardPublicPsk => 'Стандартний публічний PSK';
+
+ @override
+ String get channels_pskHex => 'PSK (Hex)';
+
+ @override
+ String get channels_generateRandomPsk => 'Згенерувати випадковий ключ PSK';
+
+ @override
+ String get channels_enterChannelName => 'Будь ласка, введіть назву каналу';
+
+ @override
+ String get channels_pskMustBe32Hex =>
+ 'PSK має складатися з 32 шістнадцяткових символів.';
+
+ @override
+ String channels_channelAdded(String name) {
+ return 'Канал «$name» додано';
+ }
+
+ @override
+ String channels_editChannelTitle(int index) {
+ return 'Редагувати канал $index';
+ }
+
+ @override
+ String get channels_smazCompression => 'Стиснення SMAZ';
+
+ @override
+ String channels_channelUpdated(String name) {
+ return 'Канал «$name» оновлено';
+ }
+
+ @override
+ String get channels_publicChannelAdded => 'Публічний канал додано';
+
+ @override
+ String get channels_sortBy => 'Сортувати за';
+
+ @override
+ String get channels_sortManual => 'Вручну';
+
+ @override
+ String get channels_sortAZ => 'А-Я';
+
+ @override
+ String get channels_sortLatestMessages => 'Останні повідомлення';
+
+ @override
+ String get channels_sortUnread => 'Непрочитані';
+
+ @override
+ String get channels_createPrivateChannel => 'Створити приватний канал';
+
+ @override
+ String get channels_createPrivateChannelDesc => 'Захищено секретним ключем.';
+
+ @override
+ String get channels_joinPrivateChannel => 'Приєднатися до приватного каналу';
+
+ @override
+ String get channels_joinPrivateChannelDesc => 'Ввести секретний ключ вручну.';
+
+ @override
+ String get channels_joinPublicChannel => 'Приєднатися до публічного каналу';
+
+ @override
+ String get channels_joinPublicChannelDesc =>
+ 'Будь-хто може приєднатися до цього каналу.';
+
+ @override
+ String get channels_joinHashtagChannel => 'Приєднатися до каналу з хештегом';
+
+ @override
+ String get channels_joinHashtagChannelDesc =>
+ 'Будь-хто може приєднатися до каналів #hashtag.';
+
+ @override
+ String get channels_scanQrCode => 'Сканувати QR-код';
+
+ @override
+ String get channels_scanQrCodeComingSoon => 'Скоро буде';
+
+ @override
+ String get channels_enterHashtag => 'Введіть хештег';
+
+ @override
+ String get channels_hashtagHint => 'напр. #команда';
+
+ @override
+ String get chat_noMessages => 'Поки немає повідомлень.';
+
+ @override
+ String get chat_sendMessage => 'Надіслати повідомлення';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return 'Надіслати повідомлення $contactName';
+ }
+
+ @override
+ String get chat_sendMessageToStart => 'Надішліть повідомлення, щоб почати';
+
+ @override
+ String get chat_originalMessageNotFound =>
+ 'Оригінальне повідомлення не знайдено';
+
+ @override
+ String chat_replyingTo(String name) {
+ return 'Відповідь $name';
+ }
+
+ @override
+ String chat_replyTo(String name) {
+ return 'Відповісти $name';
+ }
+
+ @override
+ String get chat_location => 'Розташування';
+
+ @override
+ String get chat_typeMessage => 'Введіть повідомлення...';
+
+ @override
+ String chat_messageTooLong(int maxBytes) {
+ return 'Повідомлення занадто довге (макс. $maxBytes байт).';
+ }
+
+ @override
+ String get chat_messageCopied => 'Повідомлення скопійовано';
+
+ @override
+ String get chat_messageDeleted => 'Повідомлення видалено';
+
+ @override
+ String get chat_retryingMessage => 'Спроба відновлення.';
+
+ @override
+ String chat_retryCount(int current, int max) {
+ return 'Повторна спроба $current/$max';
+ }
+
+ @override
+ String get chat_sendGif => 'Надіслати GIF';
+
+ @override
+ String get chat_reply => 'Відповісти';
+
+ @override
+ String get chat_addReaction => 'Додати реакцію';
+
+ @override
+ String get chat_me => 'Я';
+
+ @override
+ String get emojiCategorySmileys => 'Емодзі';
+
+ @override
+ String get emojiCategoryGestures => 'Жести';
+
+ @override
+ String get emojiCategoryHearts => 'Серця';
+
+ @override
+ String get emojiCategoryObjects => 'Об\'єкти';
+
+ @override
+ String get gifPicker_title => 'Вибрати GIF';
+
+ @override
+ String get gifPicker_searchHint => 'Пошук GIF...';
+
+ @override
+ String get gifPicker_poweredBy => 'На базі GIPHY';
+
+ @override
+ String get gifPicker_noGifsFound => 'GIF не знайдено';
+
+ @override
+ String get gifPicker_failedLoad => 'Не вдалося завантажити GIF-файли';
+
+ @override
+ String get gifPicker_failedSearch => 'Пошук GIF не вдався';
+
+ @override
+ String get gifPicker_noInternet => 'Немає інтернет-з\'єднання';
+
+ @override
+ String get debugLog_appTitle => 'Журнал налагодження програми';
+
+ @override
+ String get debugLog_bleTitle => 'Журнал налагодження BLE';
+
+ @override
+ String get debugLog_copyLog => 'Копіювати журнал';
+
+ @override
+ String get debugLog_clearLog => 'Очистити журнал';
+
+ @override
+ String get debugLog_copied => 'Журнал налагодження скопійовано';
+
+ @override
+ String get debugLog_bleCopied => 'Журнал BLE скопійовано';
+
+ @override
+ String get debugLog_noEntries =>
+ 'Поки що немає записів журналу налагодження.';
+
+ @override
+ String get debugLog_enableInSettings =>
+ 'Увімкніть налагодження програми в налаштуваннях';
+
+ @override
+ String get debugLog_frames => 'Кадри';
+
+ @override
+ String get debugLog_rawLogRx => 'Необроблений лог - RX';
+
+ @override
+ String get debugLog_noBleActivity => 'Поки що немає активності BLE.';
+
+ @override
+ String debugFrame_length(int count) {
+ return 'Довжина кадру: $count байт';
+ }
+
+ @override
+ String debugFrame_command(String value) {
+ return 'Команда: 0x$value';
+ }
+
+ @override
+ String get debugFrame_textMessageHeader => 'Повідомлення:';
+
+ @override
+ String debugFrame_destinationPubKey(String pubKey) {
+ return '- PubKey призначення: $pubKey';
+ }
+
+ @override
+ String debugFrame_timestamp(int timestamp) {
+ return '- Мітка часу: $timestamp';
+ }
+
+ @override
+ String debugFrame_flags(String value) {
+ return '- Прапорці: 0x$value';
+ }
+
+ @override
+ String debugFrame_textType(int type, String label) {
+ return '- Тип тексту: $type ($label)';
+ }
+
+ @override
+ String get debugFrame_textTypeCli => 'CLI';
+
+ @override
+ String get debugFrame_textTypePlain => 'Звичайний';
+
+ @override
+ String debugFrame_text(String text) {
+ return '- Текст: \"$text\"';
+ }
+
+ @override
+ String get debugFrame_hexDump => 'Дамп Hex:';
+
+ @override
+ String get chat_pathManagement => 'Керування шляхами';
+
+ @override
+ String get chat_ShowAllPaths => 'Показати всі шляхи';
+
+ @override
+ String get chat_routingMode => 'Режим маршрутизації';
+
+ @override
+ String get chat_autoUseSavedPath => 'Авто (використовувати збережений шлях)';
+
+ @override
+ String get chat_forceFloodMode => 'Примусово на всю мережу';
+
+ @override
+ String get chat_recentAckPaths =>
+ 'Недавні шляхи ACK (натисніть, щоб використати):';
+
+ @override
+ String get chat_pathHistoryFull =>
+ 'Історія шляхів заповнена. Видаліть записи, щоб додати нові.';
+
+ @override
+ String get chat_hopSingular => 'Стрибок';
+
+ @override
+ String get chat_hopPlural => 'стрибків';
+
+ @override
+ String chat_hopsCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'стрибків',
+ many: 'стрибків',
+ few: 'стрибки',
+ one: 'стрибок',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String get chat_successes => 'Успішно';
+
+ @override
+ String get chat_removePath => 'Видалити шлях';
+
+ @override
+ String get chat_noPathHistoryYet =>
+ 'Історія шляхів недоступна.\nНадішліть повідомлення, щоб виявити шляхи.';
+
+ @override
+ String get chat_pathActions => 'Дії зі шляхом:';
+
+ @override
+ String get chat_setCustomPath => 'Встановити власний шлях';
+
+ @override
+ String get chat_setCustomPathSubtitle => 'Вказати шлях маршрутизації вручну';
+
+ @override
+ String get chat_clearPath => 'Очистити шлях';
+
+ @override
+ String get chat_clearPathSubtitle =>
+ 'Примусово повторити пошук при наступному надсиланні';
+
+ @override
+ String get chat_pathCleared =>
+ 'Шлях очищено. Наступне повідомлення оновить маршрут.';
+
+ @override
+ String get chat_floodModeSubtitle =>
+ 'Використовувати перемикач маршрутизації в панелі програми';
+
+ @override
+ String get chat_floodModeEnabled =>
+ 'Увімкнено режим «на всю мережу». Перемикайте через іконку маршрутизації на панелі інструментів.';
+
+ @override
+ String get chat_fullPath => 'Повний шлях';
+
+ @override
+ String get chat_pathDetailsNotAvailable =>
+ 'Деталі шляху ще недоступні. Спробуйте надіслати повідомлення для оновлення.';
+
+ @override
+ String chat_pathSetHops(int hopCount, String status) {
+ String _temp0 = intl.Intl.pluralLogic(
+ hopCount,
+ locale: localeName,
+ other: 'стрибків',
+ many: 'стрибків',
+ few: 'стрибки',
+ one: 'стрибок',
+ );
+ return 'Шлях встановлено: $hopCount $_temp0 - $status';
+ }
+
+ @override
+ String get chat_pathSavedLocally =>
+ 'Збережено локально. Підключіться для синхронізації.';
+
+ @override
+ String get chat_pathDeviceConfirmed => 'Пристрій підтверджено.';
+
+ @override
+ String get chat_pathDeviceNotConfirmed => 'Пристрій ще не підтверджено.';
+
+ @override
+ String get chat_type => 'Ввід';
+
+ @override
+ String get chat_path => 'Шлях';
+
+ @override
+ String get chat_publicKey => 'Відкритий ключ';
+
+ @override
+ String get chat_compressOutgoingMessages => 'Стискати вихідні повідомлення';
+
+ @override
+ String get chat_floodForced => 'На всю мережу (примусово)';
+
+ @override
+ String get chat_directForced => 'Прямий (примусово)';
+
+ @override
+ String chat_hopsForced(int count) {
+ return '$count стрибків (примусово)';
+ }
+
+ @override
+ String get chat_floodAuto => 'На всю мережу (авто)';
+
+ @override
+ String get chat_direct => 'Прямий';
+
+ @override
+ String get chat_poiShared => 'Точкою інтересу поділилися';
+
+ @override
+ String chat_unread(int count) {
+ return 'Непрочитано: $count';
+ }
+
+ @override
+ String get chat_openLink => 'Відкрити посилання?';
+
+ @override
+ String get chat_openLinkConfirmation =>
+ 'Ви хочете відкрити це посилання у браузері?';
+
+ @override
+ String get chat_open => 'Відкрити';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return 'Не вдалося відкрити посилання: $url';
+ }
+
+ @override
+ String get chat_invalidLink => 'Невірний формат посилання';
+
+ @override
+ String get map_title => 'Карта вузлів';
+
+ @override
+ String get map_lineOfSight => 'Пряма видимість';
+
+ @override
+ String get map_losScreenTitle => 'Пряма видимість';
+
+ @override
+ String get map_noNodesWithLocation =>
+ 'Немає вузлів з даними про розташування';
+
+ @override
+ String get map_nodesNeedGps =>
+ 'Вузли повинні надавати свої GPS координати,\nщоб з\'явитися на карті.';
+
+ @override
+ String map_nodesCount(int count) {
+ return 'Вузли: $count';
+ }
+
+ @override
+ String map_pinsCount(int count) {
+ return 'Мітки: $count';
+ }
+
+ @override
+ String get map_chat => 'Чат';
+
+ @override
+ String get map_repeater => 'Ретранслятор';
+
+ @override
+ String get map_room => 'Кімната';
+
+ @override
+ String get map_sensor => 'Сенсор';
+
+ @override
+ String get map_pinDm => 'Ключ (DM)';
+
+ @override
+ String get map_pinPrivate => 'Замок (Приватний)';
+
+ @override
+ String get map_pinPublic => 'Ключ (Публічний)';
+
+ @override
+ String get map_lastSeen => 'Останній раз бачили';
+
+ @override
+ String get map_disconnectConfirm =>
+ 'Ви впевнені, що хочете відключитися від цього пристрою?';
+
+ @override
+ String get map_from => 'Від';
+
+ @override
+ String get map_source => 'Джерело';
+
+ @override
+ String get map_flags => 'Прапорці';
+
+ @override
+ String get map_shareMarkerHere => 'Поділитися маркером тут';
+
+ @override
+ String get map_setAsMyLocation => 'Встановити моє місцезнаходження';
+
+ @override
+ String get map_pinLabel => 'Мітка піна';
+
+ @override
+ String get map_label => 'Мітка';
+
+ @override
+ String get map_pointOfInterest => 'Точка інтересу';
+
+ @override
+ String get map_sendToContact => 'Надіслати контакту';
+
+ @override
+ String get map_sendToChannel => 'Надіслати в канал';
+
+ @override
+ String get map_noChannelsAvailable => 'Немає доступних каналів';
+
+ @override
+ String get map_publicLocationShare => 'Поділитися в публічному місці';
+
+ @override
+ String map_publicLocationShareConfirm(String channelLabel) {
+ return 'Ви збираєтеся поділитися розташуванням у $channelLabel. Цей канал публічний, і кожен, хто має ключ PSK, може це побачити.';
+ }
+
+ @override
+ String get map_connectToShareMarkers =>
+ 'Підключіться до пристрою, щоб поділитися маркерами';
+
+ @override
+ String get map_filterNodes => 'Фільтрувати вузли';
+
+ @override
+ String get map_nodeTypes => 'Типи вузлів';
+
+ @override
+ String get map_chatNodes => 'Вузли чату';
+
+ @override
+ String get map_repeaters => 'Ретранслятори';
+
+ @override
+ String get map_otherNodes => 'Інші вузли';
+
+ @override
+ String get map_showOverlaps => 'Перекриття ключа повторювача';
+
+ @override
+ String get map_keyPrefix => 'Префікс ключа';
+
+ @override
+ String get map_filterByKeyPrefix => 'Фільтрувати за префіксом ключа';
+
+ @override
+ String get map_publicKeyPrefix => 'Префікс відкритого ключа';
+
+ @override
+ String get map_markers => 'Маркери';
+
+ @override
+ String get map_showSharedMarkers => 'Показувати спільні маркери';
+
+ @override
+ String get map_showGuessedLocations =>
+ 'Показати місцезнаходження передбачених вузлів';
+
+ @override
+ String get map_showDiscoveryContacts => 'Показати контакти Відкриття';
+
+ @override
+ String get map_guessedLocation => 'Визначено місцезнаходження';
+
+ @override
+ String get map_lastSeenTime => 'Час останньої активності';
+
+ @override
+ String get map_sharedPin => 'Спільний пін';
+
+ @override
+ String get map_joinRoom => 'Приєднатися до кімнати';
+
+ @override
+ String get map_manageRepeater => 'Керувати ретранслятором';
+
+ @override
+ String get map_tapToAdd => 'Натисніть на вузли, щоб додати їх до шляху';
+
+ @override
+ String get map_runTrace => 'Виконати трасування шляху';
+
+ @override
+ String get map_runTraceWithReturnPath => 'Повернутися назад тим же шляхом';
+
+ @override
+ String get map_removeLast => 'Видалити останній';
+
+ @override
+ String get map_pathTraceCancelled => 'Відмінується трасування шляху';
+
+ @override
+ String get mapCache_title => 'Офлайн-кеш карти';
+
+ @override
+ String get mapCache_selectAreaFirst =>
+ 'Спершу виберіть область для кешування';
+
+ @override
+ String get mapCache_noTilesToDownload =>
+ 'Немає плиток для завантаження в цій області.';
+
+ @override
+ String get mapCache_downloadTilesTitle => 'Завантажити плитки';
+
+ @override
+ String mapCache_downloadTilesPrompt(int count) {
+ return 'Завантажити $count плиток для використання офлайн?';
+ }
+
+ @override
+ String get mapCache_downloadAction => 'Завантажити';
+
+ @override
+ String mapCache_cachedTiles(int count) {
+ return 'Закешовано $count плиток';
+ }
+
+ @override
+ String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
+ return 'Плитки в кеші ($downloaded) ($failed помилок)';
+ }
+
+ @override
+ String get mapCache_clearOfflineCacheTitle => 'Очистити офлайн-кеш';
+
+ @override
+ String get mapCache_clearOfflineCachePrompt =>
+ 'Видалити всі закешовані плитки карти?';
+
+ @override
+ String get mapCache_offlineCacheCleared => 'Офлайн-кеш очищено.';
+
+ @override
+ String get mapCache_noAreaSelected => 'Область не вибрано';
+
+ @override
+ String get mapCache_cacheArea => 'Область кешування';
+
+ @override
+ String get mapCache_useCurrentView => 'Використати поточний вигляд';
+
+ @override
+ String get mapCache_zoomRange => 'Діапазон масштабування';
+
+ @override
+ String mapCache_estimatedTiles(int count) {
+ return 'Оцінка плиток: $count';
+ }
+
+ @override
+ String mapCache_downloadedTiles(int completed, int total) {
+ return 'Завантажено $completed / $total';
+ }
+
+ @override
+ String get mapCache_downloadTilesButton => 'Завантажити плитки';
+
+ @override
+ String get mapCache_clearCacheButton => 'Очистити кеш';
+
+ @override
+ String mapCache_failedDownloads(int count) {
+ return 'Невдалі завантаження: $count';
+ }
+
+ @override
+ String mapCache_boundsLabel(
+ String north,
+ String south,
+ String east,
+ String west,
+ ) {
+ return 'Пн $north, Пд $south, Сх $east, Зх $west';
+ }
+
+ @override
+ String get time_justNow => 'Тільки що';
+
+ @override
+ String time_minutesAgo(int minutes) {
+ return '$minutes хв. тому';
+ }
+
+ @override
+ String time_hoursAgo(int hours) {
+ return '$hours год. тому';
+ }
+
+ @override
+ String time_daysAgo(int days) {
+ return '$days дн. тому';
+ }
+
+ @override
+ String get time_hour => 'година';
+
+ @override
+ String get time_hours => 'годин';
+
+ @override
+ String get time_day => 'день';
+
+ @override
+ String get time_days => 'днів';
+
+ @override
+ String get time_week => 'тиждень';
+
+ @override
+ String get time_weeks => 'тижнів';
+
+ @override
+ String get time_month => 'місяць';
+
+ @override
+ String get time_months => 'місяців';
+
+ @override
+ String get time_minutes => 'хвилин';
+
+ @override
+ String get time_allTime => 'Весь час';
+
+ @override
+ String get dialog_disconnect => 'Відключити';
+
+ @override
+ String get dialog_disconnectConfirm =>
+ 'Ви впевнені, що хочете відключитися від цього пристрою?';
+
+ @override
+ String get login_repeaterLogin => 'Вхід у ретранслятор';
+
+ @override
+ String get login_roomLogin => 'Вхід у кімнату';
+
+ @override
+ String get login_password => 'Пароль';
+
+ @override
+ String get login_enterPassword => 'Введіть пароль';
+
+ @override
+ String get login_savePassword => 'Зберегти пароль';
+
+ @override
+ String get login_savePasswordSubtitle =>
+ 'Пароль буде надійно збережено на цьому пристрої.';
+
+ @override
+ String get login_repeaterDescription =>
+ 'Введіть пароль ретранслятора для доступу до налаштувань та статусу.';
+
+ @override
+ String get login_roomDescription =>
+ 'Введіть пароль кімнати для доступу до налаштувань та статусу.';
+
+ @override
+ String get login_routing => 'Маршрутизація';
+
+ @override
+ String get login_routingMode => 'Режим маршрутизації';
+
+ @override
+ String get login_autoUseSavedPath => 'Авто (використовувати збережений шлях)';
+
+ @override
+ String get login_forceFloodMode => 'Примусово на всю мережу';
+
+ @override
+ String get login_managePaths => 'Керувати шляхами';
+
+ @override
+ String get login_login => 'Вхід';
+
+ @override
+ String login_attempt(int current, int max) {
+ return 'Спроба $current/$max';
+ }
+
+ @override
+ String login_failed(String error) {
+ return 'Вхід не вдався: $error';
+ }
+
+ @override
+ String get login_failedMessage =>
+ 'Вхід не вдався. Або пароль неправильний, або ретранслятор недосяжний.';
+
+ @override
+ String get common_reload => 'Перезавантажити';
+
+ @override
+ String get common_clear => 'Очистити';
+
+ @override
+ String path_currentPath(String path) {
+ return 'Поточний шлях: $path';
+ }
+
+ @override
+ String path_usingHopsPath(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'стрибками',
+ many: 'стрибками',
+ few: 'стрибками',
+ one: 'стрибком',
+ );
+ return 'Використання шляху з $count $_temp0';
+ }
+
+ @override
+ String get path_enterCustomPath => 'Ввести власний шлях';
+
+ @override
+ String get path_currentPathLabel => 'Поточний шлях';
+
+ @override
+ String get path_hexPrefixInstructions =>
+ 'Введіть 2-символьні hex-префікси для кожного стрибка, розділені комами.';
+
+ @override
+ String get path_hexPrefixExample =>
+ 'Приклад: A1,F2,3C (кожен вузол використовує перший байт свого відкритого ключа).';
+
+ @override
+ String get path_labelHexPrefixes => 'Hex-префікси';
+
+ @override
+ String get path_helperMaxHops =>
+ 'Макс. 64 стрибки. Кожен префікс - 2 шістнадцяткові символи (1 байт)';
+
+ @override
+ String get path_selectFromContacts => 'Вибрати з контактів:';
+
+ @override
+ String get path_noRepeatersFound =>
+ 'Ретрансляторів або серверів кімнат не знайдено.';
+
+ @override
+ String get path_customPathsRequire =>
+ 'Власні шляхи вимагають проміжних вузлів, які можуть передавати повідомлення.';
+
+ @override
+ String path_invalidHexPrefixes(String prefixes) {
+ return 'Некоректні hex-префікси: $prefixes';
+ }
+
+ @override
+ String get path_tooLong => 'Шлях занадто довгий. Максимум 64 стрибки.';
+
+ @override
+ String get path_setPath => 'Встановити шлях';
+
+ @override
+ String get repeater_management => 'Керування ретранслятором';
+
+ @override
+ String get room_management => 'Адміністрування сервера кімнати';
+
+ @override
+ String get repeater_guest => 'Інформація про ретранслятор';
+
+ @override
+ String get room_guest => 'Інформація про сервер кімнати';
+
+ @override
+ String get repeater_managementTools => 'Інструменти керування';
+
+ @override
+ String get repeater_guestTools => 'Інструменти для гостей';
+
+ @override
+ String get repeater_status => 'Статус';
+
+ @override
+ String get repeater_statusSubtitle =>
+ 'Показати статус, статистику та сусідів ретранслятора';
+
+ @override
+ String get repeater_telemetry => 'Телеметрія';
+
+ @override
+ String get repeater_telemetrySubtitle =>
+ 'Показати телеметрію сенсорів та статистику системи';
+
+ @override
+ String get repeater_cli => 'CLI';
+
+ @override
+ String get repeater_cliSubtitle => 'Надіслати команди ретранслятору';
+
+ @override
+ String get repeater_neighbors => 'Сусіди';
+
+ @override
+ String get repeater_neighborsSubtitle =>
+ 'Показати сусідів нульового стрибка.';
+
+ @override
+ String get repeater_settings => 'Налаштування';
+
+ @override
+ String get repeater_settingsSubtitle => 'Налаштувати параметри ретранслятора';
+
+ @override
+ String get repeater_clockSyncAfterLogin => 'Синхронізація годин після входу';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle =>
+ 'Автоматично надсилати повідомлення \"синхронізація годин\" після успішного входу.';
+
+ @override
+ String get repeater_statusTitle => 'Статус ретранслятора';
+
+ @override
+ String get repeater_routingMode => 'Режим маршрутизації';
+
+ @override
+ String get repeater_autoUseSavedPath =>
+ 'Авто (використовувати збережений шлях)';
+
+ @override
+ String get repeater_forceFloodMode => 'Примусово на всю мережу';
+
+ @override
+ String get repeater_pathManagement => 'Керування шляхами';
+
+ @override
+ String get repeater_refresh => 'Оновити';
+
+ @override
+ String get repeater_statusRequestTimeout =>
+ 'Час очікування запиту статусу вичерпано.';
+
+ @override
+ String repeater_errorLoadingStatus(String error) {
+ return 'Помилка завантаження статусу: $error';
+ }
+
+ @override
+ String get repeater_systemInformation => 'Системна інформація';
+
+ @override
+ String get repeater_battery => 'Батарея';
+
+ @override
+ String get repeater_clockAtLogin => 'Годинник (при вході)';
+
+ @override
+ String get repeater_uptime => 'Час роботи';
+
+ @override
+ String get repeater_queueLength => 'Довжина черги';
+
+ @override
+ String get repeater_debugFlags => 'Прапорці налагодження';
+
+ @override
+ String get repeater_radioStatistics => 'Статистика радіо';
+
+ @override
+ String get repeater_lastRssi => 'Останній RSSI';
+
+ @override
+ String get repeater_lastSnr => 'Останній SNR';
+
+ @override
+ String get repeater_noiseFloor => 'Рівень шуму';
+
+ @override
+ String get repeater_txAirtime => 'Ефірний час TX';
+
+ @override
+ String get repeater_rxAirtime => 'Ефірний час RX';
+
+ @override
+ String get repeater_packetStatistics => 'Статистика пакетів';
+
+ @override
+ String get repeater_sent => 'Надіслано';
+
+ @override
+ String get repeater_received => 'Отримано';
+
+ @override
+ String get repeater_duplicates => 'Дублікати';
+
+ @override
+ String repeater_daysHoursMinsSecs(
+ int days,
+ int hours,
+ int minutes,
+ int seconds,
+ ) {
+ return '$days дн. $hours год $minutes хв $seconds с';
+ }
+
+ @override
+ String repeater_packetTxTotal(int total, String flood, String direct) {
+ return 'Всього: $total, На всю мережу: $flood, Прямі: $direct';
+ }
+
+ @override
+ String repeater_packetRxTotal(int total, String flood, String direct) {
+ return 'Всього: $total, На всю мережу: $flood, Прямі: $direct';
+ }
+
+ @override
+ String repeater_duplicatesFloodDirect(String flood, String direct) {
+ return 'На всю мережу: $flood, Прямі: $direct';
+ }
+
+ @override
+ String repeater_duplicatesTotal(int total) {
+ return 'Всього: $total';
+ }
+
+ @override
+ String get repeater_settingsTitle => 'Налаштування ретранслятора';
+
+ @override
+ String get repeater_basicSettings => 'Основні налаштування';
+
+ @override
+ String get repeater_repeaterName => 'Ім\'я ретранслятора';
+
+ @override
+ String get repeater_repeaterNameHelper =>
+ 'Показати ім\'я цього ретранслятора';
+
+ @override
+ String get repeater_adminPassword => 'Пароль адміністратора';
+
+ @override
+ String get repeater_adminPasswordHelper => 'Пароль повного доступу';
+
+ @override
+ String get repeater_guestPassword => 'Гостьовий пароль';
+
+ @override
+ String get repeater_guestPasswordHelper =>
+ 'Доступ лише для читання з паролем';
+
+ @override
+ String get repeater_radioSettings => 'Налаштування радіо';
+
+ @override
+ String get repeater_frequencyMhz => 'Частота (МГц)';
+
+ @override
+ String get repeater_frequencyHelper => '300-2500 МГц';
+
+ @override
+ String get repeater_txPower => 'Потужність TX';
+
+ @override
+ String get repeater_txPowerHelper => '1-30 дБм';
+
+ @override
+ String get repeater_bandwidth => 'Смуга пропускання';
+
+ @override
+ String get repeater_spreadingFactor => 'Коефіцієнт розширення';
+
+ @override
+ String get repeater_codingRate => 'Швидкість кодування';
+
+ @override
+ String get repeater_locationSettings => 'Налаштування розташування';
+
+ @override
+ String get repeater_latitude => 'Широта';
+
+ @override
+ String get repeater_latitudeHelper =>
+ 'Десяткові градуси (наприклад, 37.7749)';
+
+ @override
+ String get repeater_longitude => 'Довгота';
+
+ @override
+ String get repeater_longitudeHelper =>
+ 'Десяткові градуси (наприклад, -122.4194)';
+
+ @override
+ String get repeater_features => 'Функції';
+
+ @override
+ String get repeater_packetForwarding => 'Пересилання пакетів';
+
+ @override
+ String get repeater_packetForwardingSubtitle =>
+ 'Дозволити ретранслятору пересилати пакети';
+
+ @override
+ String get repeater_guestAccess => 'Гостьовий доступ';
+
+ @override
+ String get repeater_guestAccessSubtitle =>
+ 'Дозволити гостьовий доступ лише для читання';
+
+ @override
+ String get repeater_privacyMode => 'Режим приватності';
+
+ @override
+ String get repeater_privacyModeSubtitle =>
+ 'Приховати ім\'я/розташування в оголошеннях';
+
+ @override
+ String get repeater_advertisementSettings => 'Налаштування оголошень';
+
+ @override
+ String get repeater_localAdvertInterval =>
+ 'Інтервал локальних оголошень (0 стрибків)';
+
+ @override
+ String repeater_localAdvertIntervalMinutes(int minutes) {
+ return '$minutes хвилин';
+ }
+
+ @override
+ String get repeater_floodAdvertInterval =>
+ 'Інтервал оголошень на всю мережу (flood)';
+
+ @override
+ String repeater_floodAdvertIntervalHours(int hours) {
+ return '$hours годин';
+ }
+
+ @override
+ String get repeater_encryptedAdvertInterval =>
+ 'Інтервал зашифрованих оголошень';
+
+ @override
+ String get repeater_dangerZone => 'Небезпечна зона';
+
+ @override
+ String get repeater_rebootRepeater => 'Перезавантажити ретранслятор';
+
+ @override
+ String get repeater_rebootRepeaterSubtitle =>
+ 'Скинути пристрій ретранслятора';
+
+ @override
+ String get repeater_rebootRepeaterConfirm =>
+ 'Ви впевнені, що хочете перезавантажити цей ретранслятор?';
+
+ @override
+ String get repeater_regenerateIdentityKey =>
+ 'Перегенерувати ключ ідентичності';
+
+ @override
+ String get repeater_regenerateIdentityKeySubtitle =>
+ 'Згенерувати нову пару ключів (публічний/приватний)';
+
+ @override
+ String get repeater_regenerateIdentityKeyConfirm =>
+ 'Це створить нову ідентичність для ретранслятора. Продовжити?';
+
+ @override
+ String get repeater_eraseFileSystem => 'Очистити файлову систему';
+
+ @override
+ String get repeater_eraseFileSystemSubtitle =>
+ 'Відформатувати файлову систему ретранслятора';
+
+ @override
+ String get repeater_eraseFileSystemConfirm =>
+ 'УВАГА: Це видалить всі дані з ретранслятора. Це не можна скасувати!';
+
+ @override
+ String get repeater_eraseSerialOnly =>
+ 'Очищення доступне лише через послідовну консоль.';
+
+ @override
+ String repeater_commandSent(String command) {
+ return 'Команда надіслана: $command';
+ }
+
+ @override
+ String repeater_errorSendingCommand(String error) {
+ return 'Помилка надсилання команди: $error';
+ }
+
+ @override
+ String get repeater_confirm => 'Підтвердити';
+
+ @override
+ String get repeater_settingsSaved => 'Налаштування успішно збережено.';
+
+ @override
+ String repeater_errorSavingSettings(String error) {
+ return 'Помилка збереження налаштувань: $error';
+ }
+
+ @override
+ String get repeater_refreshBasicSettings => 'Оновити основні налаштування';
+
+ @override
+ String get repeater_refreshRadioSettings => 'Оновити налаштування радіо';
+
+ @override
+ String get repeater_refreshTxPower => 'Оновити потужність TX';
+
+ @override
+ String get repeater_refreshLocationSettings =>
+ 'Оновити налаштування розташування';
+
+ @override
+ String get repeater_refreshPacketForwarding => 'Оновити пересилання пакетів';
+
+ @override
+ String get repeater_refreshGuestAccess => 'Оновити гостьовий доступ';
+
+ @override
+ String get repeater_refreshPrivacyMode => 'Оновити режим приватності';
+
+ @override
+ String get repeater_refreshAdvertisementSettings =>
+ 'Оновити налаштування оголошень';
+
+ @override
+ String repeater_refreshed(String label) {
+ return '$label оновлено';
+ }
+
+ @override
+ String repeater_errorRefreshing(String label) {
+ return 'Помилка оновлення $label';
+ }
+
+ @override
+ String get repeater_cliTitle => 'Ретранслятор CLI';
+
+ @override
+ String get repeater_debugNextCommand => 'Налагодити наступну команду';
+
+ @override
+ String get repeater_commandHelp => 'Довідка';
+
+ @override
+ String get repeater_clearHistory => 'Очистити історію';
+
+ @override
+ String get repeater_noCommandsSent => 'Команди ще не надсилалися.';
+
+ @override
+ String get repeater_typeCommandOrUseQuick =>
+ 'Введіть команду нижче або використовуйте швидкі команди';
+
+ @override
+ String get repeater_enterCommandHint => 'Введіть команду...';
+
+ @override
+ String get repeater_previousCommand => 'Попередня команда';
+
+ @override
+ String get repeater_nextCommand => 'Наступна команда';
+
+ @override
+ String get repeater_enterCommandFirst => 'Спершу введіть команду';
+
+ @override
+ String get repeater_cliCommandFrameTitle => 'Фрейм команди CLI';
+
+ @override
+ String repeater_cliCommandError(String error) {
+ return 'Помилка: $error';
+ }
+
+ @override
+ String get repeater_cliQuickGetName => 'Отримати ім\'я';
+
+ @override
+ String get repeater_cliQuickGetRadio => 'Отримати Радіо';
+
+ @override
+ String get repeater_cliQuickGetTx => 'Отримати TX';
+
+ @override
+ String get repeater_cliQuickNeighbors => 'Сусіди';
+
+ @override
+ String get repeater_cliQuickVersion => 'Версія';
+
+ @override
+ String get repeater_cliQuickAdvertise => 'Оголосити';
+
+ @override
+ String get repeater_cliQuickClock => 'Годинник';
+
+ @override
+ String get repeater_cliQuickClockSync => 'Синхронізація годинника';
+
+ @override
+ String get repeater_cliQuickDiscovery => 'Відкрити сусідів';
+
+ @override
+ String get repeater_cliHelpAdvert => 'Надсилає пакет оголошення';
+
+ @override
+ String get repeater_cliHelpReboot =>
+ 'Перезавантажує пристрій. (Зверніть увагу, ви можете отримати «Тайм-аут», що є нормальним)';
+
+ @override
+ String get repeater_cliHelpClock =>
+ 'Відображає поточний час за годинником кожного пристрою.';
+
+ @override
+ String get repeater_cliHelpPassword =>
+ 'Встановлює новий пароль адміністратора для пристрою.';
+
+ @override
+ String get repeater_cliHelpVersion =>
+ 'Відображає версію пристрою та дату збірки прошивки.';
+
+ @override
+ String get repeater_cliHelpClearStats =>
+ 'Скидає різні лічильники статистики до нуля.';
+
+ @override
+ String get repeater_cliHelpSetAf => 'Встановлює коефіцієнт ефірного часу.';
+
+ @override
+ String get repeater_cliHelpSetTx =>
+ 'Встановлює потужність передачі LoRa в дБм (для застосування потрібне перезавантаження).';
+
+ @override
+ String get repeater_cliHelpSetRepeat =>
+ 'Вмикає або вимикає роль ретранслятора для цього вузла.';
+
+ @override
+ String get repeater_cliHelpSetAllowReadOnly =>
+ '(Сервер кімнати) Якщо «увімкнено», порожній пароль дозволить вхід, але не дозволить публікувати в кімнаті. (тільки читання)';
+
+ @override
+ String get repeater_cliHelpSetFloodMax =>
+ 'Встановлює максимальну кількість стрибків для вхідних пакетів flood (якщо >= max, пакет не пересилається).';
+
+ @override
+ String get repeater_cliHelpSetIntThresh =>
+ 'Встановлює поріг інтерференції (в дБ). Значення за замовчуванням — 14. Встановлення на 0 вимикає виявлення інтерференції каналу.';
+
+ @override
+ String get repeater_cliHelpSetAgcResetInterval =>
+ 'Встановлює інтервал скидання автоматичного контролера посилення (AGC). Встановіть 0 для вимкнення.';
+
+ @override
+ String get repeater_cliHelpSetMultiAcks =>
+ 'Вмикає або вимикає функціональність подвійних ACK.';
+
+ @override
+ String get repeater_cliHelpSetAdvertInterval =>
+ 'Встановлює інтервал таймера для надсилання локального пакету оголошення (без ретрансляції). Встановіть 0 для вимкнення.';
+
+ @override
+ String get repeater_cliHelpSetFloodAdvertInterval =>
+ 'Встановлює інтервал таймера в годинах для надсилання пакету оголошення на всю мережу. Встановіть 0 для вимкнення.';
+
+ @override
+ String get repeater_cliHelpSetGuestPassword =>
+ 'Встановлює/оновлює гостьовий пароль. (для ретрансляторів гостьові підключення можуть надсилати запит «Get Stats»)';
+
+ @override
+ String get repeater_cliHelpSetName => 'Встановлює ім\'я для оголошення.';
+
+ @override
+ String get repeater_cliHelpSetLat =>
+ 'Встановлює широту для карти оголошень. (десяткові градуси)';
+
+ @override
+ String get repeater_cliHelpSetLon =>
+ 'Встановлює довготу для карти оголошень. (десяткові градуси)';
+
+ @override
+ String get repeater_cliHelpSetRadio =>
+ 'Повністю встановлює нові параметри радіо та зберігає їх у налаштуваннях. Потребує команди «перезавантаження» для застосування.';
+
+ @override
+ String get repeater_cliHelpSetRxDelay =>
+ 'Базові (експериментальні) параметри для застосування невеликої затримки до отриманих пакетів залежно від сили сигналу/оцінки. Встановіть 0 для вимкнення.';
+
+ @override
+ String get repeater_cliHelpSetTxDelay =>
+ 'Встановлює множник для часу роботи в режимі «на всю мережу» (flood) для пакету та системи випадкових слотів, щоб затримати його відправку (для зменшення ймовірності колізій).';
+
+ @override
+ String get repeater_cliHelpSetDirectTxDelay =>
+ 'Те саме, що й txdelay, але для застосування випадкової затримки при пересиланні пакетів у прямому режимі.';
+
+ @override
+ String get repeater_cliHelpSetBridgeEnabled => 'Увімкнути/Вимкнути міст.';
+
+ @override
+ String get repeater_cliHelpSetBridgeDelay =>
+ 'Встановити затримку перед пересиланням пакетів.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSource =>
+ 'Виберіть, чи буде міст ретранслювати отримані пакети або передані пакети.';
+
+ @override
+ String get repeater_cliHelpSetBridgeBaud =>
+ 'Встановити швидкість послідовного зв\'язку для мостів Rs232.';
+
+ @override
+ String get repeater_cliHelpSetBridgeSecret =>
+ 'Встановити секрет мосту для мостів espnow.';
+
+ @override
+ String get repeater_cliHelpSetAdcMultiplier =>
+ 'Встановлює власний множник для коригування повідомлюваної напруги батареї (підтримується лише на деяких платах).';
+
+ @override
+ String get repeater_cliHelpTempRadio =>
+ 'Встановлює тимчасові параметри радіо на задану кількість хвилин, потім повертається до початкових налаштувань. (не зберігає в налаштуваннях).';
+
+ @override
+ String get repeater_cliHelpSetPerm =>
+ 'Змінює ACL (список контролю доступу). Видаляє відповідний запис (за префіксом публічного ключа), якщо «permissions» дорівнює нулю. Додає новий запис, якщо hex публічного ключа повний і його немає в ACL. Оновлює запис на основі префікса публічного ключа. Біти дозволів залежать від ролі прошивки, але нижні 2 біти: 0 (Гість), 1 (Тільки читання), 2 (Читання/Запис), 3 (Адміністратор).';
+
+ @override
+ String get repeater_cliHelpGetBridgeType =>
+ 'Отримати тип мосту: немає, rs232, espnow';
+
+ @override
+ String get repeater_cliHelpLogStart =>
+ 'Починає запис пакетів у файлову систему.';
+
+ @override
+ String get repeater_cliHelpLogStop =>
+ 'Зупиняє запис пакетів у файлову систему.';
+
+ @override
+ String get repeater_cliHelpLogErase =>
+ 'Видаляє журнали пакетів з файлової системи.';
+
+ @override
+ String get repeater_cliHelpNeighbors =>
+ 'Показує список інших вузлів-ретрансляторів, почутих через оголошення без ретрансляції. Кожен рядок — id-hex-префікс:timestamp:snr-помножено-на-4';
+
+ @override
+ String get repeater_cliHelpNeighborRemove =>
+ 'Видаляє перший відповідний запис (за префіксом публічного ключа (hex)) зі списку сусідів.';
+
+ @override
+ String get repeater_cliHelpRegion =>
+ '(тільки серійний) Перелічує всі визначені регіони та поточні дозволи на оголошення «на всю мережу» (flood).';
+
+ @override
+ String get repeater_cliHelpRegionLoad =>
+ 'ПРИМІТКА: це спеціальний виклик кількох команд. Кожна наступна команда — це назва регіону (з відступом пробілами для позначення ієрархії батьків, мінімум один пробіл). Завершується надсиланням порожнього рядка/команди.';
+
+ @override
+ String get repeater_cliHelpRegionGet =>
+ 'Шукає регіон із заданим префіксом назви (або «» для глобальної області). Відповідає: «-> ім\'я-регіону (ім\'я-батька) \'F\'»';
+
+ @override
+ String get repeater_cliHelpRegionPut =>
+ 'Додає або оновлює визначення регіону з заданою назвою.';
+
+ @override
+ String get repeater_cliHelpRegionRemove =>
+ 'Видаляє визначення регіону з заданою назвою.';
+
+ @override
+ String get repeater_cliHelpRegionAllowf =>
+ 'Встановлює дозвіл «Flood» для заданого регіону. (\'\' для глобальної/успадкованої області)';
+
+ @override
+ String get repeater_cliHelpRegionDenyf =>
+ 'Видаляє дозвіл «Flood» для заданого регіону. (ПРИМІТКА: на даному етапі не рекомендується використовувати для глобальної/успадкованої області!! )';
+
+ @override
+ String get repeater_cliHelpRegionHome =>
+ 'Відповідає поточним «домашнім» регіоном. (Примітка: поки ніде не застосовується, зарезервовано для майбутнього використання)';
+
+ @override
+ String get repeater_cliHelpRegionHomeSet => 'Встановлює «домашній» регіон.';
+
+ @override
+ String get repeater_cliHelpRegionSave =>
+ 'Зберігає список/карту регіонів у сховищі.';
+
+ @override
+ String get repeater_cliHelpGps =>
+ 'Показує статус GPS. Коли GPS вимкнено, відповідає лише «вимкнено», якщо увімкнено — відповідає «увімкнено», статус, корекція, кількість супутників.';
+
+ @override
+ String get repeater_cliHelpGpsOnOff => 'Увімкнути/вимкнути GPS.';
+
+ @override
+ String get repeater_cliHelpGpsSync =>
+ 'Синхронізує час вузла з годинником GPS.';
+
+ @override
+ String get repeater_cliHelpGpsSetLoc =>
+ 'Встановлює позицію вузла за координатами GPS і зберігає в налаштуваннях.';
+
+ @override
+ String get repeater_cliHelpGpsAdvert =>
+ 'Надає конфігурацію оголошення розташування вузла:\n- none : не включати розташування в оголошення\n- share : ділитися розташуванням GPS (з SensorManager)\n- prefs : оголошувати розташування, збережене в налаштуваннях';
+
+ @override
+ String get repeater_cliHelpGpsAdvertSet =>
+ 'Встановлює конфігурацію оголошення розташування.';
+
+ @override
+ String get repeater_commandsListTitle => 'Список команд';
+
+ @override
+ String get repeater_commandsListNote =>
+ 'ПРИМІТКА: для різних команд «set»... також існує команда «get»...';
+
+ @override
+ String get repeater_general => 'Загальні';
+
+ @override
+ String get repeater_settingsCategory => 'Налаштування';
+
+ @override
+ String get repeater_bridge => 'Міст';
+
+ @override
+ String get repeater_logging => 'Логування';
+
+ @override
+ String get repeater_neighborsRepeaterOnly => 'Сусіди (Тільки ретранслятор)';
+
+ @override
+ String get repeater_regionManagementRepeaterOnly =>
+ 'Керування регіонами (Тільки ретранслятор)';
+
+ @override
+ String get repeater_regionNote =>
+ 'Команди регіонів були введені для керування визначеннями та дозволами регіонів.';
+
+ @override
+ String get repeater_gpsManagement => 'Керування GPS';
+
+ @override
+ String get repeater_gpsNote =>
+ 'Команда GPS була введена для керування питаннями, пов\'язаними з локацією.';
+
+ @override
+ String get telemetry_receivedData => 'Дані телеметрії отримано';
+
+ @override
+ String get telemetry_requestTimeout => 'Час запиту телеметрії вичерпано.';
+
+ @override
+ String telemetry_errorLoading(String error) {
+ return 'Помилка завантаження телеметрії: $error';
+ }
+
+ @override
+ String get telemetry_noData => 'Дані телеметрії недоступні.';
+
+ @override
+ String telemetry_channelTitle(int channel) {
+ return 'Канал $channel';
+ }
+
+ @override
+ String get telemetry_batteryLabel => 'Батарея';
+
+ @override
+ String get telemetry_voltageLabel => 'Напруга';
+
+ @override
+ String get telemetry_mcuTemperatureLabel => 'Температура MCU';
+
+ @override
+ String get telemetry_temperatureLabel => 'Температура';
+
+ @override
+ String get telemetry_currentLabel => 'Поточний струм';
+
+ @override
+ String telemetry_batteryValue(int percent, String volts) {
+ return '$percent% / $voltsВ';
+ }
+
+ @override
+ String telemetry_voltageValue(String volts) {
+ return '$voltsВ';
+ }
+
+ @override
+ String telemetry_currentValue(String amps) {
+ return '$ampsА';
+ }
+
+ @override
+ String telemetry_temperatureValue(String celsius, String fahrenheit) {
+ return '$celsius°C / $fahrenheit°F';
+ }
+
+ @override
+ String get neighbors_receivedData => 'Дані сусідів отримано';
+
+ @override
+ String get neighbors_requestTimedOut => 'Час запиту сусідів вичерпано.';
+
+ @override
+ String neighbors_errorLoading(String error) {
+ return 'Помилка завантаження сусідів: $error';
+ }
+
+ @override
+ String get neighbors_repeatersNeighbors => 'Ретранслятори-сусіди';
+
+ @override
+ String get neighbors_noData => 'Дані про сусідів недоступні.';
+
+ @override
+ String neighbors_unknownContact(String pubkey) {
+ return 'Невідомий відкритий ключ $pubkey';
+ }
+
+ @override
+ String neighbors_heardAgo(String time) {
+ return 'Почуто: $time тому';
+ }
+
+ @override
+ String get channelPath_title => 'Шлях пакету';
+
+ @override
+ String get channelPath_viewMap => 'Показати карту';
+
+ @override
+ String get channelPath_otherObservedPaths => 'Інші спостережувані шляхи';
+
+ @override
+ String get channelPath_repeaterHops => 'Стрибки ретранслятора';
+
+ @override
+ String get channelPath_noHopDetails =>
+ 'Деталі відправки не надані для цього пакету.';
+
+ @override
+ String get channelPath_messageDetails => 'Деталі повідомлення';
+
+ @override
+ String get channelPath_senderLabel => 'Відправник';
+
+ @override
+ String get channelPath_timeLabel => 'Час';
+
+ @override
+ String get channelPath_repeatsLabel => 'Повторення';
+
+ @override
+ String channelPath_pathLabel(int index) {
+ return 'Шлях $index';
+ }
+
+ @override
+ String get channelPath_observedLabel => 'Спостережено';
+
+ @override
+ String channelPath_observedPathTitle(int index, String hops) {
+ return 'Спостережуваний шлях $index • $hops';
+ }
+
+ @override
+ String get channelPath_noLocationData => 'Немає даних про розташування';
+
+ @override
+ String channelPath_timeWithDate(int day, int month, String time) {
+ return '$day/$month $time';
+ }
+
+ @override
+ String channelPath_timeOnly(String time) {
+ return '$time';
+ }
+
+ @override
+ String get channelPath_unknownPath => 'Невідомий';
+
+ @override
+ String get channelPath_floodPath => 'На всю мережу';
+
+ @override
+ String get channelPath_directPath => 'Прямий';
+
+ @override
+ String channelPath_observedZeroOf(int total) {
+ return '0 з $total стрибків';
+ }
+
+ @override
+ String channelPath_observedSomeOf(int observed, int total) {
+ return '$observed з $total стрибків';
+ }
+
+ @override
+ String get channelPath_mapTitle => 'Карта шляху';
+
+ @override
+ String get channelPath_noRepeaterLocations =>
+ 'Позиції ретрансляторів недоступні для цього шляху.';
+
+ @override
+ String channelPath_primaryPath(int index) {
+ return 'Шлях $index (Основний)';
+ }
+
+ @override
+ String get channelPath_pathLabelTitle => 'Шлях';
+
+ @override
+ String get channelPath_observedPathHeader => 'Спостережуваний шлях';
+
+ @override
+ String channelPath_selectedPathLabel(String label, String prefixes) {
+ return '$label • $prefixes';
+ }
+
+ @override
+ String get channelPath_noHopDetailsAvailable =>
+ 'Деталі стрибків недоступні для цього пакету.';
+
+ @override
+ String get channelPath_unknownRepeater => 'Невідомий ретранслятор';
+
+ @override
+ String get community_title => 'Спільнота';
+
+ @override
+ String get community_create => 'Створити спільноту';
+
+ @override
+ String get community_createDesc =>
+ 'Створити нову спільноту та поділитися через QR-код.';
+
+ @override
+ String get community_join => 'Приєднатися';
+
+ @override
+ String get community_joinTitle => 'Приєднатися до спільноти';
+
+ @override
+ String community_joinConfirmation(String name) {
+ return 'Ви бажаєте приєднатися до спільноти «$name»?';
+ }
+
+ @override
+ String get community_scanQr => 'Сканувати QR спільноти';
+
+ @override
+ String get community_scanInstructions =>
+ 'Наведіть камеру на QR-код спільноти.';
+
+ @override
+ String get community_showQr => 'Показати QR-код';
+
+ @override
+ String get community_publicChannel => 'Публічна спільнота';
+
+ @override
+ String get community_hashtagChannel => 'Хештег спільноти';
+
+ @override
+ String get community_name => 'Назва спільноти';
+
+ @override
+ String get community_enterName => 'Введіть назву спільноти';
+
+ @override
+ String community_created(String name) {
+ return 'Спільноту «$name» створено';
+ }
+
+ @override
+ String community_joined(String name) {
+ return 'Приєднався до спільноти «$name»';
+ }
+
+ @override
+ String get community_qrTitle => 'Поділитися спільнотою';
+
+ @override
+ String community_qrInstructions(String name) {
+ return 'Відскануйте цей QR-код, щоб приєднатися до $name';
+ }
+
+ @override
+ String get community_hashtagPrivacyHint =>
+ 'Канали хештегів спільноти доступні лише членам спільноти';
+
+ @override
+ String get community_invalidQrCode => 'Недійсний QR-код спільноти';
+
+ @override
+ String get community_alreadyMember => 'Вже учасник';
+
+ @override
+ String community_alreadyMemberMessage(String name) {
+ return 'Ви вже є учасником «$name».';
+ }
+
+ @override
+ String get community_addPublicChannel => 'Додати публічний канал спільноти';
+
+ @override
+ String get community_addPublicChannelHint =>
+ 'Автоматично додати публічний канал для цієї спільноти';
+
+ @override
+ String get community_noCommunities => 'Поки не приєднано до жодної групи.';
+
+ @override
+ String get community_scanOrCreate =>
+ 'Відскануйте QR-код або створіть спільноту, щоб почати';
+
+ @override
+ String get community_manageCommunities => 'Керувати спільнотами';
+
+ @override
+ String get community_delete => 'Покинути спільноту';
+
+ @override
+ String community_deleteConfirm(String name) {
+ return 'Покинути «$name»?';
+ }
+
+ @override
+ String community_deleteChannelsWarning(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'каналів',
+ many: 'каналів',
+ few: 'канали',
+ one: 'канал',
+ );
+ return 'Це також видалить $count $_temp0 та їх повідомлення.';
+ }
+
+ @override
+ String community_deleted(String name) {
+ return 'Спільноту «$name» покинуто';
+ }
+
+ @override
+ String get community_regenerateSecret => 'Перегенерувати секрет';
+
+ @override
+ String community_regenerateSecretConfirm(String name) {
+ return 'Перегенерувати секретний ключ для «$name»? Всі учасники повинні будуть відсканувати новий QR-код, щоб продовжити спілкування.';
+ }
+
+ @override
+ String get community_regenerate => 'Перегенерувати';
+
+ @override
+ String community_secretRegenerated(String name) {
+ return 'Секретний пароль для «$name» перегенеровано';
+ }
+
+ @override
+ String get community_updateSecret => 'Оновити секрет';
+
+ @override
+ String community_secretUpdated(String name) {
+ return 'Зміну секрету для «$name» оновлено';
+ }
+
+ @override
+ String community_scanToUpdateSecret(String name) {
+ return 'Відскануйте новий QR-код, щоб оновити пароль для «$name»';
+ }
+
+ @override
+ String get community_addHashtagChannel => 'Додати хештег спільноти';
+
+ @override
+ String get community_addHashtagChannelDesc =>
+ 'Додати канал хештегу для цієї спільноти';
+
+ @override
+ String get community_selectCommunity => 'Вибрати спільноту';
+
+ @override
+ String get community_regularHashtag => 'Звичайний хештег';
+
+ @override
+ String get community_regularHashtagDesc =>
+ 'Публічний хештег (будь-хто може приєднатися)';
+
+ @override
+ String get community_communityHashtag => 'Хештег спільноти';
+
+ @override
+ String get community_communityHashtagDesc =>
+ 'Ексклюзивно для членів спільноти';
+
+ @override
+ String community_forCommunity(String name) {
+ return 'Для $name';
+ }
+
+ @override
+ String get listFilter_tooltip => 'Фільтр та сортування';
+
+ @override
+ String get listFilter_sortBy => 'Сортувати за';
+
+ @override
+ String get listFilter_latestMessages => 'Останні повідомлення';
+
+ @override
+ String get listFilter_heardRecently => 'Нещодавно чули';
+
+ @override
+ String get listFilter_az => 'А-Я';
+
+ @override
+ String get listFilter_filters => 'Фільтри';
+
+ @override
+ String get listFilter_all => 'Все';
+
+ @override
+ String get listFilter_favorites => 'Улюблені';
+
+ @override
+ String get listFilter_addToFavorites => 'Додати до улюблених';
+
+ @override
+ String get listFilter_removeFromFavorites => 'Видалити зі списку улюблених';
+
+ @override
+ String get listFilter_users => 'Користувачі';
+
+ @override
+ String get listFilter_repeaters => 'Ретранслятори';
+
+ @override
+ String get listFilter_roomServers => 'Сервери кімнат';
+
+ @override
+ String get listFilter_unreadOnly => 'Тільки непрочитані повідомлення';
+
+ @override
+ String get listFilter_newGroup => 'Нова група';
+
+ @override
+ String get pathTrace_you => 'Ви';
+
+ @override
+ String get pathTrace_failed => 'Відстеження шляху не вдалося.';
+
+ @override
+ String get pathTrace_notAvailable => 'Трасування шляху недоступне.';
+
+ @override
+ String get pathTrace_refreshTooltip => 'Оновити Path Trace';
+
+ @override
+ String get pathTrace_someHopsNoLocation =>
+ 'Одне або більше хмелів відсутнє місце розташування!';
+
+ @override
+ String get pathTrace_clearTooltip => 'Очистити шлях';
+
+ @override
+ String get losSelectStartEnd =>
+ 'Виберіть початковий і кінцевий вузли для LOS.';
+
+ @override
+ String losRunFailed(String error) {
+ return 'Помилка перевірки прямої видимості: $error';
+ }
+
+ @override
+ String get losClearAllPoints => 'Очистити всі пункти';
+
+ @override
+ String get losRunToViewElevationProfile =>
+ 'Запустіть LOS, щоб переглянути профіль висоти';
+
+ @override
+ String get losMenuTitle => 'Меню LOS';
+
+ @override
+ String get losMenuSubtitle =>
+ 'Торкніться вузлів або утримуйте карту, щоб отримати власні точки';
+
+ @override
+ String get losShowDisplayNodes => 'Показати вузли відображення';
+
+ @override
+ String get losCustomPoints => 'Користувальницькі точки';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return 'Спеціальний $index';
+ }
+
+ @override
+ String get losPointA => 'Точка А';
+
+ @override
+ String get losPointB => 'Точка Б';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return 'Антена A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return 'Антена B: $value $unit';
+ }
+
+ @override
+ String get losRun => 'Запустіть LOS';
+
+ @override
+ String get losNoElevationData => 'Немає даних про висоту';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, чистий LOS, мінімальний зазор $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit, заблоковано $obstruction $heightUnit';
+ }
+
+ @override
+ String get losStatusChecking => 'LOS: перевірка...';
+
+ @override
+ String get losStatusNoData => 'LOS: немає даних';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS: $clear/$total очищено, $blocked заблоковано, $unknown невідомо';
+ }
+
+ @override
+ String get losErrorElevationUnavailable =>
+ 'Дані про висоту недоступні для одного чи кількох зразків.';
+
+ @override
+ String get losErrorInvalidInput =>
+ 'Недійсні дані про точки/висоту для розрахунку LOS.';
+
+ @override
+ String get losRenameCustomPoint => 'Перейменуйте спеціальну точку';
+
+ @override
+ String get losPointName => 'Назва точки';
+
+ @override
+ String get losShowPanelTooltip => 'Показати панель LOS';
+
+ @override
+ String get losHidePanelTooltip => 'Приховати панель LOS';
+
+ @override
+ String get losElevationAttribution =>
+ 'Дані про висоту: Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => 'Радіогоризонт';
+
+ @override
+ String get losLegendLosBeam => 'Лінія прямої видимості';
+
+ @override
+ String get losLegendTerrain => 'Рельєф';
+
+ @override
+ String get losFrequencyLabel => 'Частота';
+
+ @override
+ String get losFrequencyInfoTooltip => 'Переглянути деталі розрахунку';
+
+ @override
+ String get losFrequencyDialogTitle => 'Розрахунок радіогоризонту';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return 'Починаючи з k=$baselineK на $baselineFreq МГц, обчислення коригує k-фактор для поточного діапазону $frequencyMHz МГц, який визначає викривлену межу радіогоризонту.';
+ }
+
+ @override
+ String get contacts_pathTrace => 'Трасування шляхів';
+
+ @override
+ String get contacts_ping => 'Пінгувати';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Трасування шляху до повторювача';
+
+ @override
+ String get contacts_repeaterPing => 'Пінгувати повторювач';
+
+ @override
+ String get contacts_roomPathTrace => 'Трасування шляху до серверу кімнати';
+
+ @override
+ String get contacts_roomPing => 'Пінг сервера кімнати';
+
+ @override
+ String get contacts_chatTraceRoute => 'Трасування шляху';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return 'Відстежити маршрут до $name';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => 'Буфер обміну порожній';
+
+ @override
+ String get contacts_invalidAdvertFormat => 'Недійсні контактні дані';
+
+ @override
+ String get contacts_contactImported => 'Контакт було імпортовано.';
+
+ @override
+ String get contacts_contactImportFailed => 'Контакт не вдалося імпортувати';
+
+ @override
+ String get contacts_zeroHopAdvert => 'Реклама без перехоплення';
+
+ @override
+ String get contacts_floodAdvert => 'Залив реклами';
+
+ @override
+ String get contacts_copyAdvertToClipboard =>
+ 'Копіювати оголошення в буфер обміну';
+
+ @override
+ String get contacts_addContactFromClipboard =>
+ 'Додати контакт з буфера обміну';
+
+ @override
+ String get contacts_ShareContact => 'Копіювати контакт у буфер обміну';
+
+ @override
+ String get contacts_ShareContactZeroHop =>
+ 'Поділитися контактом за оголошенням';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent =>
+ 'Відправлено контакт за оголошенням';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed =>
+ 'Не вдалося надіслати контакт.';
+
+ @override
+ String get contacts_contactAdvertCopied =>
+ 'Рекламу скопійовано до буфера обміну.';
+
+ @override
+ String get contacts_contactAdvertCopyFailed =>
+ 'Копіювання оголошення в буфер обміну завершилося невдало';
+
+ @override
+ String get notification_activityTitle => 'Активність MeshCore';
+
+ @override
+ String notification_messagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'повідомлень',
+ many: 'повідомлень',
+ few: 'повідомлення',
+ one: 'повідомлення',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'повідомлень каналу',
+ many: 'повідомлень каналу',
+ few: 'повідомлення каналу',
+ one: 'повідомлення каналу',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ String _temp0 = intl.Intl.pluralLogic(
+ count,
+ locale: localeName,
+ other: 'нових вузлів',
+ many: 'нових вузлів',
+ few: 'нових вузли',
+ one: 'новий вузол',
+ );
+ return '$count $_temp0';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return 'Виявлено новий $contactType';
+ }
+
+ @override
+ String get notification_receivedNewMessage => 'Отримано нове повідомлення';
+
+ @override
+ String get settings_gpxExportRepeaters =>
+ 'Експортувати ретранслятори / сервер кімнати до GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle =>
+ 'Експортує ретранслятори / сервер кімнати з місцезнаходженням у файл GPX.';
+
+ @override
+ String get settings_gpxExportContacts => 'Експортувати супутників до GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle =>
+ 'Експортує супутників з місцезнаходженням у файл GPX.';
+
+ @override
+ String get settings_gpxExportAll => 'Експортувати всі контакти до GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle =>
+ 'Експортує всі контакти з місцем розташування у файл GPX.';
+
+ @override
+ String get settings_gpxExportSuccess => 'Успішно експортовано файл GPX.';
+
+ @override
+ String get settings_gpxExportNoContacts => 'Немає контактів для експорту.';
+
+ @override
+ String get settings_gpxExportNotAvailable =>
+ 'Не підтримується на вашому пристрої/операційній системі';
+
+ @override
+ String get settings_gpxExportError => 'Сталася помилка під час експорту.';
+
+ @override
+ String get settings_gpxExportRepeatersRoom =>
+ 'Місцезнаходження повторювача та сервера кімнати';
+
+ @override
+ String get settings_gpxExportChat => 'Місця супутників';
+
+ @override
+ String get settings_gpxExportAllContacts => 'Усі місця контактів';
+
+ @override
+ String get settings_gpxExportShareText =>
+ 'Дані карти експортовані з meshcore-open';
+
+ @override
+ String get settings_gpxExportShareSubject =>
+ 'експорт даних карти meshcore-open у форматі GPX';
+
+ @override
+ String get snrIndicator_nearByRepeaters => 'Ближні ретранслятори';
+
+ @override
+ String get snrIndicator_lastSeen => 'Останній раз бачили';
+
+ @override
+ String get contactsSettings_title => 'Налаштування контактів';
+
+ @override
+ String get contactsSettings_autoAddTitle => 'Автоматичне виявлення';
+
+ @override
+ String get contactsSettings_otherTitle =>
+ 'Інші налаштування, пов\'язані з контактами';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle =>
+ 'Автоматично додавати користувачів';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle =>
+ 'Дозволити супутникові автоматично додавати виявлених користувачів';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle =>
+ 'Автоматично додавати повторювачі';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle =>
+ 'Дозволити супутнику автоматично додавати виявлені ретранслятори';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle =>
+ 'Автоматично додавати сервери кімнат';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle =>
+ 'Дозволити супровіднику автоматично додавати виявлені сервери кімнат.';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle =>
+ 'Автоматично додавати датчики';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle =>
+ 'Дозволити супровіднику автоматично додавати виявлені сенсори';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => 'Перезаписати найстаріше';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ 'Коли список контактів заповнений, найстарший контакт без позначки улюбленого буде замінений.';
+
+ @override
+ String get discoveredContacts_Title => 'Виявлені контакти';
+
+ @override
+ String get discoveredContacts_noMatching =>
+ 'Відповідних контактів не знайдено';
+
+ @override
+ String get discoveredContacts_searchHint => 'Знайти виявлені контакти';
+
+ @override
+ String get discoveredContacts_contactAdded => 'Контакт додано';
+
+ @override
+ String get discoveredContacts_addContact => 'Додати контакт';
+
+ @override
+ String get discoveredContacts_copyContact =>
+ 'Копіювати контакт у буфер обміну';
+
+ @override
+ String get discoveredContacts_deleteContact => 'Видалити контакт';
+
+ @override
+ String get discoveredContacts_deleteContactAll =>
+ 'Видалити всі виявлені контакти';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent =>
+ 'Ви впевнені, що хочете видалити всі виявлені контакти?';
+
+ @override
+ String get chat_sendCooldown =>
+ 'Будь ласка, зачекайте трохи, перш ніж відправляти знову.';
+
+ @override
+ String get appSettings_jumpToOldestUnread =>
+ 'Перейти до найстарішого непрочитаного повідомлення';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ 'При відкритті чату з не прочитаними повідомленнями, прокрутіть до першого не прочитаного повідомлення, а не до останнього.';
+
+ @override
+ String get appSettings_languageHu => 'Угорський';
+
+ @override
+ String get appSettings_languageJa => 'Японська';
+
+ @override
+ String get appSettings_languageKo => 'Кореєська';
+
+ @override
+ String get radioStats_tooltip => 'Статистика радіо та мережі';
+
+ @override
+ String get radioStats_screenTitle => 'Дані про радіостанції';
+
+ @override
+ String get radioStats_notConnected =>
+ 'Підключіться до пристрою, щоб переглядати статистику радіопередач.';
+
+ @override
+ String get radioStats_firmwareTooOld =>
+ 'Статистика радіо приймача вимагає супутнього програмного забезпечення версії 8 або новішої.';
+
+ @override
+ String get radioStats_waiting => 'Очікую на отримання даних…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return 'Рівень шуму: $noiseDbm дБм';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return 'Останній показник RSSI: $rssiDbm дБм';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return 'Останній показник SNR: $snr дБ';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'Час трансляції на телеканалі TX (загальний): $seconds секунд';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'Загальний час використання RX: $seconds секунд';
+ }
+
+ @override
+ String get radioStats_chartCaption =>
+ 'Рівень шуму (дБм) на основі останніх вимірювань.';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return 'Рівень шуму: $noiseDbm дБм';
+ }
+
+ @override
+ String get radioStats_stripWaiting => 'Отримано статистику радіо…';
+
+ @override
+ String get radioStats_settingsTile => 'Дані про радіостанції';
+
+ @override
+ String get radioStats_settingsSubtitle =>
+ 'Рівень шуму, RSSI, SNR та час, протягом якого пристрій використовує радіоканал.';
+
+ @override
+ String get translation_title => 'Переклад';
+
+ @override
+ String get translation_enableTitle => 'Увімкнути переклад';
+
+ @override
+ String get translation_enableSubtitle =>
+ 'Перекладати отримані повідомлення та дозволяти попередній переклад перед відправкою.';
+
+ @override
+ String get translation_composerTitle => 'Перекладіть перед відправкою';
+
+ @override
+ String get translation_composerSubtitle =>
+ 'Контролює стан ікон перекладу, який використовується за замовчуванням.';
+
+ @override
+ String get translation_targetLanguage => 'Цільова мова';
+
+ @override
+ String get translation_useAppLanguage => 'Використовуйте мову додатку';
+
+ @override
+ String get translation_downloadedModelLabel => 'Завантажений шаблон';
+
+ @override
+ String get translation_presetModelLabel =>
+ 'Заздалегідь налаштований модель від Hugging Face';
+
+ @override
+ String get translation_manualUrlLabel =>
+ 'Посилання на веб-сторінку з інструкцією';
+
+ @override
+ String get translation_downloadModel => 'Завантажити модель';
+
+ @override
+ String get translation_downloading => 'Завантаження...';
+
+ @override
+ String get translation_working => 'Працюю...';
+
+ @override
+ String get translation_stop => 'Припинити';
+
+ @override
+ String get translation_mergingChunks =>
+ 'Об\'єднання завантажених фрагментів у кінцевий файл...';
+
+ @override
+ String get translation_downloadedModels => 'Завантажені моделі';
+
+ @override
+ String get translation_deleteModel => 'Видалити модель';
+
+ @override
+ String get translation_modelDownloaded => 'Модель перекладу завантажена.';
+
+ @override
+ String get translation_downloadStopped => 'Завантаження призупинено.';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return 'Не вдалося завантажити: $error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => 'Спочатку введіть URL моделі.';
+
+ @override
+ String get scanner_linuxPairingShowPin => 'Показати PIN';
+
+ @override
+ String get scanner_linuxPairingHidePin => 'Приховати PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => 'PIN‑код спарювання Bluetooth';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return 'Введіть PIN для $deviceName (залиште порожнім, якщо його немає).';
+ }
+
+ @override
+ String get translation_messageTranslation => 'Переклад повідомлення';
+
+ @override
+ String get translation_translateBeforeSending =>
+ 'Перекладіть перед відправкою';
+
+ @override
+ String get translation_composerEnabledHint =>
+ 'Повідомлення будуть перекладені перед відправленням.';
+
+ @override
+ String get translation_composerDisabledHint =>
+ 'Надсилайте повідомлення, використовуючи оригінальний текстовий формат.';
+
+ @override
+ String translation_translateTo(String language) {
+ return 'Перекласти на $language';
+ }
+
+ @override
+ String get translation_translationOptions => 'Варіанти перекладу';
+
+ @override
+ String get translation_systemLanguage => 'Мова системи';
+}
diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart
index 3d7bd06..4f38c64 100644
--- a/lib/l10n/app_localizations_zh.dart
+++ b/lib/l10n/app_localizations_zh.dart
@@ -24,7 +24,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get common_cancel => '取消';
@override
- String get common_ok => '好的';
+ String get common_ok => '确定';
@override
String get common_connect => '连接';
@@ -38,6 +38,9 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get common_delete => '删除';
+ @override
+ String get common_deleteAll => '删除全部';
+
@override
String get common_close => '关闭';
@@ -57,7 +60,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get common_connected => '已连接';
@override
- String get common_disconnected => '断开';
+ String get common_disconnected => '已断开';
@override
String get common_create => '创建';
@@ -78,7 +81,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get common_hide => '隐藏';
@override
- String get common_remove => '删除';
+ String get common_remove => '移除';
@override
String get common_enable => '启用';
@@ -106,30 +109,147 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get scanner_title => 'MeshCore Open';
+ String get scanner_title => '连接设备';
@override
- String get scanner_scanning => '扫描设备…';
+ String get connectionChoiceUsbLabel => 'USB';
@override
- String get scanner_connecting => '连接中...';
+ String get connectionChoiceBluetoothLabel => '蓝牙';
@override
- String get scanner_disconnecting => '断开中...';
+ String get connectionChoiceTcpLabel => 'TCP';
+
+ @override
+ String get tcpScreenTitle => '通过 TCP 连接';
+
+ @override
+ String get tcpHostLabel => 'IP地址';
+
+ @override
+ String get tcpHostHint => '192.168.40.10';
+
+ @override
+ String get tcpPortLabel => '端口';
+
+ @override
+ String get tcpPortHint => '5000';
+
+ @override
+ String get tcpStatus_notConnected => '输入目标地址,然后连接';
+
+ @override
+ String tcpStatus_connectingTo(String endpoint) {
+ return '连接到 $endpoint...';
+ }
+
+ @override
+ String get tcpErrorHostRequired => '需要提供IP地址。';
+
+ @override
+ String get tcpErrorPortInvalid => '端口号必须在 1 到 65535 之间。';
+
+ @override
+ String get tcpErrorUnsupported => '此平台不支持 TCP 传输。';
+
+ @override
+ String get tcpErrorTimedOut => 'TCP 连接超时。';
+
+ @override
+ String tcpConnectionFailed(String error) {
+ return 'TCP 连接失败:$error';
+ }
+
+ @override
+ String get usbScreenTitle => '通过USB连接';
+
+ @override
+ String get usbScreenSubtitle => '选择已检测到的串行设备,并直接连接到您的 MeshCore 节点。';
+
+ @override
+ String get usbScreenStatus => '选择一个 USB 设备';
+
+ @override
+ String get usbScreenNote => 'USB 串行接口在支持的 Android 设备和桌面平台上处于活动状态。';
+
+ @override
+ String get usbScreenEmptyState => '未找到任何 USB 设备。请插入一个,然后刷新。';
+
+ @override
+ String get usbErrorPermissionDenied => '拒绝了USB权限。';
+
+ @override
+ String get usbErrorDeviceMissing => '所选的USB设备已不再可用。';
+
+ @override
+ String get usbErrorInvalidPort => '选择一个有效的USB设备。';
+
+ @override
+ String get usbErrorBusy => '还有一个 USB 连接请求正在进行中。';
+
+ @override
+ String get usbErrorNotConnected => '没有连接任何USB设备。';
+
+ @override
+ String get usbErrorOpenFailed => '未能打开所选的USB设备。';
+
+ @override
+ String get usbErrorConnectFailed => '未能连接到所选的USB设备。';
+
+ @override
+ String get usbErrorUnsupported => '此平台不支持USB串行通信。';
+
+ @override
+ String get usbErrorAlreadyActive => 'USB 连接已建立。';
+
+ @override
+ String get usbErrorNoDeviceSelected => '未选择任何 USB 设备。';
+
+ @override
+ String get usbErrorPortClosed => 'USB 连接未建立。';
+
+ @override
+ String get usbErrorConnectTimedOut => '连接超时。请确保设备已安装 USB 伴侣固件。';
+
+ @override
+ String get usbFallbackDeviceName => 'Web 串流设备';
+
+ @override
+ String get usbStatus_notConnected => '选择一个 USB 设备';
+
+ @override
+ String get usbStatus_connecting => '连接USB设备...';
+
+ @override
+ String get usbStatus_searching => '正在搜索 USB 设备...';
+
+ @override
+ String usbConnectionFailed(String error) {
+ return 'USB 连接失败:$error';
+ }
+
+ @override
+ String get scanner_scanning => '正在搜索设备...';
+
+ @override
+ String get scanner_connecting => '正在连接...';
+
+ @override
+ String get scanner_disconnecting => '断开连接...';
@override
String get scanner_notConnected => '未连接';
@override
String scanner_connectedTo(String deviceName) {
- return '已连接至 $deviceName';
+ return '已连接到 $deviceName';
}
@override
- String get scanner_searchingDevices => '搜索 MeshCore 设备...';
+ String get scanner_searchingDevices => '正在搜索 MeshCore 设备...';
@override
- String get scanner_tapToScan => '点击扫描以查找MeshCore设备';
+ String get scanner_tapToScan => '点击“扫描”按钮以查找 MeshCore 设备。';
@override
String scanner_connectionFailed(String error) {
@@ -142,6 +262,22 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get scanner_scan => '扫描';
+ @override
+ String get scanner_bluetoothOff => '蓝牙已关闭';
+
+ @override
+ String get scanner_bluetoothOffMessage => '请开启蓝牙以搜索设备';
+
+ @override
+ String get scanner_chromeRequired => '需要 Chrome 浏览器';
+
+ @override
+ String get scanner_chromeRequiredMessage =>
+ '此 Web 应用程序需要 Google Chrome 或基于 Chromium 的浏览器以支持蓝牙。';
+
+ @override
+ String get scanner_enableBluetooth => '启用蓝牙';
+
@override
String get device_quickSwitch => '快速切换';
@@ -173,43 +309,43 @@ class AppLocalizationsZh extends AppLocalizations {
String get settings_nodeNameHint => '请输入节点名称';
@override
- String get settings_nodeNameUpdated => '姓名已更新';
+ String get settings_nodeNameUpdated => '节点名称已更新';
@override
- String get settings_radioSettings => '无线设置';
+ String get settings_radioSettings => '无线电设置';
@override
- String get settings_radioSettingsSubtitle => '频率,功率,扩展因子';
+ String get settings_radioSettingsSubtitle => '频率、功率、扩频因子';
@override
- String get settings_radioSettingsUpdated => '射频设置已更新';
+ String get settings_radioSettingsUpdated => '无线电设置已更新';
@override
String get settings_location => '位置';
@override
- String get settings_locationSubtitle => 'GPS坐标';
+ String get settings_locationSubtitle => 'GPS 坐标';
@override
- String get settings_locationUpdated => '位置已更新';
+ String get settings_locationUpdated => '位置和 GPS 设置已更新';
@override
- String get settings_locationBothRequired => '请输入纬度和经度。';
+ String get settings_locationBothRequired => '请输入经度和纬度';
@override
- String get settings_locationInvalid => '无效的纬度或经度。';
+ String get settings_locationInvalid => '无效的经度和纬度';
@override
- String get settings_locationGPSEnable => '启用GPS';
+ String get settings_locationGPSEnable => '启用 GPS';
@override
- String get settings_locationGPSEnableSubtitle => '启用GPS自动更新位置。';
+ String get settings_locationGPSEnableSubtitle => '启用 GPS 以自动更新位置。';
@override
String get settings_locationIntervalSec => 'GPS 间隔(秒)';
@override
- String get settings_locationIntervalInvalid => '时间间隔必须至少为60秒,且小于86400秒。';
+ String get settings_locationIntervalInvalid => '间隔时间必须至少为 60 秒,但不超过 86400 秒。';
@override
String get settings_latitude => '纬度';
@@ -217,41 +353,88 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get settings_longitude => '经度';
+ @override
+ String get settings_contactSettings => '联系人设置';
+
+ @override
+ String get settings_contactSettingsSubtitle => '添加联系人的设置';
+
@override
String get settings_privacyMode => '隐私模式';
@override
- String get settings_privacyModeSubtitle => '隐藏在广告中的姓名/位置';
+ String get settings_privacyModeSubtitle => '在广告中隐藏姓名/位置';
@override
- String get settings_privacyModeToggle => '开启隐私模式以隐藏您的姓名和位置在广告中的显示。';
+ String get settings_privacyModeToggle => '切换隐私模式以在广告中隐藏姓名和位置,保护个人信息。';
@override
String get settings_privacyModeEnabled => '隐私模式已启用';
@override
- String get settings_privacyModeDisabled => '隐私模式已禁用';
+ String get settings_privacyModeDisabled => '隐私模式已关闭';
+
+ @override
+ String get settings_privacy => '隐私设置';
+
+ @override
+ String get settings_privacySubtitle => '控制要共享的信息。';
+
+ @override
+ String get settings_privacySettingsDescription => '选择您的设备与他人共享的信息。';
+
+ @override
+ String get settings_denyAll => '拒绝所有';
+
+ @override
+ String get settings_allowByContact => '按联系人标志允许';
+
+ @override
+ String get settings_allowAll => '允许全部';
+
+ @override
+ String get settings_telemetryBaseMode => '遥测基础模式';
+
+ @override
+ String get settings_telemetryLocationMode => '遥测位置模式';
+
+ @override
+ String get settings_telemetryEnvironmentMode => '遥测环境模式';
+
+ @override
+ String get settings_advertLocation => '广告位置';
+
+ @override
+ String get settings_advertLocationSubtitle => '在广告中包含位置';
+
+ @override
+ String settings_multiAck(String value) {
+ return '多重ACK:$value';
+ }
+
+ @override
+ String get settings_telemetryModeUpdated => '遥测模式已更新';
@override
String get settings_actions => '操作';
@override
- String get settings_sendAdvertisement => '发送广告';
+ String get settings_sendAdvertisement => '发送广播';
@override
- String get settings_sendAdvertisementSubtitle => '现在已广播';
+ String get settings_sendAdvertisementSubtitle => '立即发送广播';
@override
- String get settings_advertisementSent => '广告已发送';
+ String get settings_advertisementSent => '已发送广播';
@override
String get settings_syncTime => '同步时间';
@override
- String get settings_syncTimeSubtitle => '将设备时钟设置为手机时间';
+ String get settings_syncTimeSubtitle => '将设备时钟设置为与手机时间一致';
@override
- String get settings_timeSynchronized => '时间同步';
+ String get settings_timeSynchronized => '时间已同步';
@override
String get settings_refreshContacts => '刷新联系人';
@@ -266,16 +449,16 @@ class AppLocalizationsZh extends AppLocalizations {
String get settings_rebootDeviceSubtitle => '重启 MeshCore 设备';
@override
- String get settings_rebootDeviceConfirm => '您确定要重启设备吗?您将会断开连接。';
+ String get settings_rebootDeviceConfirm => '确定要重启设备吗?这将断开与设备的连接。';
@override
String get settings_debug => '调试';
@override
- String get settings_bleDebugLog => '蓝牙调试日志';
+ String get settings_bleDebugLog => 'BLE 调试日志';
@override
- String get settings_bleDebugLogSubtitle => '蓝牙命令、响应和原始数据';
+ String get settings_bleDebugLogSubtitle => 'BLE 命令、响应和原始数据';
@override
String get settings_appDebugLog => '应用调试日志';
@@ -292,17 +475,21 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get settings_aboutLegalese => '2024 MeshCore 开放源代码项目';
+ String get settings_aboutLegalese => '2026 MeshCore 开源项目';
@override
String get settings_aboutDescription =>
- '一个开源的 Flutter 客户端,用于 MeshCore LoRa 网状网络设备。';
+ '一个开源的 Flutter 客户端,用于 MeshCore LoRa 无线网络设备。';
@override
- String get settings_infoName => '姓名';
+ String get settings_aboutOpenMeteoAttribution =>
+ 'LOS 高程数据:Open-Meteo (CC BY 4.0)';
@override
- String get settings_infoId => 'ID';
+ String get settings_infoName => '名称';
+
+ @override
+ String get settings_infoId => 'MAC ID';
@override
String get settings_infoStatus => '状态';
@@ -322,15 +509,6 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get settings_presets => '预设';
- @override
- String get settings_preset915Mhz => '915 MHz';
-
- @override
- String get settings_preset868Mhz => '868 MHz';
-
- @override
- String get settings_preset433Mhz => '433 MHz';
-
@override
String get settings_frequency => '频率 (MHz)';
@@ -338,31 +516,35 @@ class AppLocalizationsZh extends AppLocalizations {
String get settings_frequencyHelper => '300.0 - 2500.0';
@override
- String get settings_frequencyInvalid => '无效频率 (300-2500 MHz)';
+ String get settings_frequencyInvalid => '无效频率范围(300-2500 MHz)';
@override
String get settings_bandwidth => '带宽';
@override
- String get settings_spreadingFactor => '扩散因子';
+ String get settings_spreadingFactor => '扩频因子';
@override
String get settings_codingRate => '编码速率';
@override
- String get settings_txPower => 'TX Power (dBm)';
+ String get settings_txPower => 'TX 功率 (dBm)';
@override
String get settings_txPowerHelper => '0 - 22';
@override
- String get settings_txPowerInvalid => '无效的 TX 电功率 (0-22 dBm)';
+ String get settings_txPowerInvalid => '无效的发射功率(0-22 dBm)';
@override
- String get settings_longRange => '远距离';
+ String get settings_clientRepeat => '离网重复';
@override
- String get settings_fastSpeed => '快速速度';
+ String get settings_clientRepeatSubtitle => '允许此设备重复发送网状数据包给其他设备';
+
+ @override
+ String get settings_clientRepeatFreqWarning =>
+ '离网重复通信需要使用 433、869 或 918 兆赫兹的频率。';
@override
String settings_error(String message) {
@@ -379,10 +561,10 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_theme => '主题';
@override
- String get appSettings_themeSystem => '系统默认';
+ String get appSettings_themeSystem => '跟随系统';
@override
- String get appSettings_themeLight => '光';
+ String get appSettings_themeLight => '浅色';
@override
String get appSettings_themeDark => '深色';
@@ -391,46 +573,58 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_language => '语言';
@override
- String get appSettings_languageSystem => '系统默认';
+ String get appSettings_languageSystem => '跟随系统';
@override
- String get appSettings_languageEn => 'English';
+ String get appSettings_languageEn => '英语';
@override
- String get appSettings_languageFr => 'Français';
+ String get appSettings_languageFr => '法语';
@override
- String get appSettings_languageEs => 'Español';
+ String get appSettings_languageEs => '西班牙语';
@override
- String get appSettings_languageDe => 'Deutsch';
+ String get appSettings_languageDe => '德语';
@override
- String get appSettings_languagePl => 'Polski';
+ String get appSettings_languagePl => '波兰语';
@override
- String get appSettings_languageSl => 'Slovenščina';
+ String get appSettings_languageSl => '斯洛文尼亚语';
@override
- String get appSettings_languagePt => 'Português';
+ String get appSettings_languagePt => '葡萄牙语';
@override
- String get appSettings_languageIt => 'Italiano';
+ String get appSettings_languageIt => '意大利语';
@override
String get appSettings_languageZh => '中文';
@override
- String get appSettings_languageSv => 'Svenska';
+ String get appSettings_languageSv => '瑞典语';
@override
- String get appSettings_languageNl => 'Nederlands';
+ String get appSettings_languageNl => '荷兰语';
@override
- String get appSettings_languageSk => 'Slovenčina';
+ String get appSettings_languageSk => '斯洛伐克语';
@override
- String get appSettings_languageBg => 'Български';
+ String get appSettings_languageBg => '保加利亚语';
+
+ @override
+ String get appSettings_languageRu => '俄语';
+
+ @override
+ String get appSettings_languageUk => '乌克兰语';
+
+ @override
+ String get appSettings_enableMessageTracing => '启用消息追踪';
+
+ @override
+ String get appSettings_enableMessageTracingSubtitle => '显示消息的详细路由和时间元数据';
@override
String get appSettings_notifications => '通知';
@@ -439,10 +633,10 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_enableNotifications => '启用通知';
@override
- String get appSettings_enableNotificationsSubtitle => '接收消息和广告的通知';
+ String get appSettings_enableNotificationsSubtitle => '接收消息和广播的通知';
@override
- String get appSettings_notificationPermissionDenied => '通知权限被拒绝';
+ String get appSettings_notificationPermissionDenied => '权限被拒绝';
@override
String get appSettings_notificationsEnabled => '通知已启用';
@@ -454,40 +648,40 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_messageNotifications => '消息通知';
@override
- String get appSettings_messageNotificationsSubtitle => '显示收到新消息时的通知';
+ String get appSettings_messageNotificationsSubtitle => '收到新消息时显示通知';
@override
String get appSettings_channelMessageNotifications => '频道消息通知';
@override
- String get appSettings_channelMessageNotificationsSubtitle => '显示接收频道消息时的通知';
+ String get appSettings_channelMessageNotificationsSubtitle => '收到频道消息时显示通知';
@override
- String get appSettings_advertisementNotifications => '广告通知';
+ String get appSettings_advertisementNotifications => '广播通知';
@override
- String get appSettings_advertisementNotificationsSubtitle => '显示当新节点被发现时通知';
+ String get appSettings_advertisementNotificationsSubtitle => '发现新节点时显示通知';
@override
String get appSettings_messaging => '消息';
@override
- String get appSettings_clearPathOnMaxRetry => '清除最大重试路径';
+ String get appSettings_clearPathOnMaxRetry => '达到最大重试次数时清除路径';
@override
- String get appSettings_clearPathOnMaxRetrySubtitle => '重置联系人路径,在5次发送失败尝试后';
+ String get appSettings_clearPathOnMaxRetrySubtitle => '在5次发送失败后重置联系路径。';
@override
- String get appSettings_pathsWillBeCleared => '路径将在5次失败重试后清除';
+ String get appSettings_pathsWillBeCleared => '5次失败后将重新路由';
@override
- String get appSettings_pathsWillNotBeCleared => '路径不会自动清理';
+ String get appSettings_pathsWillNotBeCleared => '路径不会自动清除';
@override
- String get appSettings_autoRouteRotation => '自动路径旋转';
+ String get appSettings_autoRouteRotation => '自动路径轮换';
@override
- String get appSettings_autoRouteRotationSubtitle => '在最佳路径和洪水模式之间切换';
+ String get appSettings_autoRouteRotationSubtitle => '在最佳路径和泛洪模式之间切换';
@override
String get appSettings_autoRouteRotationEnabled => '自动路径轮换已启用';
@@ -496,36 +690,73 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_autoRouteRotationDisabled => '自动路径轮换已禁用';
@override
- String get appSettings_battery => '电池';
+ String get appSettings_maxRouteWeight => '最大路径重量';
@override
- String get appSettings_batteryChemistry => '电池化学';
+ String get appSettings_maxRouteWeightSubtitle => '一条路径可以累积的最大重量,取决于成功交付的数量。';
@override
- String appSettings_batteryChemistryPerDevice(String deviceName) {
- return '设置每个设备 ($deviceName)';
+ String get appSettings_initialRouteWeight => '初始路线权重';
+
+ @override
+ String get appSettings_initialRouteWeightSubtitle => '新发现路径的初始重量';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrement => '成功权重增加';
+
+ @override
+ String get appSettings_routeWeightSuccessIncrementSubtitle =>
+ '在成功交付后,将重量添加到路径中';
+
+ @override
+ String get appSettings_routeWeightFailureDecrement => '失败权重降低';
+
+ @override
+ String get appSettings_routeWeightFailureDecrementSubtitle =>
+ '从一条路径上移除的货物,由于无法成功交付而移除。';
+
+ @override
+ String get appSettings_maxMessageRetries => '最大消息重试次数';
+
+ @override
+ String get appSettings_maxMessageRetriesSubtitle => '在将消息标记为失败之前,允许尝试的次数';
+
+ @override
+ String path_routeWeight(String weight, String max) {
+ return '$weight/$max';
}
@override
- String get appSettings_batteryChemistryConnectFirst => '连接设备以选择';
+ String get appSettings_battery => '电池';
@override
- String get appSettings_batteryNmc => '18650 NMC (3.0-4.2V)';
+ String get appSettings_batteryChemistry => '电池类型';
+
+ @override
+ String appSettings_batteryChemistryPerDevice(String deviceName) {
+ return '为每个设备设置 ($deviceName)';
+ }
+
+ @override
+ String get appSettings_batteryChemistryConnectFirst => '请先连接设备';
+
+ @override
+ String get appSettings_batteryNmc => '18650 NMC 电池 (3.0-4.2V)';
@override
String get appSettings_batteryLifepo4 => '磷酸铁锂 (2.6-3.65V)';
@override
- String get appSettings_batteryLipo => 'LiPo (3.0-4.2V)';
+ String get appSettings_batteryLipo => '锂聚合物电池 (3.0-4.2V)';
@override
String get appSettings_mapDisplay => '地图显示';
@override
- String get appSettings_showRepeaters => '显示循环器';
+ String get appSettings_showRepeaters => '显示转发节点';
@override
- String get appSettings_showRepeatersSubtitle => '在地图上显示重复节点';
+ String get appSettings_showRepeatersSubtitle => '在地图上显示转发节点';
@override
String get appSettings_showChatNodes => '显示聊天节点';
@@ -537,36 +768,36 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_showOtherNodes => '显示其他节点';
@override
- String get appSettings_showOtherNodesSubtitle => '显示其他节点类型在地图上';
+ String get appSettings_showOtherNodesSubtitle => '在地图上显示其他节点类型';
@override
- String get appSettings_timeFilter => '时间筛选';
+ String get appSettings_timeFilter => '时间过滤器';
@override
String get appSettings_timeFilterShowAll => '显示所有节点';
@override
String appSettings_timeFilterShowLast(int hours) {
- return '显示来自过去 $hours 小时的节点';
+ return '显示过去 $hours 小时内的节点';
}
@override
String get appSettings_mapTimeFilter => '地图时间筛选';
@override
- String get appSettings_showNodesDiscoveredWithin => '显示发现的节点在:';
+ String get appSettings_showNodesDiscoveredWithin => '显示在此时间段内发现的节点:';
@override
String get appSettings_allTime => '所有时间';
@override
- String get appSettings_lastHour => '最后小时';
+ String get appSettings_lastHour => '过去一小时';
@override
- String get appSettings_last6Hours => '最后6小时';
+ String get appSettings_last6Hours => '过去6小时';
@override
- String get appSettings_last24Hours => '最后24小时';
+ String get appSettings_last24Hours => '过去24小时';
@override
String get appSettings_lastWeek => '上周';
@@ -574,12 +805,21 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get appSettings_offlineMapCache => '离线地图缓存';
+ @override
+ String get appSettings_unitsTitle => '单位';
+
+ @override
+ String get appSettings_unitsMetric => '公制(米/公里)';
+
+ @override
+ String get appSettings_unitsImperial => '英制 (ft / mi)';
+
@override
String get appSettings_noAreaSelected => '未选择任何区域';
@override
String appSettings_areaSelectedZoom(int minZoom, int maxZoom) {
- return '选中的区域(缩放至 $minZoom - $maxZoom)';
+ return '已选择区域(缩放 $minZoom - $maxZoom)';
}
@override
@@ -589,10 +829,10 @@ class AppLocalizationsZh extends AppLocalizations {
String get appSettings_appDebugLogging => '应用调试日志';
@override
- String get appSettings_appDebugLoggingSubtitle => '记录应用调试消息以供故障排除';
+ String get appSettings_appDebugLoggingSubtitle => '记录应用调试消息以进行故障排除。';
@override
- String get appSettings_appDebugLoggingEnabled => '应用调试日志已启用';
+ String get appSettings_appDebugLoggingEnabled => '调试日志已启用';
@override
String get appSettings_appDebugLoggingDisabled => '应用调试日志已禁用';
@@ -601,98 +841,162 @@ class AppLocalizationsZh extends AppLocalizations {
String get contacts_title => '联系人';
@override
- String get contacts_noContacts => '还没有联系人';
+ String get contacts_noContacts => '暂无联系人';
@override
- String get contacts_contactsWillAppear => '设备会广播时,联系人会显示';
+ String get contacts_contactsWillAppear => '当设备发送广播时,联系人将显示。';
@override
- String get contacts_searchContacts => '搜索联系人...';
+ String get contacts_unread => '未读';
@override
- String get contacts_noUnreadContacts => '未读联系人';
+ String get contacts_searchContactsNoNumber => '搜索联系人...';
@override
- String get contacts_noContactsFound => '未找到任何联系人或群组';
+ String contacts_searchContacts(int number, String str) {
+ return '搜索联系人...';
+ }
+
+ @override
+ String contacts_searchFavorites(int number, String str) {
+ return '搜索 $number$str 收藏...';
+ }
+
+ @override
+ String contacts_searchUsers(int number, String str) {
+ return '搜索 $number$str 位用户...';
+ }
+
+ @override
+ String contacts_searchRepeaters(int number, String str) {
+ return '搜索 $number$str 重复器...';
+ }
+
+ @override
+ String contacts_searchRoomServers(int number, String str) {
+ return '搜索 $number$str 房间服务器...';
+ }
+
+ @override
+ String get contacts_noUnreadContacts => '没有未读内容';
+
+ @override
+ String get contacts_noContactsFound => '未找到任何联系人或群聊';
@override
String get contacts_deleteContact => '删除联系人';
@override
String contacts_removeConfirm(String contactName) {
- return '从联系人中删除 $contactName 吗?';
+ return '从联系人中移除 $contactName?';
}
@override
- String get contacts_manageRepeater => '管理重复项';
+ String get contacts_manageRepeater => '管理转发节点';
@override
String get contacts_manageRoom => '管理房间服务器';
@override
- String get contacts_roomLogin => '房间登录';
+ String get contacts_roomLogin => '服务器登录';
@override
String get contacts_openChat => '打开聊天';
@override
- String get contacts_editGroup => '编辑组';
+ String get contacts_editGroup => '编辑群聊';
@override
- String get contacts_deleteGroup => '删除分组';
+ String get contacts_deleteGroup => '删除群聊';
@override
String contacts_deleteGroupConfirm(String groupName) {
- return '删除\"$groupName\"?';
+ return '删除群聊 \"$groupName\"?';
}
@override
- String get contacts_newGroup => '新组';
+ String get contacts_newGroup => '新建群聊';
@override
- String get contacts_groupName => '组名';
+ String get contacts_groupName => '群聊名称';
@override
- String get contacts_groupNameRequired => '组名不能为空';
+ String get contacts_groupNameRequired => '请输入群聊名称';
+
+ @override
+ String get contacts_groupNameReserved => '该群组名称已被保留';
@override
String contacts_groupAlreadyExists(String name) {
- return '组“$name”已存在';
+ return '名为 \"$name\" 的群聊已存在';
}
@override
String get contacts_filterContacts => '筛选联系人...';
@override
- String get contacts_noContactsMatchFilter => '未找到匹配您的筛选条件的结果';
+ String get contacts_noContactsMatchFilter => '没有符合条件的联系人';
@override
- String get contacts_noMembers => '没有会员';
+ String get contacts_noMembers => '暂无成员';
@override
- String get contacts_lastSeenNow => '最后一次登录时间现在';
+ String get contacts_lastSeenNow => '刚刚';
@override
String contacts_lastSeenMinsAgo(int minutes) {
- return '最后一次出现 $minutes 分前';
+ return '最后在线 $minutes 分钟前';
}
@override
- String get contacts_lastSeenHourAgo => '最后一次出现前1小时';
+ String get contacts_lastSeenHourAgo => '最后在线 1小时前';
@override
String contacts_lastSeenHoursAgo(int hours) {
- return '最后一次出现 $hours 小时前';
+ return '最后在线 $hours 小时前';
}
@override
- String get contacts_lastSeenDayAgo => '最后一次登录前一天';
+ String get contacts_lastSeenDayAgo => '最后在线 1天前';
@override
String contacts_lastSeenDaysAgo(int days) {
- return '最后一次出现 $days 天前';
+ return '最后在线 $days 天前';
}
+ @override
+ String get contact_info => '联系信息';
+
+ @override
+ String get contact_settings => '联系人设置';
+
+ @override
+ String get contact_telemetry => '遥测数据';
+
+ @override
+ String get contact_lastSeen => '最近出现';
+
+ @override
+ String get contact_clearChat => '清除聊天记录';
+
+ @override
+ String get contact_teleBase => '遥测基站';
+
+ @override
+ String get contact_teleBaseSubtitle => '允许共享电池电量和基本遥测数据';
+
+ @override
+ String get contact_teleLoc => '遥测位置';
+
+ @override
+ String get contact_teleLocSubtitle => '允许共享位置数据';
+
+ @override
+ String get contact_teleEnv => '遥测环境';
+
+ @override
+ String get contact_teleEnvSubtitle => '允许共享环境传感器数据';
+
@override
String get channels_title => '频道';
@@ -700,7 +1004,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get channels_noChannelsConfigured => '未配置任何频道';
@override
- String get channels_addPublicChannel => '添加公开频道';
+ String get channels_addPublicChannel => '添加公共频道';
@override
String get channels_searchChannels => '搜索频道...';
@@ -714,34 +1018,45 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get channels_hashtagChannel => '话题频道';
+ String get channels_hashtagChannel => '标签频道';
@override
- String get channels_public => '公开';
+ String get channels_public => '公共';
@override
String get channels_private => '私有';
@override
- String get channels_publicChannel => '公开频道';
+ String get channels_publicChannel => '公共频道';
@override
- String get channels_privateChannel => '私聊频道';
+ String get channels_privateChannel => '私有频道';
@override
String get channels_editChannel => '编辑频道';
+ @override
+ String get channels_muteChannel => '静音频道';
+
+ @override
+ String get channels_unmuteChannel => '取消静音频道';
+
@override
String get channels_deleteChannel => '删除频道';
@override
String channels_deleteChannelConfirm(String name) {
- return '删除\"$name\"?此操作无法撤销。';
+ return '删除频道 \"$name\"?此操作不可撤销。';
+ }
+
+ @override
+ String channels_channelDeleteFailed(String name) {
+ return '无法删除频道 \"$name\"';
}
@override
String channels_channelDeleted(String name) {
- return '频道“$name”已删除';
+ return '已删除频道 \"$name\"';
}
@override
@@ -757,23 +1072,23 @@ class AppLocalizationsZh extends AppLocalizations {
String get channels_usePublicChannel => '使用公共频道';
@override
- String get channels_standardPublicPsk => '标准公钥共享密钥';
+ String get channels_standardPublicPsk => '标准公共 PSK';
@override
String get channels_pskHex => 'PSK (十六进制)';
@override
- String get channels_generateRandomPsk => '生成随机PSK';
+ String get channels_generateRandomPsk => '生成随机 PSK';
@override
String get channels_enterChannelName => '请输入频道名称';
@override
- String get channels_pskMustBe32Hex => 'PSK 必须是 32 个十六进制字符';
+ String get channels_pskMustBe32Hex => 'PSK 必须为 32 个十六进制字符';
@override
String channels_channelAdded(String name) {
- return '频道“$name”已添加';
+ return '已添加频道 \"$name\"';
}
@override
@@ -786,14 +1101,14 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String channels_channelUpdated(String name) {
- return '频道“$name”已更新';
+ return '频道 \"$name\" 已更新';
}
@override
- String get channels_publicChannelAdded => '公共频道已添加';
+ String get channels_publicChannelAdded => '已添加公共频道';
@override
- String get channels_sortBy => '按类型排序';
+ String get channels_sortBy => '排序方式';
@override
String get channels_sortManual => '手动';
@@ -808,13 +1123,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get channels_sortUnread => '未读';
@override
- String get channels_createPrivateChannel => '创建私聊频道';
+ String get channels_createPrivateChannel => '创建私有频道';
@override
String get channels_createPrivateChannelDesc => '使用密钥保护。';
@override
- String get channels_joinPrivateChannel => '加入私密频道';
+ String get channels_joinPrivateChannel => '加入私有频道';
@override
String get channels_joinPrivateChannelDesc => '手动输入密钥。';
@@ -823,38 +1138,46 @@ class AppLocalizationsZh extends AppLocalizations {
String get channels_joinPublicChannel => '加入公共频道';
@override
- String get channels_joinPublicChannelDesc => '任何人都可以加入这个频道。';
+ String get channels_joinPublicChannelDesc => '任何人都可以加入。';
@override
String get channels_joinHashtagChannel => '加入标签频道';
@override
- String get channels_joinHashtagChannelDesc => '任何人都可以加入话题频道。';
+ String get channels_joinHashtagChannelDesc => '任何人都可以加入标签频道。';
@override
String get channels_scanQrCode => '扫描二维码';
@override
- String get channels_scanQrCodeComingSoon => '即将到来';
+ String get channels_scanQrCodeComingSoon => '即将推出';
@override
String get channels_enterHashtag => '输入标签';
@override
- String get channels_hashtagHint => '例如 #团队';
+ String get channels_hashtagHint => '例如:#团队';
@override
- String get chat_noMessages => '目前还没有消息';
+ String get chat_noMessages => '暂无消息';
@override
- String get chat_sendMessageToStart => '发送消息开始';
+ String get chat_sendMessage => '发送消息';
+
+ @override
+ String chat_sendMessageTo(String contactName) {
+ return '发送消息给 $contactName';
+ }
+
+ @override
+ String get chat_sendMessageToStart => '发送消息开始对话';
@override
String get chat_originalMessageNotFound => '找不到原始消息';
@override
String chat_replyingTo(String name) {
- return '回复 $name';
+ return '正在回复 $name';
}
@override
@@ -865,17 +1188,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get chat_location => '位置';
- @override
- String chat_sendMessageTo(String contactName) {
- return '向$contactName发送消息';
- }
-
@override
String get chat_typeMessage => '输入消息...';
@override
String chat_messageTooLong(int maxBytes) {
- return '消息太长了(最大 $maxBytes 字节)。';
+ return '消息过长(最多 $maxBytes 字节)';
}
@override
@@ -885,7 +1203,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get chat_messageDeleted => '消息已删除';
@override
- String get chat_retryingMessage => '重试';
+ String get chat_retryingMessage => '正在重试消息';
@override
String chat_retryCount(int current, int max) {
@@ -893,46 +1211,46 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get chat_sendGif => '发送GIF';
+ String get chat_sendGif => '发送 GIF';
@override
String get chat_reply => '回复';
@override
- String get chat_addReaction => '添加反应';
+ String get chat_addReaction => '添加表情';
@override
String get chat_me => '我';
@override
- String get emojiCategorySmileys => '表情符号';
+ String get emojiCategorySmileys => '表情';
@override
String get emojiCategoryGestures => '手势';
@override
- String get emojiCategoryHearts => '心';
+ String get emojiCategoryHearts => '爱心';
@override
- String get emojiCategoryObjects => '对象';
+ String get emojiCategoryObjects => '物品';
@override
- String get gifPicker_title => '选择一个 GIF';
+ String get gifPicker_title => '选择 GIF';
@override
- String get gifPicker_searchHint => '搜索GIF...';
+ String get gifPicker_searchHint => '搜索 GIF...';
@override
- String get gifPicker_poweredBy => '由 GIPHY 提供支持';
+ String get gifPicker_poweredBy => '由 GIPHY 提供';
@override
- String get gifPicker_noGifsFound => '未找到 GIF 动画';
+ String get gifPicker_noGifsFound => '未找到 GIF';
@override
- String get gifPicker_failedLoad => 'GIF 加载失败';
+ String get gifPicker_failedLoad => '加载 GIF 失败';
@override
- String get gifPicker_failedSearch => '搜索GIF失败';
+ String get gifPicker_failedSearch => '搜索 GIF 失败';
@override
String get gifPicker_noInternet => '无网络连接';
@@ -941,7 +1259,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get debugLog_appTitle => '应用调试日志';
@override
- String get debugLog_bleTitle => '蓝牙调试日志';
+ String get debugLog_bleTitle => 'BLE 调试日志';
@override
String get debugLog_copyLog => '复制日志';
@@ -953,22 +1271,22 @@ class AppLocalizationsZh extends AppLocalizations {
String get debugLog_copied => '调试日志已复制';
@override
- String get debugLog_bleCopied => '蓝牙日志复制';
+ String get debugLog_bleCopied => 'BLE 日志已复制';
@override
- String get debugLog_noEntries => '尚未生成调试日志';
+ String get debugLog_noEntries => '暂无调试日志';
@override
- String get debugLog_enableInSettings => '启用应用调试日志记录设置';
+ String get debugLog_enableInSettings => '请在设置中启用应用调试日志。';
@override
String get debugLog_frames => '帧';
@override
- String get debugLog_rawLogRx => '原始日志-RX';
+ String get debugLog_rawLogRx => '原始日志 RX';
@override
- String get debugLog_noBleActivity => '目前还没有蓝牙活动。';
+ String get debugLog_noBleActivity => '暂无 BLE 活动';
@override
String debugFrame_length(int count) {
@@ -981,11 +1299,11 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get debugFrame_textMessageHeader => '短信框';
+ String get debugFrame_textMessageHeader => '文本消息:';
@override
String debugFrame_destinationPubKey(String pubKey) {
- return '- 目的地公钥:$pubKey';
+ return '- 目标公钥:$pubKey';
}
@override
@@ -1004,62 +1322,59 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get debugFrame_textTypeCli => 'CLI';
+ String get debugFrame_textTypeCli => '命令行';
@override
- String get debugFrame_textTypePlain => '简洁';
+ String get debugFrame_textTypePlain => '纯文本';
@override
String debugFrame_text(String text) {
- return '- 文本:\"$text\"';
+ return '- 文本:“$text”';
}
@override
- String get debugFrame_hexDump => '十六进制数据';
+ String get debugFrame_hexDump => '十六进制数据:';
@override
String get chat_pathManagement => '路径管理';
+ @override
+ String get chat_ShowAllPaths => '显示所有路径';
+
@override
String get chat_routingMode => '路由模式';
@override
- String get chat_autoUseSavedPath => '自动(使用已保存路径)';
+ String get chat_autoUseSavedPath => '自动(使用保存的路径)';
@override
- String get chat_forceFloodMode => '强制洪水模式';
+ String get chat_forceFloodMode => '强制泛洪模式';
@override
- String get chat_recentAckPaths => '最近的 ACK 路径 (点击以使用):';
+ String get chat_recentAckPaths => '最近使用的 ACK 路径(点击使用):';
@override
- String get chat_pathHistoryFull => '路径历史已满。删除条目以添加新条目。';
+ String get chat_pathHistoryFull => '路径历史已满,请删除后再添加。';
@override
- String get chat_hopSingular => '跳转';
+ String get chat_hopSingular => '跳';
@override
- String get chat_hopPlural => '跳跃';
+ String get chat_hopPlural => '跳';
@override
String chat_hopsCount(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: '跳跃',
- one: '跳跃',
- );
- return '$count $_temp0';
+ return '$count 跳';
}
@override
String get chat_successes => '成功';
@override
- String get chat_removePath => '删除路径';
+ String get chat_removePath => '移除路径';
@override
- String get chat_noPathHistoryYet => '还没有历史记录。\n发送消息以发现路径。';
+ String get chat_noPathHistoryYet => '暂无路径历史。\n发送消息以探索路径。';
@override
String get chat_pathActions => '路径操作:';
@@ -1074,36 +1389,30 @@ class AppLocalizationsZh extends AppLocalizations {
String get chat_clearPath => '清除路径';
@override
- String get chat_clearPathSubtitle => '强制下次发送时重新发现';
+ String get chat_clearPathSubtitle => '清除当前路径,下次发送将重新尝试。';
@override
- String get chat_pathCleared => '路径已清除。下一条消息将重新发现路线。';
+ String get chat_pathCleared => '路径已清除。下一条消息将重新路由。';
@override
- String get chat_floodModeSubtitle => '使用应用栏中的路由切换开关';
+ String get chat_floodModeSubtitle => '在应用栏中切换路由模式。';
@override
- String get chat_floodModeEnabled => '防洪模式已启用。通过应用程序栏中的路由图标进行反转。';
+ String get chat_floodModeEnabled => '泛洪模式已启用。可通过应用栏的路由图标切换。';
@override
String get chat_fullPath => '完整路径';
@override
- String get chat_pathDetailsNotAvailable => '路径详情尚未获取。请尝试发送消息以刷新。';
+ String get chat_pathDetailsNotAvailable => '路径信息暂不可用,请尝试发送消息刷新。';
@override
String chat_pathSetHops(int hopCount, String status) {
- String _temp0 = intl.Intl.pluralLogic(
- hopCount,
- locale: localeName,
- other: 'hops',
- one: 'hop',
- );
- return '路径设置:$hopCount $_temp0 - $status';
+ return '路径设置:$hopCount 跳 - $status';
}
@override
- String get chat_pathSavedLocally => '已本地保存。连接以同步。';
+ String get chat_pathSavedLocally => '已本地保存,连接设备后可同步。';
@override
String get chat_pathDeviceConfirmed => '设备已确认。';
@@ -1112,7 +1421,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get chat_pathDeviceNotConfirmed => '设备尚未确认。';
@override
- String get chat_type => '输入';
+ String get chat_type => '类型';
@override
String get chat_path => '路径';
@@ -1124,38 +1433,61 @@ class AppLocalizationsZh extends AppLocalizations {
String get chat_compressOutgoingMessages => '压缩发送的消息';
@override
- String get chat_floodForced => '强制溢出';
+ String get chat_floodForced => '泛洪(强制)';
@override
- String get chat_directForced => '强制直接';
+ String get chat_directForced => '直连(强制)';
@override
String chat_hopsForced(int count) {
- return '$count 次跳跃 (强制)';
+ return '$count 跳(强制)';
}
@override
- String get chat_floodAuto => '自动防洪';
+ String get chat_floodAuto => '自动泛洪';
@override
- String get chat_direct => '直接';
+ String get chat_direct => '直连';
@override
- String get chat_poiShared => '共享位置信息';
+ String get chat_poiShared => '共享位置';
@override
String chat_unread(int count) {
return '未读:$count';
}
+ @override
+ String get chat_openLink => '打开链接?';
+
+ @override
+ String get chat_openLinkConfirmation => '是否使用浏览器打开此链接?';
+
+ @override
+ String get chat_open => '打开';
+
+ @override
+ String chat_couldNotOpenLink(String url) {
+ return '无法打开链接:$url';
+ }
+
+ @override
+ String get chat_invalidLink => '无效的链接格式';
+
@override
String get map_title => '节点地图';
@override
- String get map_noNodesWithLocation => '没有具有位置数据的节点';
+ String get map_lineOfSight => '视线';
@override
- String get map_nodesNeedGps => '节点需要共享它们的 GPS 坐标\n才能在地图上显示';
+ String get map_losScreenTitle => '视线';
+
+ @override
+ String get map_noNodesWithLocation => '没有包含位置信息的节点';
+
+ @override
+ String get map_nodesNeedGps => '节点需要共享 GPS 坐标才能在地图上显示';
@override
String map_nodesCount(int count) {
@@ -1164,14 +1496,14 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String map_pinsCount(int count) {
- return '针:$count';
+ return '标记:$count';
}
@override
String get map_chat => '聊天';
@override
- String get map_repeater => '重复器';
+ String get map_repeater => '转发节点';
@override
String get map_room => '房间';
@@ -1180,34 +1512,37 @@ class AppLocalizationsZh extends AppLocalizations {
String get map_sensor => '传感器';
@override
- String get map_pinDm => '私信 (DM)';
+ String get map_pinDm => '标记(私信)';
@override
- String get map_pinPrivate => '私密模式';
+ String get map_pinPrivate => '私有';
@override
- String get map_pinPublic => '公开(公版)';
+ String get map_pinPublic => '公共';
@override
- String get map_lastSeen => '最后一次登录';
+ String get map_lastSeen => '最后在线';
@override
- String get map_disconnectConfirm => '您确定要断开与此设备的连接吗?';
+ String get map_disconnectConfirm => '确定要断开与此设备的连接吗?';
@override
- String get map_from => '从';
+ String get map_from => '来自';
@override
String get map_source => '来源';
@override
- String get map_flags => '旗帜';
+ String get map_flags => '标志';
@override
- String get map_shareMarkerHere => '分享标记在此';
+ String get map_shareMarkerHere => '在此分享标记';
@override
- String get map_pinLabel => '固定标签';
+ String get map_setAsMyLocation => '设置为我的位置';
+
+ @override
+ String get map_pinLabel => '标签';
@override
String get map_label => '标签';
@@ -1229,14 +1564,14 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String map_publicLocationShareConfirm(String channelLabel) {
- return '您即将分享一个位置在 $channelLabel。此频道公开,任何拥有 PSK 的人都可以看到它。';
+ return '您即将在 $channelLabel 上分享一个位置。此频道是公开的,任何拥有 PSK 的人都可以看到。';
}
@override
String get map_connectToShareMarkers => '连接设备以共享标记';
@override
- String get map_filterNodes => '筛选节点';
+ String get map_filterNodes => '过滤节点';
@override
String get map_nodeTypes => '节点类型';
@@ -1245,19 +1580,22 @@ class AppLocalizationsZh extends AppLocalizations {
String get map_chatNodes => '聊天节点';
@override
- String get map_repeaters => '重复器';
+ String get map_repeaters => '转发节点';
@override
String get map_otherNodes => '其他节点';
@override
- String get map_keyPrefix => '键前缀';
+ String get map_showOverlaps => '重复键重叠';
@override
- String get map_filterByKeyPrefix => '按关键词前缀筛选';
+ String get map_keyPrefix => '关键字前缀';
@override
- String get map_publicKeyPrefix => '公钥前缀';
+ String get map_filterByKeyPrefix => '按关键字前缀筛选';
+
+ @override
+ String get map_publicKeyPrefix => '关键字前缀';
@override
String get map_markers => '标记';
@@ -1266,32 +1604,56 @@ class AppLocalizationsZh extends AppLocalizations {
String get map_showSharedMarkers => '显示共享标记';
@override
- String get map_lastSeenTime => '最后一次查看时间';
+ String get map_showGuessedLocations => '显示猜测的节点位置';
@override
- String get map_sharedPin => '共享 PIN';
+ String get map_showDiscoveryContacts => '显示发现联系人';
+
+ @override
+ String get map_guessedLocation => '猜测的位置';
+
+ @override
+ String get map_lastSeenTime => '最后在线时间';
+
+ @override
+ String get map_sharedPin => '共享标记';
@override
String get map_joinRoom => '加入房间';
@override
- String get map_manageRepeater => '管理重复项';
+ String get map_manageRepeater => '管理转发节点';
+
+ @override
+ String get map_tapToAdd => '点击节点以添加到路径';
+
+ @override
+ String get map_runTrace => '运行路径追踪';
+
+ @override
+ String get map_runTraceWithReturnPath => '沿着相同的路径返回';
+
+ @override
+ String get map_removeLast => '移除最后一个';
+
+ @override
+ String get map_pathTraceCancelled => '路径追踪已取消';
@override
String get mapCache_title => '离线地图缓存';
@override
- String get mapCache_selectAreaFirst => '选择一个区域进行缓存';
+ String get mapCache_selectAreaFirst => '请先选择要缓存的区域';
@override
- String get mapCache_noTilesToDownload => '该区域没有可下载的瓦片。';
+ String get mapCache_noTilesToDownload => '此区域没有可下载的瓦片';
@override
String get mapCache_downloadTilesTitle => '下载瓦片';
@override
String mapCache_downloadTilesPrompt(int count) {
- return '下载 $count 个瓦片用于离线使用?';
+ return '这需要下载 $count 个瓦片';
}
@override
@@ -1304,20 +1666,20 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String mapCache_cachedTilesWithFailed(int downloaded, int failed) {
- return '已缓存 $downloaded 个瓦片 ($failed 失败)';
+ return '已缓存 $downloaded 个瓦片($failed 个失败)';
}
@override
String get mapCache_clearOfflineCacheTitle => '清除离线缓存';
@override
- String get mapCache_clearOfflineCachePrompt => '删除所有缓存地图瓦片?';
+ String get mapCache_clearOfflineCachePrompt => '清除所有缓存的地图瓦片';
@override
String get mapCache_offlineCacheCleared => '离线缓存已清除';
@override
- String get mapCache_noAreaSelected => '未选择任何区域';
+ String get mapCache_noAreaSelected => '未选择区域';
@override
String get mapCache_cacheArea => '缓存区域';
@@ -1330,12 +1692,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String mapCache_estimatedTiles(int count) {
- return '预计瓦片数量:$count';
+ return '估计瓦片数:$count';
}
@override
String mapCache_downloadedTiles(int completed, int total) {
- return '已下载 $completed / $total';
+ return '已下载 $completed/$total';
}
@override
@@ -1374,7 +1736,7 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String time_daysAgo(int days) {
- return '$days 天前';
+ return '$days天前';
}
@override
@@ -1384,22 +1746,22 @@ class AppLocalizationsZh extends AppLocalizations {
String get time_hours => '小时';
@override
- String get time_day => '今天';
+ String get time_day => '天';
@override
String get time_days => '天';
@override
- String get time_week => '本周';
+ String get time_week => '周';
@override
- String get time_weeks => '几周';
+ String get time_weeks => '周';
@override
- String get time_month => '月份';
+ String get time_month => '月';
@override
- String get time_months => '月份';
+ String get time_months => '月';
@override
String get time_minutes => '分钟';
@@ -1411,13 +1773,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get dialog_disconnect => '断开';
@override
- String get dialog_disconnectConfirm => '您确定要断开与此设备的连接吗?';
+ String get dialog_disconnectConfirm => '确定要断开与此设备的连接吗?';
@override
- String get login_repeaterLogin => '重复登录';
+ String get login_repeaterLogin => '转发节点登录';
@override
- String get login_roomLogin => '房间登录';
+ String get login_roomLogin => '房间服务器登录';
@override
String get login_password => '密码';
@@ -1429,13 +1791,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get login_savePassword => '保存密码';
@override
- String get login_savePasswordSubtitle => '密码将安全地存储在这个设备上';
+ String get login_savePasswordSubtitle => '密码将安全地存储在此设备上';
@override
- String get login_repeaterDescription => '输入重复密码以访问设置和状态。';
+ String get login_repeaterDescription => '输入转发节点密码以访问设置和状态。';
@override
- String get login_roomDescription => '输入房间密码以访问设置和状态。';
+ String get login_roomDescription => '输入房间服务器密码以访问设置和状态。';
@override
String get login_routing => '路由';
@@ -1444,10 +1806,10 @@ class AppLocalizationsZh extends AppLocalizations {
String get login_routingMode => '路由模式';
@override
- String get login_autoUseSavedPath => '自动(使用已保存路径)';
+ String get login_autoUseSavedPath => '自动(使用保存的路径)';
@override
- String get login_forceFloodMode => '强制洪水模式';
+ String get login_forceFloodMode => '强制泛洪模式';
@override
String get login_managePaths => '管理路径';
@@ -1466,7 +1828,7 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get login_failedMessage => '登录失败。密码不正确或中继器不可达。';
+ String get login_failedMessage => '登录失败。可能是密码错误或无法连接到服务器。';
@override
String get common_reload => '重新加载';
@@ -1481,13 +1843,7 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String path_usingHopsPath(int count) {
- String _temp0 = intl.Intl.pluralLogic(
- count,
- locale: localeName,
- other: 'hops',
- one: 'hop',
- );
- return '使用 $count $_temp0 路径';
+ return '使用 $count 跳路径';
}
@override
@@ -1497,25 +1853,25 @@ class AppLocalizationsZh extends AppLocalizations {
String get path_currentPathLabel => '当前路径';
@override
- String get path_hexPrefixInstructions => '输入2个字符的十六进制前缀,每个前缀之间用逗号分隔。';
+ String get path_hexPrefixInstructions => '请输入每个中继节点的2字符十六进制前缀,用逗号分隔。';
@override
- String get path_hexPrefixExample => 'A1,F2,3C (每个节点使用其公钥的第一字节)';
+ String get path_hexPrefixExample => '例如:A1, F2, 3C(每个节点使用其公钥的第一字节)';
@override
- String get path_labelHexPrefixes => '十六进制前缀';
+ String get path_labelHexPrefixes => '路径(十六进制前缀)';
@override
- String get path_helperMaxHops => '最大 64 步跳。每个前缀是 2 个十六进制字符(1 字节)';
+ String get path_helperMaxHops => '最多 64 跳。每个前缀由 2 个十六进制字符(1 字节)组成。';
@override
- String get path_selectFromContacts => '或从联系人中选择:';
+ String get path_selectFromContacts => '或从联系人列表中选择:';
@override
- String get path_noRepeatersFound => '未找到任何重复器或房间服务器。';
+ String get path_noRepeatersFound => '未找到任何转发节点或房间服务器。';
@override
- String get path_customPathsRequire => '自定义路径需要中间跳转,这些跳转可以传递消息。';
+ String get path_customPathsRequire => '自定义路径需要中间节点转发消息。';
@override
String path_invalidHexPrefixes(String prefixes) {
@@ -1523,61 +1879,76 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get path_tooLong => '路径太长。允许的最大跳跃次数为 64 次。';
+ String get path_tooLong => '路径过长,最多允许 64 跳。';
@override
String get path_setPath => '设置路径';
@override
- String get repeater_management => '重复器管理';
+ String get repeater_management => '转发节点管理';
@override
String get room_management => '房间服务器管理';
+ @override
+ String get repeater_guest => '重复器信息';
+
+ @override
+ String get room_guest => '服务器信息';
+
@override
String get repeater_managementTools => '管理工具';
+ @override
+ String get repeater_guestTools => '访客工具';
+
@override
String get repeater_status => '状态';
@override
- String get repeater_statusSubtitle => '查看重复器状态、统计信息和邻居';
+ String get repeater_statusSubtitle => '查看转发节点状态、统计和邻居';
@override
String get repeater_telemetry => '遥测';
@override
- String get repeater_telemetrySubtitle => '查看传感器和系统状态的Telemetry数据';
+ String get repeater_telemetrySubtitle => '查看传感器和系统状态数据';
@override
- String get repeater_cli => 'CLI';
+ String get repeater_cli => '命令行';
@override
- String get repeater_cliSubtitle => '发送命令到重复器';
+ String get repeater_cliSubtitle => '向转发节点发送命令';
@override
- String get repeater_neighbours => '邻居';
+ String get repeater_neighbors => '邻居';
@override
- String get repeater_neighboursSubtitle => '查看零跳邻居。';
+ String get repeater_neighborsSubtitle => '查看邻居节点(零跳)';
@override
String get repeater_settings => '设置';
@override
- String get repeater_settingsSubtitle => '配置重复器参数';
+ String get repeater_settingsSubtitle => '配置转发节点参数';
@override
- String get repeater_statusTitle => '重复器状态';
+ String get repeater_clockSyncAfterLogin => '登录后,自动同步时钟';
+
+ @override
+ String get repeater_clockSyncAfterLoginSubtitle => '在成功登录后,自动发送“时钟同步”指令。';
+
+ @override
+ String get repeater_statusTitle => '转发节点状态';
@override
String get repeater_routingMode => '路由模式';
@override
- String get repeater_autoUseSavedPath => '自动(使用已保存路径)';
+ String get repeater_autoUseSavedPath => '自动(使用保存的路径)';
@override
- String get repeater_forceFloodMode => '强制洪水模式';
+ String get repeater_forceFloodMode => '强制泛洪模式';
@override
String get repeater_pathManagement => '路径管理';
@@ -1586,11 +1957,11 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_refresh => '刷新';
@override
- String get repeater_statusRequestTimeout => '状态请求超时。';
+ String get repeater_statusRequestTimeout => '状态请求超时';
@override
String repeater_errorLoadingStatus(String error) {
- return '错误加载状态:$error';
+ return '加载状态时出错:$error';
}
@override
@@ -1600,13 +1971,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_battery => '电池';
@override
- String get repeater_clockAtLogin => '时间 (登录时)';
+ String get repeater_clockAtLogin => '登录时的时钟';
@override
- String get repeater_uptime => '可用时间';
+ String get repeater_uptime => '运行时间';
@override
- String get repeater_queueLength => '排队长度';
+ String get repeater_queueLength => '队列长度';
@override
String get repeater_debugFlags => '调试标志';
@@ -1615,28 +1986,28 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_radioStatistics => '无线电统计';
@override
- String get repeater_lastRssi => '上次RSSI';
+ String get repeater_lastRssi => '上次 RSSI';
@override
- String get repeater_lastSnr => '最后 SNR';
+ String get repeater_lastSnr => '上次 SNR';
@override
- String get repeater_noiseFloor => '噪声地板';
+ String get repeater_noiseFloor => '底噪';
@override
- String get repeater_txAirtime => 'TX Airtime';
+ String get repeater_txAirtime => '发送空中时间';
@override
- String get repeater_rxAirtime => 'RX Airtime';
+ String get repeater_rxAirtime => '接收空中时间';
@override
String get repeater_packetStatistics => '数据包统计';
@override
- String get repeater_sent => '已发送';
+ String get repeater_sent => '发送';
@override
- String get repeater_received => '已收到';
+ String get repeater_received => '接收';
@override
String get repeater_duplicates => '重复';
@@ -1653,17 +2024,17 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String repeater_packetTxTotal(int total, String flood, String direct) {
- return '总计:$total, 洪流:$flood, 直连:$direct';
+ return '总计:$total,泛洪:$flood,直连:$direct';
}
@override
String repeater_packetRxTotal(int total, String flood, String direct) {
- return '总计:$total, 洪流:$flood, 直连:$direct';
+ return '总计:$total,泛洪:$flood,直连:$direct';
}
@override
String repeater_duplicatesFloodDirect(String flood, String direct) {
- return '洪水:$flood, 直通:$direct';
+ return '泛洪:$flood,直连:$direct';
}
@override
@@ -1672,16 +2043,16 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get repeater_settingsTitle => '重复设置';
+ String get repeater_settingsTitle => '转发节点设置';
@override
String get repeater_basicSettings => '基本设置';
@override
- String get repeater_repeaterName => '重复器名称';
+ String get repeater_repeaterName => '转发节点名称';
@override
- String get repeater_repeaterNameHelper => '显示此重复器的名称';
+ String get repeater_repeaterNameHelper => '此转发节点的显示名称';
@override
String get repeater_adminPassword => '管理员密码';
@@ -1696,7 +2067,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_guestPasswordHelper => '只读访问密码';
@override
- String get repeater_radioSettings => '射频设置';
+ String get repeater_radioSettings => '无线电设置';
@override
String get repeater_frequencyMhz => '频率 (MHz)';
@@ -1705,7 +2076,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_frequencyHelper => '300-2500 MHz';
@override
- String get repeater_txPower => 'TX Power';
+ String get repeater_txPower => 'TX 功率';
@override
String get repeater_txPowerHelper => '1-30 dBm';
@@ -1714,7 +2085,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_bandwidth => '带宽';
@override
- String get repeater_spreadingFactor => '扩散因子';
+ String get repeater_spreadingFactor => '扩频因子';
@override
String get repeater_codingRate => '编码速率';
@@ -1726,13 +2097,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_latitude => '纬度';
@override
- String get repeater_latitudeHelper => '十进度的数字(例如:37.7749)';
+ String get repeater_latitudeHelper => '十进制,例如 37.7749';
@override
String get repeater_longitude => '经度';
@override
- String get repeater_longitudeHelper => '十进度的数字(例如:-122.4194)';
+ String get repeater_longitudeHelper => '十进制,例如 -122.4194';
@override
String get repeater_features => '功能';
@@ -1741,25 +2112,25 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_packetForwarding => '数据包转发';
@override
- String get repeater_packetForwardingSubtitle => '启用重复器以转发数据包';
+ String get repeater_packetForwardingSubtitle => '启用转发节点转发数据包';
@override
String get repeater_guestAccess => '访客访问';
@override
- String get repeater_guestAccessSubtitle => '允许访客仅读访问';
+ String get repeater_guestAccessSubtitle => '允许访客只读权限';
@override
String get repeater_privacyMode => '隐私模式';
@override
- String get repeater_privacyModeSubtitle => '隐藏在广告中的姓名/位置';
+ String get repeater_privacyModeSubtitle => '在广播中隐藏姓名/位置';
@override
- String get repeater_advertisementSettings => '广告设置';
+ String get repeater_advertisementSettings => '广播设置';
@override
- String get repeater_localAdvertInterval => '本地广告间隔';
+ String get repeater_localAdvertInterval => '本地广播间隔';
@override
String repeater_localAdvertIntervalMinutes(int minutes) {
@@ -1767,7 +2138,7 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get repeater_floodAdvertInterval => '洪水广告间隔';
+ String get repeater_floodAdvertInterval => '泛洪广播间隔';
@override
String repeater_floodAdvertIntervalHours(int hours) {
@@ -1775,19 +2146,19 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get repeater_encryptedAdvertInterval => '加密广告间隔';
+ String get repeater_encryptedAdvertInterval => '加密广播间隔';
@override
- String get repeater_dangerZone => '危险区域';
+ String get repeater_dangerZone => '危险设置';
@override
- String get repeater_rebootRepeater => '重启重复器';
+ String get repeater_rebootRepeater => '重启转发节点';
@override
- String get repeater_rebootRepeaterSubtitle => '重启重复器设备';
+ String get repeater_rebootRepeaterSubtitle => '重启转发节点设备';
@override
- String get repeater_rebootRepeaterConfirm => '您确定要重启这个中继器吗?';
+ String get repeater_rebootRepeaterConfirm => '确定要重启此转发节点吗?';
@override
String get repeater_regenerateIdentityKey => '重新生成身份密钥';
@@ -1796,19 +2167,19 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_regenerateIdentityKeySubtitle => '生成新的公钥/私钥对';
@override
- String get repeater_regenerateIdentityKeyConfirm => '这将生成一个重复器的新身份。继续吗?';
+ String get repeater_regenerateIdentityKeyConfirm => '这将为转发节点生成新身份,继续吗?';
@override
- String get repeater_eraseFileSystem => '删除文件系统';
+ String get repeater_eraseFileSystem => '擦除文件系统';
@override
- String get repeater_eraseFileSystemSubtitle => '格式化重复文件系统';
+ String get repeater_eraseFileSystemSubtitle => '格式化转发节点文件系统';
@override
- String get repeater_eraseFileSystemConfirm => '警告:这将擦除重复器上的所有数据。 这无法撤销!';
+ String get repeater_eraseFileSystemConfirm => '警告:此操作将清除转发节点上的所有数据,且无法恢复!';
@override
- String get repeater_eraseSerialOnly => '通过串行控制台才能删除。';
+ String get repeater_eraseSerialOnly => '擦除功能仅可通过串行控制台使用。';
@override
String repeater_commandSent(String command) {
@@ -1824,11 +2195,11 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_confirm => '确认';
@override
- String get repeater_settingsSaved => '设置已保存成功';
+ String get repeater_settingsSaved => '设置保存成功';
@override
String repeater_errorSavingSettings(String error) {
- return '保存设置出错:$error';
+ return '保存设置时出错:$error';
}
@override
@@ -1838,7 +2209,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_refreshRadioSettings => '刷新无线电设置';
@override
- String get repeater_refreshTxPower => '刷新 TX 电量';
+ String get repeater_refreshTxPower => '刷新 TX 功率';
@override
String get repeater_refreshLocationSettings => '刷新位置设置';
@@ -1847,13 +2218,13 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_refreshPacketForwarding => '刷新包转发';
@override
- String get repeater_refreshGuestAccess => '刷新访客访问';
+ String get repeater_refreshGuestAccess => '刷新访客权限';
@override
String get repeater_refreshPrivacyMode => '刷新隐私模式';
@override
- String get repeater_refreshAdvertisementSettings => '刷新广告设置';
+ String get repeater_refreshAdvertisementSettings => '刷新广播设置';
@override
String repeater_refreshed(String label) {
@@ -1866,10 +2237,10 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get repeater_cliTitle => '重复器命令行工具';
+ String get repeater_cliTitle => '转发节点命令行';
@override
- String get repeater_debugNextCommand => '调试下一步命令';
+ String get repeater_debugNextCommand => '调试下一条命令';
@override
String get repeater_commandHelp => '帮助';
@@ -1878,7 +2249,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_clearHistory => '清除历史';
@override
- String get repeater_noCommandsSent => '尚未发送任何命令';
+ String get repeater_noCommandsSent => '尚未发送命令';
@override
String get repeater_typeCommandOrUseQuick => '输入命令或使用快捷命令';
@@ -1887,16 +2258,16 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_enterCommandHint => '输入命令...';
@override
- String get repeater_previousCommand => '上一个命令';
+ String get repeater_previousCommand => '上一条命令';
@override
- String get repeater_nextCommand => '下一步命令';
+ String get repeater_nextCommand => '下一条命令';
@override
- String get repeater_enterCommandFirst => '请输入一个命令';
+ String get repeater_enterCommandFirst => '请先输入命令';
@override
- String get repeater_cliCommandFrameTitle => 'CLI 命令窗口';
+ String get repeater_cliCommandFrameTitle => 'CLI 命令帧';
@override
String repeater_cliCommandError(String error) {
@@ -1904,10 +2275,10 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get repeater_cliQuickGetName => '获取姓名';
+ String get repeater_cliQuickGetName => '获取名称';
@override
- String get repeater_cliQuickGetRadio => '获取收音机';
+ String get repeater_cliQuickGetRadio => '获取无线电设置';
@override
String get repeater_cliQuickGetTx => '获取 TX';
@@ -1919,196 +2290,185 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_cliQuickVersion => '版本';
@override
- String get repeater_cliQuickAdvertise => '发布';
+ String get repeater_cliQuickAdvertise => '发送广播';
@override
String get repeater_cliQuickClock => '时钟';
@override
- String get repeater_cliHelpAdvert => '发送广告包';
+ String get repeater_cliQuickClockSync => '同步时钟';
@override
- String get repeater_cliHelpReboot => '重启设备。(请注意,可能会出现“超时”现象,这是正常现象)';
+ String get repeater_cliQuickDiscovery => '发现邻居';
@override
- String get repeater_cliHelpClock => '显示每个设备的当前时间。';
+ String get repeater_cliHelpAdvert => '发送广播包';
@override
- String get repeater_cliHelpPassword => '设置设备的新管理员密码。';
+ String get repeater_cliHelpReboot => '重启设备。(注意:可能会收到超时错误,属于正常现象)';
@override
- String get repeater_cliHelpVersion => '显示设备版本和固件构建日期。';
+ String get repeater_cliHelpClock => '显示设备当前时间';
@override
- String get repeater_cliHelpClearStats => '重置各种统计数值为零。';
+ String get repeater_cliHelpPassword => '设置新的管理员密码';
@override
- String get repeater_cliHelpSetAf => '设置空闲时间因子。';
+ String get repeater_cliHelpVersion => '显示设备版本和固件构建日期';
@override
- String get repeater_cliHelpSetTx => '设置 LoRa 传输功率 (重置生效)';
+ String get repeater_cliHelpClearStats => '重置各种统计数据';
@override
- String get repeater_cliHelpSetRepeat => '启用或禁用此节点的重复器角色。';
+ String get repeater_cliHelpSetAf => '设置时间因子';
+
+ @override
+ String get repeater_cliHelpSetTx => '设置 LoRa 发射功率 (dBm)(重启生效)';
+
+ @override
+ String get repeater_cliHelpSetRepeat => '启用或禁用此节点的转发功能';
@override
String get repeater_cliHelpSetAllowReadOnly =>
- '(房间服务器) 如果“开”了,则空密码登录将被允许,但不能向房间发布内容。(仅限读取)';
+ '(房间服务器)设为“开”则允许空密码登录,但只能读(不能发送)';
@override
- String get repeater_cliHelpSetFloodMax => '设置最大换路包数量(如果 >= 最大,则不转发包)。';
+ String get repeater_cliHelpSetFloodMax => '设置最大传入数据包跳数(≥该值则不转发)';
@override
- String get repeater_cliHelpSetIntThresh =>
- '设置干扰阈值(以 dB 为单位)。默认值为 14。将设置为 0 以禁用通道干扰检测。';
+ String get repeater_cliHelpSetIntThresh => '设置干扰阈值 (dB),默认14,设为0禁用';
@override
- String get repeater_cliHelpSetAgcResetInterval =>
- '设置间隔以重置自动增益控制器。将设置为 0 以禁用。';
+ String get repeater_cliHelpSetAgcResetInterval => '设置 AGC 重置间隔(秒),设为0禁用';
@override
- String get repeater_cliHelpSetMultiAcks => '启用或禁用“双 ACKs”功能。';
+ String get repeater_cliHelpSetMultiAcks => '启用或禁用“多重确认”功能';
@override
- String get repeater_cliHelpSetAdvertInterval =>
- '设置定时器间隔时间为分钟,以发送本地(零跳)广告包。将设置为0以禁用。';
+ String get repeater_cliHelpSetAdvertInterval => '设置本地广播间隔(分钟),设为0禁用';
@override
- String get repeater_cliHelpSetFloodAdvertInterval =>
- '设置定时器间隔时间为小时,以发送洪水广告包。将设置为 0 以禁用。';
+ String get repeater_cliHelpSetFloodAdvertInterval => '设置泛洪广播间隔(小时),设为0禁用';
@override
- String get repeater_cliHelpSetGuestPassword =>
- '设置/更新客人密码。(对于重复器,客人在登录时可以发送“获取统计”请求)';
+ String get repeater_cliHelpSetGuestPassword => '设置/更新访客密码';
@override
- String get repeater_cliHelpSetName => '设置广告名称。';
+ String get repeater_cliHelpSetName => '设置广播名称';
@override
- String get repeater_cliHelpSetLat => '设置广告地图纬度(十进制度)';
+ String get repeater_cliHelpSetLat => '设置广播纬度(十进制)';
@override
- String get repeater_cliHelpSetLon => '设置广告地图经度 (十进位)';
+ String get repeater_cliHelpSetLon => '设置广播经度(十进制)';
@override
- String get repeater_cliHelpSetRadio => '设置全新的无线电参数,并保存到偏好设置。需要执行“重启”命令才能应用。';
+ String get repeater_cliHelpSetRadio => '完全重设无线电参数并保存,需重启生效';
@override
- String get repeater_cliHelpSetRxDelay =>
- '设置(实验性)的基础(必须大于 1 才能生效)是用于对接收到的数据包应用轻微延迟,基于信号强度/得分。将设置为 0 以禁用。';
+ String get repeater_cliHelpSetRxDelay => '(实验性)设置接收延迟基数,设为0禁用';
@override
- String get repeater_cliHelpSetTxDelay =>
- '设置一个与时间-在空气中(time-on-air)的系数,用于洪水模式的数据包,并结合随机插槽系统,以延迟其转发。(以降低碰撞的可能性)';
+ String get repeater_cliHelpSetTxDelay => '通过因子和随机时隙延迟泛洪数据包转发,降低冲突';
@override
- String get repeater_cliHelpSetDirectTxDelay =>
- '与txdelay相同,但用于为直接模式包的转发应用随机延迟。';
+ String get repeater_cliHelpSetDirectTxDelay => '同 txdelay,用于直连模式数据包';
@override
- String get repeater_cliHelpSetBridgeEnabled => '启用/禁用桥梁';
+ String get repeater_cliHelpSetBridgeEnabled => '启用/禁用桥接';
@override
- String get repeater_cliHelpSetBridgeDelay => '设置在重新发送数据包之前延迟时间。';
+ String get repeater_cliHelpSetBridgeDelay => '设置桥接转发延迟';
@override
- String get repeater_cliHelpSetBridgeSource => '选择桥梁是否会重传接收到的数据包或发送的数据包。';
+ String get repeater_cliHelpSetBridgeSource => '选择桥接器转发接收或发送的数据包';
@override
- String get repeater_cliHelpSetBridgeBaud => '设置rs232桥接的串口链路波特率。';
+ String get repeater_cliHelpSetBridgeBaud => '设置 RS232 桥接串口波特率';
@override
- String get repeater_cliHelpSetBridgeSecret => '设置 espnow 桥的秘密。';
+ String get repeater_cliHelpSetBridgeSecret => '设置 ESPNOW 桥接密钥';
@override
- String get repeater_cliHelpSetAdcMultiplier => '设置自定义因子以调整报告的电池电压(仅限部分板卡支持)。';
+ String get repeater_cliHelpSetAdcMultiplier => '设置电池电压校正系数(特定板支持)';
@override
- String get repeater_cliHelpTempRadio =>
- '设置临时无线电参数,持续指定的分钟数,之后恢复为原始无线电参数。(不保存到偏好设置)。';
+ String get repeater_cliHelpTempRadio => '临时设置无线电参数指定分钟,之后恢复(不保存)';
@override
- String get repeater_cliHelpSetPerm =>
- '修改ACL。如果“权限”为零,则删除匹配的条目(通过pubkey前缀)。如果pubkey-hex的完整长度且当前不在ACL中,则添加新条目。通过匹配pubkey前缀更新条目。权限位因固件角色而异,但低2位为:0(Guest)、1(只读)、2(读写)、3(Admin)';
+ String get repeater_cliHelpSetPerm => '修改 ACL,权限位:0访客、1只读、2读写、3管理员';
@override
- String get repeater_cliHelpGetBridgeType => '获取桥接类型:无,RS232,ESPNow';
+ String get repeater_cliHelpGetBridgeType => '支持桥接模式:RS232、ESPNOW';
@override
- String get repeater_cliHelpLogStart => '开始将数据包记录到文件系统。';
+ String get repeater_cliHelpLogStart => '开始记录数据包到文件系统';
@override
- String get repeater_cliHelpLogStop => '停止将数据包记录到文件系统。';
+ String get repeater_cliHelpLogStop => '停止记录数据包';
@override
- String get repeater_cliHelpLogErase => '删除文件系统中的包日志。';
+ String get repeater_cliHelpLogErase => '删除所有记录的数据包';
@override
- String get repeater_cliHelpNeighbors =>
- '显示通过零跳广告收听的其他重复节点列表。 每行是 id-prefix-hex:时间戳:snr-times-4';
+ String get repeater_cliHelpNeighbors => '显示零跳广播收到的其他转发节点列表';
@override
- String get repeater_cliHelpNeighborRemove =>
- '移除邻居列表中第一个匹配的条目(通过十六进制 pubkey 前缀)。';
+ String get repeater_cliHelpNeighborRemove => '从邻居列表删除第一个匹配项(通过公钥前缀)';
@override
- String get repeater_cliHelpRegion => '(仅显示区域) 列出所有已定义的区域和当前的防洪权限。';
+ String get repeater_cliHelpRegion => '(仅串口)列出所有定义区域及当前泛洪权限';
@override
- String get repeater_cliHelpRegionLoad =>
- '注意:这是一个特殊的多命令调用。 随后的每个命令都是一个区域名称(用空格缩进以指示父级层次结构,至少需要一个空格)。 以发送一个空行/命令结束。';
+ String get repeater_cliHelpRegionLoad => '特殊多命令调用,以空行结束';
@override
- String get repeater_cliHelpRegionGet =>
- '搜索具有给定名称前缀的区域(或“”用于全局范围)。回复为“-> region-name (parent-name) ‘F’”';
+ String get repeater_cliHelpRegionGet => '搜索指定前缀的区域';
@override
- String get repeater_cliHelpRegionPut => '添加或更新区域定义,使用指定名称。';
+ String get repeater_cliHelpRegionPut => '添加或更新区域定义';
@override
- String get repeater_cliHelpRegionRemove => '删除指定名称的区域定义。(必须没有子区域)';
+ String get repeater_cliHelpRegionRemove => '删除指定区域定义';
@override
- String get repeater_cliHelpRegionAllowf => '设置指定区域的“洪水”权限。(“”代表全局/遗留范围)';
+ String get repeater_cliHelpRegionAllowf => '为区域设置“泛洪”权限';
@override
- String get repeater_cliHelpRegionDenyf =>
- '移除指定区域的‘F’lood权限。 (注意:目前阶段不建议在此范围内使用,尤其是全局/旧版范围!!)';
+ String get repeater_cliHelpRegionDenyf => '移除区域的“泛洪”权限';
@override
- String get repeater_cliHelpRegionHome => '回复当前“主页”区域。 (注意尚未应用,保留用于未来)';
+ String get repeater_cliHelpRegionHome => '返回当前“主区域”(预留)';
@override
- String get repeater_cliHelpRegionHomeSet => '设置‘主页’区域。';
+ String get repeater_cliHelpRegionHomeSet => '设置“主”区域';
@override
- String get repeater_cliHelpRegionSave => '保存区域列表/地图到存储。';
+ String get repeater_cliHelpRegionSave => '保存区域列表到存储';
@override
- String get repeater_cliHelpGps =>
- '显示GPS状态。当GPS关闭时,回复仅为“关闭”,如果已开启,则回复为“开启”、“状态”、“定位”和卫星数量。';
+ String get repeater_cliHelpGps => '显示 GPS 状态';
@override
- String get repeater_cliHelpGpsOnOff => '切换 GPS 开启状态。';
+ String get repeater_cliHelpGpsOnOff => '切换 GPS 电源';
@override
- String get repeater_cliHelpGpsSync => '同步节点时间与 GPS 钟。';
+ String get repeater_cliHelpGpsSync => '将节点时间与 GPS 同步';
@override
- String get repeater_cliHelpGpsSetLoc => '设置节点位置至 GPS 坐标并保存偏好设置。';
+ String get repeater_cliHelpGpsSetLoc => '将节点坐标设为 GPS 坐标并保存';
@override
- String get repeater_cliHelpGpsAdvert =>
- '提供节点广告配置位置:\n- none:不包含位置在广告中\n- share:分享 GPS 位置(来自 SensorManager)\n- prefs:在偏好设置中投放位置';
+ String get repeater_cliHelpGpsAdvert => '设置位置广播配置:none/share/prefs';
@override
- String get repeater_cliHelpGpsAdvertSet => '设置广告位置配置。';
+ String get repeater_cliHelpGpsAdvertSet => '设置广播位置配置';
@override
String get repeater_commandsListTitle => '命令列表';
@override
- String get repeater_commandsListNote => '注意:对于各种“设置...”命令,也存在“获取...”命令。';
+ String get repeater_commandsListNote => '注意:多数 set 命令也有对应的 get 命令';
@override
String get repeater_general => '通用';
@@ -2117,39 +2477,39 @@ class AppLocalizationsZh extends AppLocalizations {
String get repeater_settingsCategory => '设置';
@override
- String get repeater_bridge => '桥';
+ String get repeater_bridge => '桥接';
@override
- String get repeater_logging => '记录';
+ String get repeater_logging => '日志';
@override
- String get repeater_neighborsRepeaterOnly => '邻居(仅限重复器)';
+ String get repeater_neighborsRepeaterOnly => '邻居(仅转发节点)';
@override
- String get repeater_regionManagementRepeaterOnly => '区域管理(仅限重复器)';
+ String get repeater_regionManagementRepeaterOnly => '区域管理(仅转发节点)';
@override
- String get repeater_regionNote => '区域命令已推出,用于管理区域定义和权限。';
+ String get repeater_regionNote => '区域命令用于管理区域定义和权限';
@override
- String get repeater_gpsManagement => 'GPS管理';
+ String get repeater_gpsManagement => 'GPS 管理';
@override
- String get repeater_gpsNote => 'GPS 命令已引入用于管理与位置相关的主题。';
+ String get repeater_gpsNote => 'GPS 命令用于位置相关任务';
@override
- String get telemetry_receivedData => '接收遥测数据';
+ String get telemetry_receivedData => '接收到的遥测数据';
@override
- String get telemetry_requestTimeout => '遥测请求超时。';
+ String get telemetry_requestTimeout => '遥测请求超时';
@override
String telemetry_errorLoading(String error) {
- return '错误加载遥测数据:$error';
+ return '加载遥测数据时出错:$error';
}
@override
- String get telemetry_noData => '没有可用的 telemetry 数据。';
+ String get telemetry_noData => '暂无遥测数据';
@override
String telemetry_channelTitle(int channel) {
@@ -2169,7 +2529,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get telemetry_temperatureLabel => '温度';
@override
- String get telemetry_currentLabel => '当前';
+ String get telemetry_currentLabel => '电流';
@override
String telemetry_batteryValue(int percent, String volts) {
@@ -2192,10 +2552,10 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get neighbors_receivedData => '收到邻居数据';
+ String get neighbors_receivedData => '已接收邻居信息';
@override
- String get neighbors_requestTimedOut => '邻居请求超时处理。';
+ String get neighbors_requestTimedOut => '邻居请求超时';
@override
String neighbors_errorLoading(String error) {
@@ -2203,19 +2563,19 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get neighbors_repeatersNeighbours => '重复器邻居';
+ String get neighbors_repeatersNeighbors => '转发节点的邻居';
@override
- String get neighbors_noData => '没有可用的邻居数据。';
+ String get neighbors_noData => '暂无邻居信息';
@override
String neighbors_unknownContact(String pubkey) {
- return '未知$pubkey';
+ return '未知 $pubkey';
}
@override
String neighbors_heardAgo(String time) {
- return '听到的时间:$time前';
+ return '听到:$time前';
}
@override
@@ -2228,16 +2588,16 @@ class AppLocalizationsZh extends AppLocalizations {
String get channelPath_otherObservedPaths => '其他观察到的路径';
@override
- String get channelPath_repeaterHops => '重复跳跃';
+ String get channelPath_repeaterHops => '转发节点跳数';
@override
- String get channelPath_noHopDetails => '此包的详细信息未提供。';
+ String get channelPath_noHopDetails => '此数据包未提供详细信息';
@override
String get channelPath_messageDetails => '消息详情';
@override
- String get channelPath_senderLabel => '发件人';
+ String get channelPath_senderLabel => '发送者';
@override
String get channelPath_timeLabel => '时间';
@@ -2251,15 +2611,15 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get channelPath_observedLabel => '已观察';
+ String get channelPath_observedLabel => '观察到的';
@override
String channelPath_observedPathTitle(int index, String hops) {
- return '观察路径 $index • $hops';
+ return '观察到的路径 $index • $hops';
}
@override
- String get channelPath_noLocationData => '没有位置数据';
+ String get channelPath_noLocationData => '无位置信息';
@override
String channelPath_timeWithDate(int day, int month, String time) {
@@ -2275,37 +2635,37 @@ class AppLocalizationsZh extends AppLocalizations {
String get channelPath_unknownPath => '未知';
@override
- String get channelPath_floodPath => '洪水';
+ String get channelPath_floodPath => '泛洪';
@override
- String get channelPath_directPath => '直接';
+ String get channelPath_directPath => '直连';
@override
String channelPath_observedZeroOf(int total) {
- return '0 of $total 跳跃';
+ return '0 / $total 跳';
}
@override
String channelPath_observedSomeOf(int observed, int total) {
- return '已观察到 $observed 步中的 $total 步';
+ return '$observed / $total 跳';
}
@override
String get channelPath_mapTitle => '路径地图';
@override
- String get channelPath_noRepeaterLocations => '此路径没有可用的重复器位置。';
+ String get channelPath_noRepeaterLocations => '此路径上没有可用的转发节点位置信息';
@override
String channelPath_primaryPath(int index) {
- return '路径 $index (主)';
+ return '路径 $index(主要)';
}
@override
String get channelPath_pathLabelTitle => '路径';
@override
- String get channelPath_observedPathHeader => '已观察路径';
+ String get channelPath_observedPathHeader => '观察到的路径';
@override
String channelPath_selectedPathLabel(String label, String prefixes) {
@@ -2313,10 +2673,10 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get channelPath_noHopDetailsAvailable => '此包的跳跃详情不可用。';
+ String get channelPath_noHopDetailsAvailable => '此数据包暂无详细信息';
@override
- String get channelPath_unknownRepeater => '未知重复器';
+ String get channelPath_unknownRepeater => '未知转发节点';
@override
String get community_title => '社区';
@@ -2325,7 +2685,7 @@ class AppLocalizationsZh extends AppLocalizations {
String get community_create => '创建社区';
@override
- String get community_createDesc => '创建新的社区并可通过二维码分享。';
+ String get community_createDesc => '创建新社区并通过二维码分享。';
@override
String get community_join => '加入';
@@ -2335,23 +2695,23 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String community_joinConfirmation(String name) {
- return '您想加入社区 \"$name\" 吗?';
+ return '是否加入社区 \"$name\"?';
}
@override
String get community_scanQr => '扫描社区二维码';
@override
- String get community_scanInstructions => '将相机对准社区二维码';
+ String get community_scanInstructions => '将摄像头对准社区的二维码';
@override
String get community_showQr => '显示二维码';
@override
- String get community_publicChannel => '社区公开';
+ String get community_publicChannel => '社区公共频道';
@override
- String get community_hashtagChannel => '社区标签';
+ String get community_hashtagChannel => '社区标签频道';
@override
String get community_name => '社区名称';
@@ -2361,12 +2721,12 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String community_created(String name) {
- return '社区“$name”已创建';
+ return '社区 \"$name\" 已创建';
}
@override
String community_joined(String name) {
- return '加入社区 \"$name\"';
+ return '已加入社区 \"$name\"';
}
@override
@@ -2374,49 +2734,49 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String community_qrInstructions(String name) {
- return '扫描此二维码加入$name';
+ return '扫描此二维码加入 \"$name\"';
}
@override
- String get community_hashtagPrivacyHint => '社区标签频道仅社区成员可加入';
+ String get community_hashtagPrivacyHint => '仅社区成员可加入社区标签频道。';
@override
String get community_invalidQrCode => '无效的社区二维码';
@override
- String get community_alreadyMember => '已经是会员了';
+ String get community_alreadyMember => '已是成员';
@override
String community_alreadyMemberMessage(String name) {
- return '您已经是 \"$name\" 的会员。';
+ return '您已是 \"$name\" 的成员。';
}
@override
- String get community_addPublicChannel => '添加社区公共频道';
+ String get community_addPublicChannel => '添加公共频道';
@override
- String get community_addPublicChannelHint => '自动添加该社区的公共频道';
+ String get community_addPublicChannelHint => '自动添加此社区的公共频道';
@override
- String get community_noCommunities => '尚未加入任何社区';
+ String get community_noCommunities => '尚未加入任何社区。';
@override
- String get community_scanOrCreate => '扫描二维码或创建社区开始';
+ String get community_scanOrCreate => '扫描二维码或创建社区以开始。';
@override
- String get community_manageCommunities => '管理社群';
+ String get community_manageCommunities => '管理社区';
@override
String get community_delete => '退出社区';
@override
String community_deleteConfirm(String name) {
- return '退出 \"$name\"?';
+ return '是否退出 \"$name\"?';
}
@override
String community_deleteChannelsWarning(int count) {
- return '这也将删除 $count 个频道及其消息。';
+ return '这将同时删除 $count 个频道及其所有消息。';
}
@override
@@ -2425,71 +2785,71 @@ class AppLocalizationsZh extends AppLocalizations {
}
@override
- String get community_regenerateSecret => 'Regenerate Secret';
+ String get community_regenerateSecret => '重新生成密钥';
@override
String community_regenerateSecretConfirm(String name) {
- return 'Regenerate the secret key for \"$name\"? All members will need to scan the new QR code to continue communicating.';
+ return '是否为 \"$name\" 重新生成密钥?所有成员需扫描新的二维码才能继续通信。';
}
@override
- String get community_regenerate => 'Regenerate';
+ String get community_regenerate => '重新生成';
@override
String community_secretRegenerated(String name) {
- return 'Secret regenerated for \"$name\"';
+ return '已为 \"$name\" 重新生成密钥';
}
@override
- String get community_updateSecret => 'Update Secret';
+ String get community_updateSecret => '更新密钥';
@override
String community_secretUpdated(String name) {
- return 'Secret updated for \"$name\"';
+ return '“$name”的密钥已更新';
}
@override
String community_scanToUpdateSecret(String name) {
- return 'Scan the new QR code to update the secret for \"$name\"';
+ return '扫描新二维码以更新 \"$name\" 的密钥';
}
@override
- String get community_addHashtagChannel => '添加社区标签';
+ String get community_addHashtagChannel => '添加标签频道';
@override
- String get community_addHashtagChannelDesc => '添加一个话题频道给此社区';
+ String get community_addHashtagChannelDesc => '为此社区创建标签频道';
@override
String get community_selectCommunity => '选择社区';
@override
- String get community_regularHashtag => '常规话题标签';
+ String get community_regularHashtag => '普通标签';
@override
- String get community_regularHashtagDesc => '公共话题(任何人都可以加入)';
+ String get community_regularHashtagDesc => '公共标签频道(任何人都可参与)';
@override
String get community_communityHashtag => '社区标签';
@override
- String get community_communityHashtagDesc => '仅限社区成员使用';
+ String get community_communityHashtagDesc => '仅限社区成员';
@override
String community_forCommunity(String name) {
- return '对于 $name';
+ return '为 $name';
}
@override
- String get listFilter_tooltip => '筛选和排序';
+ String get listFilter_tooltip => '筛选与排序';
@override
- String get listFilter_sortBy => '按类型排序';
+ String get listFilter_sortBy => '排序方式';
@override
String get listFilter_latestMessages => '最新消息';
@override
- String get listFilter_heardRecently => '最近听说';
+ String get listFilter_heardRecently => '最近听到';
@override
String get listFilter_az => 'A-Z';
@@ -2500,18 +2860,568 @@ class AppLocalizationsZh extends AppLocalizations {
@override
String get listFilter_all => '全部';
+ @override
+ String get listFilter_favorites => '收藏';
+
+ @override
+ String get listFilter_addToFavorites => '添加到收藏';
+
+ @override
+ String get listFilter_removeFromFavorites => '从收藏中移除';
+
@override
String get listFilter_users => '用户';
@override
- String get listFilter_repeaters => '重复器';
+ String get listFilter_repeaters => '转发节点';
@override
String get listFilter_roomServers => '房间服务器';
@override
- String get listFilter_unreadOnly => '未读消息';
+ String get listFilter_unreadOnly => '仅显示未读';
@override
- String get listFilter_newGroup => '新组';
+ String get listFilter_newGroup => '新建群聊';
+
+ @override
+ String get pathTrace_you => '我自己';
+
+ @override
+ String get pathTrace_failed => '路径追踪失败。';
+
+ @override
+ String get pathTrace_notAvailable => '无法获取路径信息。';
+
+ @override
+ String get pathTrace_refreshTooltip => '刷新路径追踪';
+
+ @override
+ String get pathTrace_someHopsNoLocation => '某些跳缺少位置信息!';
+
+ @override
+ String get pathTrace_clearTooltip => '清除路径';
+
+ @override
+ String get losSelectStartEnd => '选择 LOS 的起始节点和结束节点。';
+
+ @override
+ String losRunFailed(String error) {
+ return '视线检查失败:$error';
+ }
+
+ @override
+ String get losClearAllPoints => '清除所有点';
+
+ @override
+ String get losRunToViewElevationProfile => '运行 LOS 查看高程剖面';
+
+ @override
+ String get losMenuTitle => '服务水平菜单';
+
+ @override
+ String get losMenuSubtitle => '点击节点或长按地图以获取自定义点';
+
+ @override
+ String get losShowDisplayNodes => '显示显示节点';
+
+ @override
+ String get losCustomPoints => '自定义积分';
+
+ @override
+ String losCustomPointLabel(int index) {
+ return '自定义 $index';
+ }
+
+ @override
+ String get losPointA => 'A点';
+
+ @override
+ String get losPointB => 'B点';
+
+ @override
+ String losAntennaA(String value, String unit) {
+ return '天线 A: $value $unit';
+ }
+
+ @override
+ String losAntennaB(String value, String unit) {
+ return '天线 B:$value $unit';
+ }
+
+ @override
+ String get losRun => '运行视距';
+
+ @override
+ String get losNoElevationData => '无海拔数据';
+
+ @override
+ String losProfileClear(
+ String distance,
+ String distanceUnit,
+ String clearance,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit,清除 LOS,最小间隙 $clearance $heightUnit';
+ }
+
+ @override
+ String losProfileBlocked(
+ String distance,
+ String distanceUnit,
+ String obstruction,
+ String heightUnit,
+ ) {
+ return '$distance $distanceUnit,被 $obstruction $heightUnit 阻止';
+ }
+
+ @override
+ String get losStatusChecking => '洛斯:正在检查...';
+
+ @override
+ String get losStatusNoData => 'LOS:无数据';
+
+ @override
+ String losStatusSummary(int clear, int total, int blocked, int unknown) {
+ return 'LOS:$clear/$total 清除,$blocked 阻塞,$unknown 未知';
+ }
+
+ @override
+ String get losErrorElevationUnavailable => '一个或多个样本的海拔数据不可用。';
+
+ @override
+ String get losErrorInvalidInput => '用于 LOS 计算的点/高程数据无效。';
+
+ @override
+ String get losRenameCustomPoint => '重命名自定义点';
+
+ @override
+ String get losPointName => '点名称';
+
+ @override
+ String get losShowPanelTooltip => '显示 LOS 面板';
+
+ @override
+ String get losHidePanelTooltip => '隐藏 LOS 面板';
+
+ @override
+ String get losElevationAttribution => '高程数据:Open-Meteo (CC BY 4.0)';
+
+ @override
+ String get losLegendRadioHorizon => '无线电地平线';
+
+ @override
+ String get losLegendLosBeam => '视距波束';
+
+ @override
+ String get losLegendTerrain => '地形';
+
+ @override
+ String get losFrequencyLabel => '频率';
+
+ @override
+ String get losFrequencyInfoTooltip => '查看计算详情';
+
+ @override
+ String get losFrequencyDialogTitle => '无线电地平线计算';
+
+ @override
+ String losFrequencyDialogDescription(
+ double baselineK,
+ double baselineFreq,
+ double frequencyMHz,
+ double kFactor,
+ ) {
+ return '从 $baselineFreq MHz 处的 k=$baselineK 开始,计算调整当前 $frequencyMHz MHz 频段的 k 因子,该因子定义了弯曲的无线电范围上限。';
+ }
+
+ @override
+ String get contacts_pathTrace => '路径追踪';
+
+ @override
+ String get contacts_ping => 'Ping';
+
+ @override
+ String get contacts_repeaterPathTrace => 'Trace 转发节点';
+
+ @override
+ String get contacts_repeaterPing => 'Ping 转发节点';
+
+ @override
+ String get contacts_roomPathTrace => 'Trace 房间服务器';
+
+ @override
+ String get contacts_roomPing => 'Ping 房间服务器';
+
+ @override
+ String get contacts_chatTraceRoute => '路由追踪';
+
+ @override
+ String contacts_pathTraceTo(String name) {
+ return '追踪至 $name 的路径';
+ }
+
+ @override
+ String get contacts_clipboardEmpty => '剪贴板为空';
+
+ @override
+ String get contacts_invalidAdvertFormat => '无效的联系人信息格式';
+
+ @override
+ String get contacts_contactImported => '联系人已导入';
+
+ @override
+ String get contacts_contactImportFailed => '导入联系人失败。';
+
+ @override
+ String get contacts_zeroHopAdvert => '发送零跳广播';
+
+ @override
+ String get contacts_floodAdvert => '发送泛洪广播';
+
+ @override
+ String get contacts_copyAdvertToClipboard => '复制广播到剪贴板';
+
+ @override
+ String get contacts_addContactFromClipboard => '从剪贴板添加联系人';
+
+ @override
+ String get contacts_ShareContact => '复制联系人信息到剪贴板';
+
+ @override
+ String get contacts_ShareContactZeroHop => '通过广播分享联系人';
+
+ @override
+ String get contacts_zeroHopContactAdvertSent => '零跳广播已发送';
+
+ @override
+ String get contacts_zeroHopContactAdvertFailed => '发送联系人广播失败。';
+
+ @override
+ String get contacts_contactAdvertCopied => '广播已复制到剪贴板。';
+
+ @override
+ String get contacts_contactAdvertCopyFailed => '复制广播到剪贴板失败。';
+
+ @override
+ String get notification_activityTitle => 'MeshCore 活动';
+
+ @override
+ String notification_messagesCount(int count) {
+ return '$count 条消息';
+ }
+
+ @override
+ String notification_channelMessagesCount(int count) {
+ return '$count 条频道消息';
+ }
+
+ @override
+ String notification_newNodesCount(int count) {
+ return '$count 个新节点';
+ }
+
+ @override
+ String notification_newTypeDiscovered(String contactType) {
+ return '发现新 $contactType';
+ }
+
+ @override
+ String get notification_receivedNewMessage => '收到新消息';
+
+ @override
+ String get settings_gpxExportRepeaters => '导出转发节点/房间服务器到 GPX';
+
+ @override
+ String get settings_gpxExportRepeatersSubtitle => '导出带位置的转发节点/房间服务器到 GPX 文件';
+
+ @override
+ String get settings_gpxExportContacts => '导出伙伴到 GPX';
+
+ @override
+ String get settings_gpxExportContactsSubtitle => '导出带位置的伙伴到 GPX 文件';
+
+ @override
+ String get settings_gpxExportAll => '导出所有联系人到 GPX';
+
+ @override
+ String get settings_gpxExportAllSubtitle => '导出所有带位置的联系人到 GPX 文件';
+
+ @override
+ String get settings_gpxExportSuccess => 'GPX 文件导出成功';
+
+ @override
+ String get settings_gpxExportNoContacts => '没有可导出的联系人';
+
+ @override
+ String get settings_gpxExportNotAvailable => '您的设备/操作系统不支持';
+
+ @override
+ String get settings_gpxExportError => '导出时出错';
+
+ @override
+ String get settings_gpxExportRepeatersRoom => '转发节点与房间服务器位置';
+
+ @override
+ String get settings_gpxExportChat => '伙伴位置';
+
+ @override
+ String get settings_gpxExportAllContacts => '所有联系人位置';
+
+ @override
+ String get settings_gpxExportShareText => '来自 MeshCore Open 的地图数据导出';
+
+ @override
+ String get settings_gpxExportShareSubject => 'MeshCore Open GPX 地图数据导出';
+
+ @override
+ String get snrIndicator_nearByRepeaters => '附近的重复器';
+
+ @override
+ String get snrIndicator_lastSeen => '最近访问';
+
+ @override
+ String get contactsSettings_title => '联系人设置';
+
+ @override
+ String get contactsSettings_autoAddTitle => '自动发现';
+
+ @override
+ String get contactsSettings_otherTitle => '其他联系人相关设置';
+
+ @override
+ String get contactsSettings_autoAddUsersTitle => '自动添加用户';
+
+ @override
+ String get contactsSettings_autoAddUsersSubtitle => '允许伴侣自动添加发现的用户';
+
+ @override
+ String get contactsSettings_autoAddRepeatersTitle => '自动添加重复器';
+
+ @override
+ String get contactsSettings_autoAddRepeatersSubtitle => '允许伴侣自动添加发现的重复器';
+
+ @override
+ String get contactsSettings_autoAddRoomServersTitle => '自动添加房间服务器';
+
+ @override
+ String get contactsSettings_autoAddRoomServersSubtitle => '允许伴侣自动添加发现的房间服务器';
+
+ @override
+ String get contactsSettings_autoAddSensorsTitle => '自动添加传感器';
+
+ @override
+ String get contactsSettings_autoAddSensorsSubtitle => '允许伴侣自动添加发现的传感器';
+
+ @override
+ String get contactsSettings_overwriteOldestTitle => '覆盖最旧的';
+
+ @override
+ String get contactsSettings_overwriteOldestSubtitle =>
+ '当联系人列表已满时,将替换最老的非收藏联系人。';
+
+ @override
+ String get discoveredContacts_Title => '已发现的联系人';
+
+ @override
+ String get discoveredContacts_noMatching => '没有匹配的联系人';
+
+ @override
+ String get discoveredContacts_searchHint => '搜索已发现的联系人';
+
+ @override
+ String get discoveredContacts_contactAdded => '联系人已添加';
+
+ @override
+ String get discoveredContacts_addContact => '添加联系人';
+
+ @override
+ String get discoveredContacts_copyContact => '复制联系人到剪贴板';
+
+ @override
+ String get discoveredContacts_deleteContact => '删除联系人';
+
+ @override
+ String get discoveredContacts_deleteContactAll => '删除所有发现的联系人';
+
+ @override
+ String get discoveredContacts_deleteContactAllContent => '您确定要删除所有发现的联系人吗?';
+
+ @override
+ String get chat_sendCooldown => '请稍等片刻后再尝试发送。';
+
+ @override
+ String get appSettings_jumpToOldestUnread => '跳转到最旧、未读的文章';
+
+ @override
+ String get appSettings_jumpToOldestUnreadSubtitle =>
+ '在打开包含未读消息的聊天时,请滚动到第一个未读消息,而不是最新的消息。';
+
+ @override
+ String get appSettings_languageHu => '匈牙利';
+
+ @override
+ String get appSettings_languageJa => '日语';
+
+ @override
+ String get appSettings_languageKo => '韩语';
+
+ @override
+ String get radioStats_tooltip => '无线电和网状结构统计数据';
+
+ @override
+ String get radioStats_screenTitle => '广播统计数据';
+
+ @override
+ String get radioStats_notConnected => '连接到设备以查看收音机统计信息。';
+
+ @override
+ String get radioStats_firmwareTooOld => '使用无线电统计功能需要配合使用 v8 或更高版本的固件。';
+
+ @override
+ String get radioStats_waiting => '正在等待数据…';
+
+ @override
+ String radioStats_noiseFloor(int noiseDbm) {
+ return '噪声水平:$noiseDbm dBm';
+ }
+
+ @override
+ String radioStats_lastRssi(int rssiDbm) {
+ return '上次 RSSI 值:$rssiDbm dBm';
+ }
+
+ @override
+ String radioStats_lastSnr(String snr) {
+ return '上次 SNR:$snr dB';
+ }
+
+ @override
+ String radioStats_txAir(int seconds) {
+ return 'TX 频道播出时间(总时长):$seconds 秒';
+ }
+
+ @override
+ String radioStats_rxAir(int seconds) {
+ return 'RX 使用时长(总时长):$seconds 秒';
+ }
+
+ @override
+ String get radioStats_chartCaption => '近期的噪声水平(dBm)。';
+
+ @override
+ String radioStats_stripNoise(int noiseDbm) {
+ return '噪声水平:$noiseDbm dBm';
+ }
+
+ @override
+ String get radioStats_stripWaiting => '正在获取收音机数据…';
+
+ @override
+ String get radioStats_settingsTile => '广播统计数据';
+
+ @override
+ String get radioStats_settingsSubtitle => '噪声水平、RSSI、信噪比和空中时间';
+
+ @override
+ String get translation_title => '翻译';
+
+ @override
+ String get translation_enableTitle => '启用翻译功能';
+
+ @override
+ String get translation_enableSubtitle => '翻译收到的消息,并允许在发送前进行翻译。';
+
+ @override
+ String get translation_composerTitle => '在发送之前进行翻译';
+
+ @override
+ String get translation_composerSubtitle => '控制作曲家翻译图标的默认状态。';
+
+ @override
+ String get translation_targetLanguage => '目标语言';
+
+ @override
+ String get translation_useAppLanguage => '使用应用程序语言';
+
+ @override
+ String get translation_downloadedModelLabel => '下载的模型';
+
+ @override
+ String get translation_presetModelLabel => '预设的 Hugging Face 模型';
+
+ @override
+ String get translation_manualUrlLabel => '手动模型网址';
+
+ @override
+ String get translation_downloadModel => '下载模型';
+
+ @override
+ String get translation_downloading => '正在下载...';
+
+ @override
+ String get translation_working => '工作中...';
+
+ @override
+ String get translation_stop => '停止';
+
+ @override
+ String get translation_mergingChunks => '将下载的片段合并成最终文件...';
+
+ @override
+ String get translation_downloadedModels => '下载的模型';
+
+ @override
+ String get translation_deleteModel => '删除模型';
+
+ @override
+ String get translation_modelDownloaded => '翻译模型已下载。';
+
+ @override
+ String get translation_downloadStopped => '下载已停止。';
+
+ @override
+ String translation_downloadFailed(String error) {
+ return '下载失败:$error';
+ }
+
+ @override
+ String get translation_enterUrlFirst => '首先,请输入模型的 URL。';
+
+ @override
+ String get scanner_linuxPairingShowPin => '显示PIN码';
+
+ @override
+ String get scanner_linuxPairingHidePin => '隐藏 PIN';
+
+ @override
+ String get scanner_linuxPairingPinTitle => '蓝牙配对 PIN';
+
+ @override
+ String scanner_linuxPairingPinPrompt(String deviceName) {
+ return '输入 $deviceName 的 PIN 码(如果为空,则留空)。';
+ }
+
+ @override
+ String get translation_messageTranslation => '消息翻译';
+
+ @override
+ String get translation_translateBeforeSending => '在发送前进行翻译';
+
+ @override
+ String get translation_composerEnabledHint => '消息将在发送前进行翻译。';
+
+ @override
+ String get translation_composerDisabledHint => '使用原始的打字方式发送消息。';
+
+ @override
+ String translation_translateTo(String language) {
+ return '翻译成 $language';
+ }
+
+ @override
+ String get translation_translationOptions => '翻译选项';
+
+ @override
+ String get translation_systemLanguage => '系统语言';
}
diff --git a/lib/l10n/app_nl.arb b/lib/l10n/app_nl.arb
index 75a6cc0..96bdb84 100644
--- a/lib/l10n/app_nl.arb
+++ b/lib/l10n/app_nl.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Kan kanaal {name} niet verwijderen",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "nl",
"appTitle": "MeshCore Open",
"nav_contacts": "Contacten",
@@ -76,7 +84,7 @@
"settings_appSettings": "App Instellingen",
"settings_appSettingsSubtitle": "Notificaties, berichten en kaartinstellingen",
"settings_nodeSettings": "Node Instellingen",
- "settings_nodeName": "Node Naam",
+ "settings_nodeName": "Nodenaam",
"settings_nodeNameNotSet": "Niet ingesteld",
"settings_nodeNameHint": "Voer nodenaam in",
"settings_nodeNameUpdated": "Naam bijgewerkt",
@@ -99,13 +107,13 @@
"settings_sendAdvertisement": "Verzend Advertentie",
"settings_sendAdvertisementSubtitle": "Nu aanwezigheid uitzenden",
"settings_advertisementSent": "Advertentie verzonden",
- "settings_syncTime": "Synchronisatie Tijd",
+ "settings_syncTime": "Tijd Synchroniseren",
"settings_syncTimeSubtitle": "Stel de apparaatklok in op de tijd van de telefoon.",
"settings_timeSynchronized": "Tijdsynchronisatie",
"settings_refreshContacts": "Contacten vernieuwen",
"settings_refreshContactsSubtitle": "Contactlijst opnieuw laden van het apparaat",
"settings_rebootDevice": "Apparaat opnieuw opstarten",
- "settings_rebootDeviceSubtitle": "Herstart het MeshCore apparaat",
+ "settings_rebootDeviceSubtitle": "Herstart het MeshCore-apparaat",
"settings_rebootDeviceConfirm": "Ben je er zeker van dat je het apparaat opnieuw wilt opstarten? Je wordt losgekoppeld.",
"settings_debug": "Debug",
"settings_bleDebugLog": "BLE Debug Log",
@@ -131,20 +139,15 @@
"settings_infoContactsCount": "Aantal Contacten",
"settings_infoChannelCount": "Aantal Kanalen",
"settings_presets": "Presets",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frequentie (MHz)",
"settings_frequencyHelper": "300,0 - 2500,0",
"settings_frequencyInvalid": "Ongeldige frequentie (300-2500 MHz)",
"settings_bandwidth": "Bandbreedte",
"settings_spreadingFactor": "Spreadsnelheid",
"settings_codingRate": "Codeertarief",
- "settings_txPower": "TX Vermogen (dBm)",
+ "settings_txPower": "TX-Vermogen (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Ongeldige TX-vermogen (0-22 dBm)",
- "settings_longRange": "Lange Afstand",
- "settings_fastSpeed": "Hoge Snelheid",
"settings_error": "Fout: {message}",
"@settings_error": {
"placeholders": {
@@ -229,11 +232,11 @@
"appSettings_mapTimeFilter": "Filter tijd op kaart",
"appSettings_showNodesDiscoveredWithin": "Toon nodes ontdekt binnen:",
"appSettings_allTime": "Altijd",
- "appSettings_lastHour": "Laat uur",
- "appSettings_last6Hours": "laatste 6 uur",
- "appSettings_last24Hours": "De laatste 24 uur",
- "appSettings_lastWeek": "Laatste week",
- "appSettings_offlineMapCache": "Offline Kaarten Cache",
+ "appSettings_lastHour": "Afgelopen uur",
+ "appSettings_last6Hours": "Afgelopen 6 uur",
+ "appSettings_last24Hours": "Afgelopen 24 uur",
+ "appSettings_lastWeek": "Afgelopen week",
+ "appSettings_offlineMapCache": "Offline Kaartcache",
"appSettings_noAreaSelected": "Geen gebied geselecteerd",
"appSettings_areaSelectedZoom": "Geselecteerd gebied (zoom {minZoom}-{maxZoom})",
"@appSettings_areaSelectedZoom": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Nieuwe Groep",
"contacts_groupName": "Groepnaam",
"contacts_groupNameRequired": "De groepnaam is verplicht.",
+ "contacts_groupNameReserved": "Deze groepsnaam is gereserveerd",
"contacts_groupAlreadyExists": "De groep \"{name}\" bestaat al.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Open kanaal",
"channels_privateChannel": "Private kanaal",
"channels_editChannel": "Kanaal bewerken",
+ "channels_muteChannel": "Kanaal dempen",
+ "channels_unmuteChannel": "Kanaal dempen opheffen",
"channels_deleteChannel": "Kanaal verwijderen",
"channels_deleteChannelConfirm": "Verwijderen \"{name}\"? Dit kan niet worden teruggedraaid.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Link openen?",
+ "chat_openLinkConfirmation": "Wilt u deze link in uw browser openen?",
+ "chat_open": "Openen",
+ "chat_couldNotOpenLink": "Kan link niet openen: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Ongeldig linkformaat",
"map_title": "Node Map",
"map_noNodesWithLocation": "Geen nodes met locatiegegevens",
"map_nodesNeedGps": "Nodes moeten hun GPS-coördinaten delen\nom op de kaart te verschijnen",
@@ -664,7 +682,7 @@
"map_showSharedMarkers": "Toon gedeelde markeringen",
"map_lastSeenTime": "Laatste Bekeken Tijd",
"map_sharedPin": "Gedeelde pin",
- "map_joinRoom": "Sluit Kamer",
+ "map_joinRoom": "Kamer Toetreden",
"map_manageRepeater": "Beheer Repeater",
"mapCache_title": "Offline Kaarten Cache",
"mapCache_selectAreaFirst": "Select een gebied om eerst in de cache op te slaan",
@@ -860,7 +878,7 @@
"path_tooLong": "Pad is te lang. Maximaal 64 sprongen zijn toegestaan.",
"path_setPath": "Stel Pad in",
"repeater_management": "Beheer Repeaters",
- "repeater_managementTools": "Beheerinstrumenten",
+ "repeater_managementTools": "Beheerfuncties",
"repeater_status": "Status",
"repeater_statusSubtitle": "Status, statistieken en buren bekijken",
"repeater_telemetry": "Telemetry",
@@ -894,8 +912,8 @@
"repeater_lastRssi": "Laatste RSSI",
"repeater_lastSnr": "Laatste SNR",
"repeater_noiseFloor": "Ruisvloer",
- "repeater_txAirtime": "TX Airtime",
- "repeater_rxAirtime": "RX Airtime",
+ "repeater_txAirtime": "TX-zendtijd",
+ "repeater_rxAirtime": "RX-zendtijd",
"repeater_packetStatistics": "Pakketstatistieken",
"repeater_sent": "Verzonden",
"repeater_received": "Ontvangen",
@@ -964,14 +982,14 @@
}
}
},
- "repeater_settingsTitle": "Repeater Instellingen",
+ "repeater_settingsTitle": "Repeaterinstellingen",
"repeater_basicSettings": "Basisinstellingen",
"repeater_repeaterName": "Repeaternaam",
- "repeater_repeaterNameHelper": "Weergave naam voor deze repeater",
+ "repeater_repeaterNameHelper": "Weergavenaam voor deze repeater",
"repeater_adminPassword": "Admin wachtwoord",
- "repeater_adminPasswordHelper": "Volledige toegangspaswoord",
- "repeater_guestPassword": "Wachtwoord Gast",
- "repeater_guestPasswordHelper": "Leesbeheer wachtwoord",
+ "repeater_adminPasswordHelper": "Wachtwoord administratortoegang",
+ "repeater_guestPassword": "Gast wachtwoord",
+ "repeater_guestPasswordHelper": "Wachtwoord gasttoegen",
"repeater_radioSettings": "Radio Instellingen",
"repeater_frequencyMhz": "Frequentie (MHz)",
"repeater_frequencyHelper": "300-2500 MHz",
@@ -980,7 +998,7 @@
"repeater_bandwidth": "Bandbreedte",
"repeater_spreadingFactor": "Spreidingsfactor",
"repeater_codingRate": "Codeertarief",
- "repeater_locationSettings": "Locatie Instellingen",
+ "repeater_locationSettings": "Locatie-instellingen",
"repeater_latitude": "Breedtegraad",
"repeater_latitudeHelper": "Graadseconden (bijv. 37.7749)",
"repeater_longitude": "Lengtegraad",
@@ -990,9 +1008,9 @@
"repeater_packetForwardingSubtitle": "Repeater instellen om pakketten door te sturen",
"repeater_guestAccess": "Toegang voor Gasten",
"repeater_guestAccessSubtitle": "Toegestane leesbeheer toegang voor gasten.",
- "repeater_privacyMode": "Privacy Modus",
+ "repeater_privacyMode": "Privacymodus",
"repeater_privacyModeSubtitle": "Naam/locatie verbergen in advertenties",
- "repeater_advertisementSettings": "Advertentie Instellingen",
+ "repeater_advertisementSettings": "Advertentie-instellingen",
"repeater_localAdvertInterval": "Lokale Advertentie Interval",
"repeater_localAdvertIntervalMinutes": "{minutes} minuten",
"@repeater_localAdvertIntervalMinutes": {
@@ -1055,7 +1073,7 @@
"repeater_refreshLocationSettings": "Instellingen Locatie Vernieuwen",
"repeater_refreshPacketForwarding": "Vernieuwen Pakket Doorversturing",
"repeater_refreshGuestAccess": "Toegang Gast Vernieuwen",
- "repeater_refreshPrivacyMode": "Privacy Mode vernieuwen",
+ "repeater_refreshPrivacyMode": "Privacymode vernieuwen",
"repeater_refreshAdvertisementSettings": "Instellingen Advertentie Bijwerken",
"repeater_refreshed": "{label} is vernieuwd",
"@repeater_refreshed": {
@@ -1076,8 +1094,8 @@
"repeater_cliTitle": "Repeater CLI",
"repeater_debugNextCommand": "Debug Volgende Commando",
"repeater_commandHelp": "Help",
- "repeater_clearHistory": "Verwijder Geschiedenis",
- "repeater_noCommandsSent": "Geen commando's verzonden nog.",
+ "repeater_clearHistory": "Geschiedenis Verwijderen",
+ "repeater_noCommandsSent": "Nog geen commando's verzonden.",
"repeater_typeCommandOrUseQuick": "Typ een opdracht hieronder of gebruik snelle commando's",
"repeater_enterCommandHint": "Voer bevel in...",
"repeater_previousCommand": "Vorige opdracht",
@@ -1092,14 +1110,14 @@
}
}
},
- "repeater_cliQuickGetName": "Haal Naam op",
- "repeater_cliQuickGetRadio": "Radio ontvangen",
- "repeater_cliQuickGetTx": "Krijg TX",
- "repeater_cliQuickNeighbors": "Buren",
- "repeater_cliQuickVersion": "Versie",
- "repeater_cliQuickAdvertise": "Advertenties",
- "repeater_cliQuickClock": "Tijd",
- "repeater_cliHelpAdvert": "Verstuurt een advertentiepakket",
+ "repeater_cliQuickGetName": "Naam opvragen",
+ "repeater_cliQuickGetRadio": "Radio-instellingen opvragen",
+ "repeater_cliQuickGetTx": "TX opvragen",
+ "repeater_cliQuickNeighbors": "Buren opvragen",
+ "repeater_cliQuickVersion": "Versie opvragen",
+ "repeater_cliQuickAdvertise": "Advertenties opvragen",
+ "repeater_cliQuickClock": "Tijd opvragen",
+ "repeater_cliHelpAdvert": "Advertentie uitzenden",
"repeater_cliHelpReboot": "Herstart het apparaat. (let op, je krijgt mogelijk een 'Timeout', wat normaal is)",
"repeater_cliHelpClock": "Toont de huidige tijd per apparaat's klok.",
"repeater_cliHelpPassword": "Stelt een nieuw beheerderswachtwoord in voor het apparaat.",
@@ -1185,7 +1203,7 @@
},
"telemetry_batteryLabel": "Batterij",
"telemetry_voltageLabel": "Spanning",
- "telemetry_mcuTemperatureLabel": "MCU Temperatuur",
+ "telemetry_mcuTemperatureLabel": "MCU-temperatuur",
"telemetry_temperatureLabel": "Temperatuur",
"telemetry_currentLabel": "Huidig",
"telemetry_batteryValue": "{percent}% / {volts}V",
@@ -1328,7 +1346,7 @@
"listFilter_tooltip": "Filteren en sorteren",
"listFilter_sortBy": "Sorteren door",
"listFilter_latestMessages": "Recente berichten",
- "listFilter_heardRecently": "Hoor je onlangs",
+ "listFilter_heardRecently": "Recent gezien",
"listFilter_az": "A-Z",
"listFilter_filters": "Filters",
"listFilter_all": "Alles",
@@ -1344,21 +1362,21 @@
}
}
},
- "repeater_neighbours": "Buren",
- "repeater_neighboursSubtitle": "Bekijk nul hops buren.",
+ "repeater_neighbors": "Buren",
+ "repeater_neighborsSubtitle": "Bekijk nul-hopsburen.",
"neighbors_receivedData": "Ontvangen Buurdata",
"neighbors_requestTimedOut": "Buren vragen om tijdelijk uitgeschakeld.",
"neighbors_errorLoading": "Fout bij het laden van buren: {error}",
- "neighbors_repeatersNeighbours": "Herhalingen Buren",
+ "neighbors_repeatersNeighbors": "Repeatbburen",
"neighbors_noData": "Geen gegevens van buren beschikbaar.",
"channels_createPrivateChannelDesc": "Beveiligd met een geheime sleutel.",
- "channels_createPrivateChannel": "Maak een Privé Kanaal",
- "channels_joinPrivateChannel": "Sluit een Privé Kanaal aan",
- "channels_joinPrivateChannelDesc": "Handmatig een geheime sleutel invoeren.",
- "channels_joinPublicChannel": "Sluit het Open Kanaal",
- "channels_joinPublicChannelDesc": "Iedereen kan dit kanaal aanmelden.",
- "channels_joinHashtagChannel": "Sluit een Hashtag Kanaal",
- "channels_joinHashtagChannelDesc": "Iedereen kan lid worden van hashtag-kanalen.",
+ "channels_createPrivateChannel": "PrivéKanaal Aanmaken",
+ "channels_joinPrivateChannel": "PrivéKanaal Toetreden",
+ "channels_joinPrivateChannelDesc": "Voer handmatig een geheime sleutel in.",
+ "channels_joinPublicChannel": "Publiek Kanaal Toetreden",
+ "channels_joinPublicChannelDesc": "Iedereen kan toetreden tot dit kanaal.",
+ "channels_joinHashtagChannel": "Hashtag-kanaal Aanmaken",
+ "channels_joinHashtagChannelDesc": "Iedereen kan toetreden tot hashtag-kanalen.",
"channels_scanQrCode": "Scan een QR-code",
"channels_scanQrCodeComingSoon": "Komt later",
"channels_enterHashtag": "Voer hashtag in",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Dit verwijdert ook {count} kanaal/kanalen en hun berichten.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Community \"{name}\" verlaten",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Open hashtag (iedereen kan deelnemen)",
"community_communityHashtag": "Gemeenschappelijk Hashtag",
"community_communityHashtagDesc": "Alleen zichtbaar voor leden van de community",
- "community_forCommunity": "Voor {name}"
+ "community_forCommunity": "Voor {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_secretRegenerated": "Geheim hersteld voor \"{name}\"",
+ "community_regenerateSecret": "Regeneer Geheimwoord",
+ "community_regenerateSecretConfirm": "Regeneere de geheime sleutel voor \"{name}\"? Alle leden moeten de nieuwe QR-code scannen om verder te communiceren.",
+ "community_regenerate": "Regeneer",
+ "community_updateSecret": "Bijwerken Geheime",
+ "community_secretUpdated": "Geheim gewijzigd voor \"{name}\"",
+ "community_scanToUpdateSecret": "Scan de nieuwe QR-code om het geheim voor \"{name}\" bij te werken",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Jij",
+ "pathTrace_failed": "Padtrace mislukt.",
+ "pathTrace_notAvailable": "Padtrace niet beschikbaar.",
+ "pathTrace_refreshTooltip": "Path Trace vernieuwen.",
+ "contacts_pathTrace": "Pad Traceren",
+ "contacts_ping": "Pingen",
+ "contacts_repeaterPathTrace": "Pad traceren naar repeater",
+ "contacts_repeaterPing": "Ping repeater",
+ "contacts_roomPathTrace": "Padtrace naar room server",
+ "contacts_roomPing": "Ping kamer server",
+ "contacts_chatTraceRoute": "Route traceren",
+ "contacts_pathTraceTo": "Trace route to {name}",
+ "appSettings_languageUk": "Oekraïens",
+ "contacts_invalidAdvertFormat": "Ongeldige contactgegevens",
+ "contacts_contactImportFailed": "Contact kon niet geïmporteerd worden.",
+ "contacts_zeroHopAdvert": "Zero Hop Reclame",
+ "contacts_floodAdvert": "Overstromingsadvertentie",
+ "contacts_copyAdvertToClipboard": "Advert naar klembord kopiëren",
+ "appSettings_languageRu": "Russisch",
+ "appSettings_enableMessageTracing": "Berichttracking inschakelen",
+ "appSettings_enableMessageTracingSubtitle": "Gedetailleerde routerings- en timing-metadata voor berichten weergeven",
+ "contacts_clipboardEmpty": "Knipbord is leeg.",
+ "contacts_addContactFromClipboard": "Contact uit klembord toevoegen",
+ "contacts_contactImported": "Contact is geïmporteerd.",
+ "contacts_zeroHopContactAdvertSent": "Contact verzonden via advertentie",
+ "contacts_contactAdvertCopied": "Reclame gekopieerd naar Klembord.",
+ "contacts_contactAdvertCopyFailed": "Kopiëren van advertentie naar Clipboard is mislukt.",
+ "contacts_ShareContact": "Contact naar Klembord kopiëren",
+ "contacts_ShareContactZeroHop": "Contact delen via advertentie",
+ "contacts_zeroHopContactAdvertFailed": "Mislukt om contact te verzenden",
+ "notification_activityTitle": "MeshCore Activiteit",
+ "notification_messagesCount": "{count} {count, plural, =1{bericht} other{berichten}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{kanaalbericht} other{kanaalberichten}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{nieuw knooppunt} other{nieuwe knooppunten}}",
+ "notification_newTypeDiscovered": "Nieuw {contactType} ontdekt",
+ "notification_receivedNewMessage": "Nieuw bericht ontvangen",
+ "settings_gpxExportRepeatersSubtitle": "Exporteert repeaters / roomserver met een locatie naar GPX-bestand.",
+ "settings_gpxExportRepeaters": "Exporteer repeaters / roomserver naar GPX",
+ "settings_gpxExportSuccess": "Succesvol GPX-bestand geëxporteerd.",
+ "settings_gpxExportNoContacts": "Geen contacten om te exporteren.",
+ "settings_gpxExportNotAvailable": "Niet ondersteund op uw apparaat/besturingssysteem",
+ "settings_gpxExportError": "Er was een fout bij het exporteren.",
+ "settings_gpxExportContacts": "Companions exporteren naar GPX",
+ "settings_gpxExportAll": "Alle contacten exporteren naar GPX",
+ "settings_gpxExportAllSubtitle": "Exporteert alle contacten met een locatie naar een GPX-bestand.",
+ "settings_gpxExportContactsSubtitle": "Exporteert metgezellen met een locatie naar een GPX-bestand.",
+ "settings_gpxExportRepeatersRoom": "Repeater- en kamer servers locaties",
+ "settings_gpxExportChat": "Locaties van metgezellen",
+ "settings_gpxExportAllContacts": "Alle contactlocaties",
+ "settings_gpxExportShareText": "Kaartgegevens geëxporteerd uit meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open GPX kaartgegevens exporteren",
+ "pathTrace_someHopsNoLocation": "Een of meer van de hops ontbreken een locatie!",
+ "map_removeLast": "Verwijder Laatste",
+ "pathTrace_clearTooltip": "Weg wissen",
+ "map_pathTraceCancelled": "Pad traceren geannuleerd",
+ "map_tapToAdd": "Tik op knooppunten om ze toe te voegen aan het pad",
+ "map_runTrace": "Padeshulp traceren",
+ "scanner_enableBluetooth": "Activeer Bluetooth",
+ "scanner_bluetoothOffMessage": "Zorg ervoor dat Bluetooth is ingeschakeld om naar apparaten te zoeken.",
+ "scanner_chromeRequired": "Chrome-browser vereist",
+ "scanner_chromeRequiredMessage": "Deze webapplicatie vereist Google Chrome of een op Chromium gebaseerde browser voor Bluetooth-ondersteuning.",
+ "scanner_bluetoothOff": "Bluetooth is uitgeschakeld",
+ "snrIndicator_lastSeen": "Laatst gezien",
+ "snrIndicator_nearByRepeaters": "Nabije herhalingseenheden",
+ "chat_ShowAllPaths": "Toon alle paden",
+ "settings_clientRepeat": "Off-Grid Herhalen",
+ "settings_clientRepeatSubtitle": "Laat dit apparaat de berichten van andere apparaten doorsturen.",
+ "settings_clientRepeatFreqWarning": "Om een signaal buiten het netwerk te versturen, zijn frequenties van 433, 869 of 918 MHz vereist.",
+ "settings_aboutOpenMeteoAttribution": "LOS-hoogtegegevens: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Eenheden",
+ "appSettings_unitsMetric": "Metrisch (m / km)",
+ "appSettings_unitsImperial": "Imperiaal (ft / mi)",
+ "map_lineOfSight": "Zichtlijn",
+ "map_losScreenTitle": "Zichtlijn",
+ "losSelectStartEnd": "Selecteer begin- en eindknooppunten voor LOS.",
+ "losRunFailed": "Zichtlijncontrole mislukt: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Wis alle punten",
+ "losRunToViewElevationProfile": "Voer LOS uit om het hoogteprofiel te bekijken",
+ "losMenuTitle": "LOS-menu",
+ "losMenuSubtitle": "Tik op knooppunten of druk lang op de kaart voor aangepaste punten",
+ "losShowDisplayNodes": "Toon weergaveknooppunten",
+ "losCustomPoints": "Aangepaste punten",
+ "losCustomPointLabel": "Aangepast {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punt A",
+ "losPointB": "Punt B",
+ "losAntennaA": "Antenne A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenne B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Voer LOS uit",
+ "losNoElevationData": "Geen hoogtegegevens",
+ "losProfileClear": "{distance} {distanceUnit}, vrije LOS, min. vrije ruimte {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, geblokkeerd door {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: controleren...",
+ "losStatusNoData": "LOS: geen gegevens",
+ "losStatusSummary": "LOS: {clear}/{total} gewist, {blocked} geblokkeerd, {unknown} onbekend",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Hoogtegegevens niet beschikbaar voor een of meer monsters.",
+ "losErrorInvalidInput": "Ongeldige punten/hoogtegegevens voor LOS-berekening.",
+ "losRenameCustomPoint": "Hernoem aangepast punt",
+ "losPointName": "Puntnaam",
+ "losShowPanelTooltip": "Toon LOS-paneel",
+ "losHidePanelTooltip": "LOS-paneel verbergen",
+ "losElevationAttribution": "Hoogtegegevens: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Radiohorizon",
+ "losLegendLosBeam": "Zichtlijn",
+ "losLegendTerrain": "Terrein",
+ "losFrequencyLabel": "Frequentie",
+ "losFrequencyInfoTooltip": "Bekijk details van de berekening",
+ "losFrequencyDialogTitle": "Berekening van de radiohorizon",
+ "losFrequencyDialogDescription": "Beginnend met k={baselineK} bij {baselineFreq} MHz, wordt bij de berekening de k-factor aangepast voor de huidige {frequencyMHz} MHz-band, die de gebogen radiohorizonkap definieert.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Verwijderen uit favorieten",
+ "listFilter_favorites": "Favorieten",
+ "listFilter_addToFavorites": "Toevoegen aan favorieten",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Ongelezen",
+ "contacts_searchRepeaters": "Zoek {number}{str} Repeaters...",
+ "contacts_searchContactsNoNumber": "Zoek contacten...",
+ "contacts_searchUsers": "Zoek {number}{str} gebruikers...",
+ "contacts_searchFavorites": "Zoek {number}{str} favorieten...",
+ "contacts_searchRoomServers": "Zoek {number}{str} Room servers...",
+ "contactsSettings_autoAddUsersTitle": "Gebruikers automatisch toevoegen",
+ "contactsSettings_title": "Instellingen voor contacten",
+ "settings_contactSettings": "Contactinstellingen",
+ "contactsSettings_otherTitle": "Andere instellingen voor contactgerelateerde zaken",
+ "contactsSettings_autoAddRepeatersSubtitle": "Sta toe dat de companion automatisch ontdekte repeaters toevoegt",
+ "contactsSettings_autoAddRoomServersTitle": "Automatisch kamerservers toevoegen",
+ "contactsSettings_autoAddRoomServersSubtitle": "Sta toe dat de companion automatisch ontdekte kamer servers toevoegt.",
+ "contactsSettings_autoAddSensorsTitle": "Automatisch sensoren toevoegen",
+ "settings_contactSettingsSubtitle": "Instellingen voor het toevoegen van contacten",
+ "contactsSettings_autoAddTitle": "Automatische detectie",
+ "contactsSettings_autoAddSensorsSubtitle": "Sta toe dat de companion automatisch ontdekte sensoren toevoegt",
+ "contactsSettings_autoAddUsersSubtitle": "Sta toe dat de companion automatisch ontdekte gebruikers toevoegt",
+ "contactsSettings_autoAddRepeatersTitle": "Automatisch herhalingstoestellen toevoegen",
+ "contactsSettings_overwriteOldestTitle": "Overschrijf Oudste",
+ "discoveredContacts_noMatching": "Geen overeenkomende contacten",
+ "discoveredContacts_addContact": "Contact toevoegen",
+ "discoveredContacts_copyContact": "Kopieer contact naar klembord",
+ "discoveredContacts_deleteContact": "Contact verwijderen",
+ "discoveredContacts_Title": "Ontdekte contacten",
+ "discoveredContacts_contactAdded": "Contact toegevoegd",
+ "discoveredContacts_searchHint": "Ontdekte contacten zoeken",
+ "contactsSettings_overwriteOldestSubtitle": "Wanneer de contactenlijst vol is, wordt de oudste niet-favoriete contactpersoon vervangen.",
+ "common_deleteAll": "Alles verwijderen",
+ "discoveredContacts_deleteContactAll": "Verwijder alle ontdekte contacten",
+ "discoveredContacts_deleteContactAllContent": "Weet u zeker dat u alle ontdekte contacten wilt verwijderen?",
+ "map_guessedLocation": "Geroerde locatie",
+ "map_showGuessedLocations": "Toon de voorspelde locaties van de knopen",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenSubtitle": "Selecteer een gedetecteerd seriële apparaat en verbind deze direct met uw MeshCore-node.",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenTitle": "Verbind via USB",
+ "usbScreenStatus": "Selecteer een USB-apparaat",
+ "usbScreenNote": "USB-serieel is actief op ondersteunde Android-apparaten en desktop-platforms.",
+ "usbScreenEmptyState": "Geen USB-apparaten gevonden. Sluit er een aan en herlaad.",
+ "usbErrorPermissionDenied": "Toegang via USB is geweigerd.",
+ "usbErrorDeviceMissing": "Het geselecteerde USB-apparaat is niet meer beschikbaar.",
+ "usbErrorInvalidPort": "Selecteer een geldig USB-apparaat.",
+ "usbErrorBusy": "Een andere verzoek om een USB-verbinding is al in behandeling.",
+ "usbErrorNotConnected": "Er is geen USB-apparaat aangesloten.",
+ "usbErrorOpenFailed": "Kon het geselecteerde USB-apparaat niet openen.",
+ "usbErrorConnectFailed": "Kon niet verbinding maken met het geselecteerde USB-apparaat.",
+ "usbErrorUnsupported": "USB-serieel is niet ondersteund op deze platform.",
+ "usbErrorAlreadyActive": "Een USB-verbinding is al actief.",
+ "usbErrorNoDeviceSelected": "Geen USB-apparaat is geselecteerd.",
+ "usbErrorPortClosed": "De USB-verbinding is niet actief.",
+ "usbFallbackDeviceName": "Web-serieapparaat",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbConnectionFailed": "Fout bij de USB-verbinding: {error}",
+ "usbStatus_notConnected": "Selecteer een USB-apparaat",
+ "usbStatus_connecting": "Verbinding maken met USB-apparaat...",
+ "usbStatus_searching": "Zoeken naar USB-apparaten...",
+ "usbErrorConnectTimedOut": "Verbinding is verbroken. Zorg ervoor dat het apparaat de juiste USB-firmware heeft.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpScreenTitle": "Verbind via TCP",
+ "tcpHostLabel": "IP-adres",
+ "tcpHostHint": "192.168.40.10",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpPortLabel": "Poort",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Voer het eindpunt in en verbind",
+ "tcpStatus_connectingTo": "Verbinding maken met {endpoint}...",
+ "tcpErrorHostRequired": "Een IP-adres is vereist.",
+ "tcpErrorPortInvalid": "De poortwaarde moet tussen 1 en 65535 liggen.",
+ "tcpErrorUnsupported": "TCP-transport wordt niet ondersteund op deze platform.",
+ "tcpErrorTimedOut": "De TCP-verbinding is verlopen.",
+ "tcpConnectionFailed": "Verbinding met TCP mislukt: {error}",
+ "map_showDiscoveryContacts": "Ontdek contacten weergeven",
+ "map_setAsMyLocation": "Stel dit in als mijn locatie",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Privacyinstellingen",
+ "settings_privacySubtitle": "Beheer welke informatie wordt gedeeld",
+ "settings_telemetryLocationMode": "Telemetrie-locatiemodus",
+ "settings_telemetryEnvironmentMode": "Telemetrie-omgevingsmodus",
+ "settings_advertLocation": "Advertentielocatie",
+ "settings_advertLocationSubtitle": "Locatie opnemen in advertentie",
+ "settings_privacySettingsDescription": "Kies welke informatie uw apparaat deelt met anderen",
+ "settings_allowByContact": "Toestaan op basis van contactvlaggen",
+ "settings_allowAll": "Alles toestaan",
+ "settings_denyAll": "Weiger alles",
+ "contact_info": "Contactinformatie",
+ "settings_telemetryBaseMode": "Telemetrie-basismodus",
+ "contact_teleBase": "Telemetrie_basis",
+ "contact_teleLoc": "Telemetrielocatie",
+ "contact_teleLocSubtitle": "Locatiegegevens delen toestaan",
+ "contact_teleEnv": "Telemetrieomgeving",
+ "contact_teleEnvSubtitle": "Delen van omgevingsensordata toestaan",
+ "contact_settings": "Contactinstellingen",
+ "contact_telemetry": "Telemetrie",
+ "contact_lastSeen": "Laatst gezien",
+ "contact_clearChat": "Chat leegmaken",
+ "contact_teleBaseSubtitle": "Sta delen van batterij niveau en basis telemetrie toe",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeightSubtitle": "Het maximale gewicht dat een route kan bereiken door succesvolle leveringen.",
+ "appSettings_initialRouteWeight": "เริ่มต้น gewicht van de route",
+ "appSettings_maxRouteWeight": "Maximale gewicht voor de route",
+ "appSettings_initialRouteWeightSubtitle": "Startgewicht voor nieuwe, ontdekte routes",
+ "appSettings_routeWeightSuccessIncrement": "Toename in het gewicht van het succes",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Gewicht wordt toegevoegd aan een route na een succesvolle levering.",
+ "appSettings_routeWeightFailureDecrement": "Vermindering van het gewicht van fouten",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Gewicht verwijderd van een pad na een mislukte levering",
+ "appSettings_maxMessageRetries": "Aantal pogingen om berichten te versturen",
+ "appSettings_maxMessageRetriesSubtitle": "Aantal pogingen om een bericht opnieuw te versturen voordat het als mislukt wordt gemarkeerd",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Telemetrie-modus bijgewerkt",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "map_showOverlaps": "Herhalingssleutel overlapt",
+ "map_runTraceWithReturnPath": "Terugkeren op hetzelfde pad.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_jumpToOldestUnread": "Ga naar het oudste ongelezen bericht",
+ "appSettings_jumpToOldestUnreadSubtitle": "Bij het openen van een chat met ongelezen berichten, scroll dan naar het eerste ongelezen bericht, in plaats van naar het meest recente.",
+ "chat_sendCooldown": "Gelieve even te wachten voordat u opnieuw verzendt.",
+ "appSettings_languageHu": "Hongaars",
+ "appSettings_languageJa": "Japanisch",
+ "appSettings_languageKo": "Koreaans",
+ "radioStats_tooltip": "Statistieken voor radio en mesh-netwerken",
+ "radioStats_screenTitle": "Statistieken over radio",
+ "radioStats_notConnected": "Verbind met een apparaat om radio-statistieken te bekijken.",
+ "radioStats_firmwareTooOld": "Om de statistieken via radio te kunnen gebruiken, is firmware versie 8 of een nieuwere vereist.",
+ "radioStats_waiting": "Wacht op gegevens…",
+ "radioStats_noiseFloor": "Ruisfrequentie: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Laatste RSSI-waarde: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Laatste SNR: {snr} dB",
+ "radioStats_txAir": "TX-tijd (totaal): {seconds} s",
+ "radioStats_rxAir": "Tijd besteed met RX (totaal): {seconds} s",
+ "radioStats_chartCaption": "Ruisfrequentie (dBm) over recente metingen.",
+ "radioStats_stripNoise": "Ruisfrequentie: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Radio-statistieken ophalen…",
+ "radioStats_settingsTile": "Statistieken over radio",
+ "radioStats_settingsSubtitle": "Ruimtelijke ruis, RSSI, SNR en beschikbare tijd",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "Vertaal inkomende berichten en maak het mogelijk om berichten vooraf te vertalen.",
+ "translation_enableTitle": "Activeer vertaling",
+ "translation_title": "Vertaling",
+ "translation_composerTitle": "Vertaal voor verzending",
+ "translation_composerSubtitle": "Stelt de standaardstatus van het pictogram voor de vertaling van de componist in.",
+ "translation_useAppLanguage": "Gebruik de taal van de app",
+ "translation_targetLanguage": "Doeltaal",
+ "translation_downloadedModelLabel": "Gedownloade model",
+ "translation_presetModelLabel": "Voorgeprogrammeerd Hugging Face-model",
+ "translation_manualUrlLabel": "URL van de handleiding",
+ "translation_downloadModel": "Download het model",
+ "translation_downloading": "Downloaden...",
+ "translation_working": "Werken...",
+ "translation_mergingChunks": "Het samenvoegen van de gedownloade stukken tot één eindbestand...",
+ "translation_stop": "Stoppen",
+ "translation_downloadedModels": "Gedownloade modellen",
+ "translation_deleteModel": "Model verwijderen",
+ "translation_modelDownloaded": "Vertalingmodel gedownload.",
+ "translation_downloadStopped": "Download is afgebroken.",
+ "translation_downloadFailed": "Download mislukt: {error}",
+ "translation_enterUrlFirst": "Voer eerst een URL van een model in.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "Stuur berichten in de oorspronkelijke, getypte taal.",
+ "translation_translateBeforeSending": "Vertaal voor verzending",
+ "translation_composerEnabledHint": "De berichten worden vertaald voordat ze verzonden worden.",
+ "translation_messageTranslation": "Berichtvertaling",
+ "translation_translationOptions": "Opties voor vertaling",
+ "translation_systemLanguage": "Taal van het systeem",
+ "translation_translateTo": "Vertalen naar {language}",
+ "scanner_linuxPairingShowPin": "Toon PIN",
+ "scanner_linuxPairingHidePin": "PIN verbergen",
+ "scanner_linuxPairingPinPrompt": "Voer PIN in voor {deviceName} (laat leeg als er geen is).",
+ "scanner_linuxPairingPinTitle": "Bluetooth‑koppelings‑PIN",
+ "repeater_cliQuickDiscovery": "Ontdek Buren",
+ "repeater_cliQuickClockSync": "Kloksynchronisatie",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Automatisch een \"klok synchroniseren\" bericht versturen na een succesvolle inlog.",
+ "repeater_clockSyncAfterLogin": "Na het inloggen, klok synchroniseren",
+ "repeater_guestTools": "Gastenfuncties",
+ "room_guest": "Informatie over de server",
+ "chat_sendMessage": "Verzend bericht",
+ "repeater_guest": "Informatie over herhalingsapparatuur"
}
diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb
index 50732d1..b62a78a 100644
--- a/lib/l10n/app_pl.arb
+++ b/lib/l10n/app_pl.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Nie udało się usunąć kanału \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "pl",
"appTitle": "MeshCore Open",
"nav_contacts": "Kontakty",
@@ -9,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",
@@ -20,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",
@@ -48,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": {
@@ -79,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",
@@ -90,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_privacyModeSubtitle": "Ukryj imię/lokalizację w reklamach",
- "settings_privacyModeToggle": "Włącz tryb prywatności, aby ukryć swoje imię i lokalizację w reklamach.",
+ "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 Reklamę",
- "settings_sendAdvertisementSubtitle": "Obecność transmisji jest teraz",
- "settings_advertisementSent": "Reklama wysłana",
- "settings_syncTime": "Czas synchronizacji",
+ "settings_sendAdvertisement": "Wyślij rozgłoszenie",
+ "settings_sendAdvertisementSubtitle": "Nadaj obecność teraz",
+ "settings_advertisementSent": "Rozgłoszenie wysłane",
+ "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": {
@@ -122,29 +130,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_aboutDescription": "Otwartoźródłowy klient Flutter dla urządzeń MeshCore LoRa do sieci mesh.",
+ "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_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
+ "settings_presets": "Presety",
"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_longRange": "Długi zasięg",
- "settings_fastSpeed": "Szybka prędkość",
"settings_error": "Błąd: {message}",
"@settings_error": {
"placeholders": {
@@ -176,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",
@@ -184,20 +187,20 @@
"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_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": {
@@ -210,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ż Powtórniki",
- "appSettings_showRepeatersSubtitle": "Wyświetl węzły powtarzające się 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",
@@ -226,15 +229,15 @@
}
}
},
- "appSettings_mapTimeFilter": "Filtrowanie Czasu Mapy",
+ "appSettings_mapTimeFilter": "Filtr 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_noAreaSelected": "Nie zaznaczono żadnej powierzchni.",
+ "appSettings_lastWeek": "Ostatni tydzień",
+ "appSettings_offlineMapCache": "Pamięć podręczna map offline",
+ "appSettings_noAreaSelected": "Nie wybrano żadnego obszaru.",
"appSettings_areaSelectedZoom": "Wybrany obszar (skala {minZoom}-{maxZoom})",
"@appSettings_areaSelectedZoom": {
"placeholders": {
@@ -248,17 +251,27 @@
},
"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_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",
"contacts_noContacts": "Brak jeszcze kontaktów.",
- "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia reklamują się.",
- "contacts_searchContacts": "Wyszukaj kontakty...",
+ "contacts_contactsWillAppear": "Kontakty będą wyświetlane, gdy urządzenia nadają rozgłoszenia.",
+ "contacts_searchContacts": "Wyszukaj {number}{str} {number, plural, one{kontakt} few{kontakty} many{kontaktów} other{kontaktu}}...",
+ "@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",
- "contacts_removeConfirm": "Usuń {contactName} z kontaktów?",
+ "contacts_removeConfirm": "Usunąć {contactName} z kontaktów?",
"@contacts_removeConfirm": {
"placeholders": {
"contactName": {
@@ -266,7 +279,7 @@
}
}
},
- "contacts_manageRepeater": "Zarządzaj Powtórzami",
+ "contacts_manageRepeater": "Zarządzaj przekaźnikiem",
"contacts_roomLogin": "Logowanie do pokoju",
"contacts_openChat": "Otwórz czat",
"contacts_editGroup": "Edytuj Grupę",
@@ -282,6 +295,7 @@
"contacts_newGroup": "Nowa Grupa",
"contacts_groupName": "Nazwa grupy",
"contacts_groupNameRequired": "Nazwa grupy jest wymagana",
+ "contacts_groupNameReserved": "Ta nazwa grupy jest zastrzeżona",
"contacts_groupAlreadyExists": "Grupa \"{name}\" już istnieje",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -293,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": "Ostatnie połączenie",
- "contacts_lastSeenMinsAgo": "Ostatnie połączenie {minutes} min temu",
+ "contacts_lastSeenNow": "niedawno",
+ "contacts_lastSeenMinsAgo": "~ {minutes} min",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +316,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "Ostatni raz widziany 1 godzinę temu",
- "contacts_lastSeenHoursAgo": "Ostatnie połączenie {hours} godzin temu",
+ "contacts_lastSeenHourAgo": "~ 1 godz.",
+ "contacts_lastSeenHoursAgo": "~ {hours} godz.",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +325,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "Ostatni raz widziany 1 dzień temu",
- "contacts_lastSeenDaysAgo": "Ostatnie połączenie {days} dni temu",
+ "contacts_lastSeenDayAgo": "~ 1 dzień",
+ "contacts_lastSeenDaysAgo": "~ {days} dni",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -333,12 +347,14 @@
}
}
},
- "channels_hashtagChannel": "Kanał z hashtagami",
+ "channels_hashtagChannel": "Kanał hashtagów",
"channels_public": "Publiczny",
- "channels_private": "Prywatne",
+ "channels_private": "Prywatny",
"channels_publicChannel": "Kanał publiczny",
"channels_privateChannel": "Prywatny kanał",
"channels_editChannel": "Edytuj kanał",
+ "channels_muteChannel": "Wycisz kanał",
+ "channels_unmuteChannel": "Wyłącz wyciszenie kanału",
"channels_deleteChannel": "Usuń kanał",
"channels_deleteChannelConfirm": "Usuń \"{name}\"? Nie można tego cofnąć.",
"@channels_deleteChannelConfirm": {
@@ -360,11 +376,11 @@
"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.",
- "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": {
@@ -395,7 +411,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",
@@ -407,7 +423,7 @@
}
}
},
- "chat_replyTo": "Odpowiedz {name}",
+ "chat_replyTo": "Odpowiedz do {name}",
"@chat_replyTo": {
"placeholders": {
"name": {
@@ -435,8 +451,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": {
@@ -452,8 +468,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...",
@@ -470,9 +486,9 @@
"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_noBleActivity": "Brak aktywności BLE jeszcze.",
+ "debugLog_frames": "Ramki",
+ "debugLog_rawLogRx": "Surowy log RX",
+ "debugLog_noBleActivity": "Brak aktywności BLE.",
"debugFrame_length": "Długość ramy: {count} bajtów",
"@debugFrame_length": {
"placeholders": {
@@ -490,7 +506,7 @@
}
},
"debugFrame_textMessageHeader": "Wiadomość tekstowa:",
- "debugFrame_destinationPubKey": "- Oznaczenie PubKey: {pubKey}",
+ "debugFrame_destinationPubKey": "- Docelowy klucz publiczny: {pubKey}",
"@debugFrame_destinationPubKey": {
"placeholders": {
"pubKey": {
@@ -498,7 +514,7 @@
}
}
},
- "debugFrame_timestamp": "- Timestamp: {timestamp}",
+ "debugFrame_timestamp": "- Znacznik czasu: {timestamp}",
"@debugFrame_timestamp": {
"placeholders": {
"timestamp": {
@@ -526,7 +542,7 @@
}
},
"debugFrame_textTypeCli": "CLI",
- "debugFrame_textTypePlain": "Proste",
+ "debugFrame_textTypePlain": "Zwykły",
"debugFrame_text": "- Tekst: \"{text}\"",
"@debugFrame_text": {
"placeholders": {
@@ -535,16 +551,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": "Skacz",
- "chat_hopPlural": "skoczkowie",
- "chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}",
+ "chat_hopSingular": "skok",
+ "chat_hopPlural": "skoki",
+ "chat_hopsCount": "{count} {count, plural, one{skok} few{skoki} many{skoków} other{skoków}}",
"@chat_hopsCount": {
"placeholders": {
"count": {
@@ -554,15 +570,15 @@
},
"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ę 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_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}",
@@ -579,11 +595,11 @@
"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",
- "chat_floodForced": "Powodowana Powódź",
+ "chat_floodForced": "Zalew (wymuszony)",
"chat_directForced": "Bezpośrednio (wymuszono)",
"chat_hopsForced": "{count} skoków (wymuszonych)",
"@chat_hopsForced": {
@@ -593,10 +609,10 @@
}
}
},
- "chat_floodAuto": "Powodzie (automatyczne)",
+ "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": {
@@ -604,6 +620,18 @@
}
}
},
+ "chat_openLink": "Otworzyć link?",
+ "chat_openLinkConfirmation": "Czy chcesz otworzyć ten link w przeglądarce?",
+ "chat_open": "Otwórz",
+ "chat_couldNotOpenLink": "Nie można otworzyć linku: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Nieprawidłowy format linku",
"map_title": "Mapa węzłów",
"map_noNodesWithLocation": "Brak węzłów z danymi lokalizacyjnymi",
"map_nodesNeedGps": "Węzły muszą udostępniać swoje współrzędne GPS,\naby pojawić się na mapie.",
@@ -615,7 +643,7 @@
}
}
},
- "map_pinsCount": "Pinki: {count}",
+ "map_pinsCount": "Pinezki: {count}",
"@map_pinsCount": {
"placeholders": {
"count": {
@@ -624,26 +652,26 @@
}
},
"map_chat": "Rozmowa",
- "map_repeater": "Powtórzacz",
+ "map_repeater": "Przekaźnik",
"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": {
@@ -651,26 +679,26 @@
}
}
},
- "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",
- "map_repeaters": "Powtarzacze",
+ "map_repeaters": "Przekaźniki",
"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_lastSeenTime": "Ostatni raz widiany",
- "map_sharedPin": "Podzielony PIN",
+ "map_publicKeyPrefix": "Prefiks klucza publicznego",
+ "map_markers": "Znaczniki",
+ "map_showSharedMarkers": "Pokaż udostępnione znaczniki.",
+ "map_lastSeenTime": "Ostatni raz widziany",
+ "map_sharedPin": "Udostępniona pinezka",
"map_joinRoom": "Dołącz do pokoju",
- "map_manageRepeater": "Zarządzaj Powtórzami",
- "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?",
+ "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.",
+ "mapCache_downloadTilesTitle": "Pobierz kafelki",
+ "mapCache_downloadTilesPrompt": "Pobrać {count} kafelków do użytku offline?",
"@mapCache_downloadTilesPrompt": {
"placeholders": {
"count": {
@@ -679,7 +707,7 @@
}
},
"mapCache_downloadAction": "Pobierz",
- "mapCache_cachedTiles": "Pamiętanych {count} płytek",
+ "mapCache_cachedTiles": "Zapisano w pamięci podręcznej {count} kafelków",
"@mapCache_cachedTiles": {
"placeholders": {
"count": {
@@ -687,7 +715,7 @@
}
}
},
- "mapCache_cachedTilesWithFailed": "Pamiętane {downloaded} płytki ({failed} nieudane)",
+ "mapCache_cachedTilesWithFailed": "Zapisano w pamięci podręcznej {downloaded} kafelków ({failed} nieudanych)",
"@mapCache_cachedTilesWithFailed": {
"placeholders": {
"downloaded": {
@@ -699,13 +727,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": {
@@ -724,7 +752,7 @@
}
}
},
- "mapCache_downloadTilesButton": "Pobierz Paski",
+ "mapCache_downloadTilesButton": "Pobierz kafelki",
"mapCache_clearCacheButton": "Wyczyść pamięć podręczną",
"mapCache_failedDownloads": "Nieudane pobrania: {count}",
"@mapCache_failedDownloads": {
@@ -783,23 +811,23 @@
"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 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 statusu.",
+ "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": "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}",
@@ -821,10 +849,10 @@
}
}
},
- "login_failedMessage": "Logowanie nie powiodło się. Hasło jest nieprawidłowe albo repeater jest nieosiągalny.",
- "common_reload": "Ponownie załadować",
+ "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": "Aktualny ścieżka: {path}",
+ "path_currentPath": "Aktualna ścieżka: {path}",
"@path_currentPath": {
"placeholders": {
"path": {
@@ -832,7 +860,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": {
@@ -841,13 +869,13 @@
}
},
"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_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": {
@@ -859,23 +887,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 przekaźnikami",
"repeater_managementTools": "Narzędzia Zarządzania",
"repeater_status": "Status",
- "repeater_statusSubtitle": "Wyświetl status powtarzacza, statystyki i sąsiadów.",
- "repeater_telemetry": "Telemetry",
+ "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 powielacza",
+ "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": "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": {
@@ -887,15 +915,15 @@
"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",
"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",
@@ -917,7 +945,7 @@
}
}
},
- "repeater_packetTxTotal": "Razem: {total}, Powodzenie: {flood}, Bezpośrednio: {direct}",
+ "repeater_packetTxTotal": "Razem: {total}, Zalew: {flood}, Bezpośrednio: {direct}",
"@repeater_packetTxTotal": {
"placeholders": {
"total": {
@@ -931,7 +959,7 @@
}
}
},
- "repeater_packetRxTotal": "Razem: {total}, Powodzenie: {flood}, Bezpośrednio: {direct}",
+ "repeater_packetRxTotal": "Razem: {total}, Zalew: {flood}, Bezpośrednio: {direct}",
"@repeater_packetRxTotal": {
"placeholders": {
"total": {
@@ -945,7 +973,7 @@
}
}
},
- "repeater_duplicatesFloodDirect": "Powodzie: {flood}, Bezpośrednie: {direct}",
+ "repeater_duplicatesFloodDirect": "Zalew: {flood}, Bezpośrednie: {direct}",
"@repeater_duplicatesFloodDirect": {
"placeholders": {
"flood": {
@@ -964,22 +992,22 @@
}
}
},
- "repeater_settingsTitle": "Ustawienia Powtórki",
+ "repeater_settingsTitle": "Ustawienia przekaźnika",
"repeater_basicSettings": "Podstawowe Ustawienia",
- "repeater_repeaterName": "Nazwa Powtórnika",
- "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": "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",
- "repeater_txPower": "TX Power",
+ "repeater_txPower": "Moc TX",
"repeater_txPowerHelper": "1-30 dBm",
"repeater_bandwidth": "Przepustowość",
- "repeater_spreadingFactor": "Rozkład Czynnika",
- "repeater_codingRate": "Stawka kodowania",
+ "repeater_spreadingFactor": "Współczynnik rozpraszania",
+ "repeater_codingRate": "Współczynnik kodowania",
"repeater_locationSettings": "Ustawienia Lokalizacji",
"repeater_latitude": "Szerokość",
"repeater_latitudeHelper": "Stopnie dziesiętne (np. 37.7749)",
@@ -987,13 +1015,13 @@
"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 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",
- "repeater_privacyModeSubtitle": "Ukryj imię/lokalizację w reklamach",
- "repeater_advertisementSettings": "Ustawienia Reklam",
- "repeater_localAdvertInterval": "Interwał Reklamy Lokalnej",
+ "repeater_privacyMode": "Tryb prywatności",
+ "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": {
@@ -1002,7 +1030,7 @@
}
}
},
- "repeater_floodAdvertInterval": "Interwał Reklamy Powodziowej",
+ "repeater_floodAdvertInterval": "Interwał rozgłoszenia zalewowego",
"repeater_floodAdvertIntervalHours": "{hours} godzin",
"@repeater_floodAdvertIntervalHours": {
"placeholders": {
@@ -1011,17 +1039,17 @@
}
}
},
- "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_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": "To zostanie wygenerowane nowe 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 powielacza",
- "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": {
@@ -1050,13 +1078,13 @@
}
},
"repeater_refreshBasicSettings": "Odśwież Podstawowe Ustawienia",
- "repeater_refreshRadioSettings": "Odśwież Ustawienia Radio",
- "repeater_refreshTxPower": "Odśwież TX power",
+ "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": {
@@ -1073,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ę",
@@ -1083,7 +1111,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": {
@@ -1092,14 +1120,14 @@
}
}
},
- "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 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.",
@@ -1107,18 +1135,18 @@
"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 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 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_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_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 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)",
"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).",
@@ -1131,19 +1159,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 reklamom 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 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.",
@@ -1151,22 +1179,22 @@
"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_commandsListNote": "UWAGA: dla różnych poleceń \"set ...\" istnieje również polecenie \"get ...\".",
"repeater_general": "Ogólne",
"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ą.",
- "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": {
@@ -1187,7 +1215,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": {
@@ -1199,7 +1227,7 @@
}
}
},
- "telemetry_voltageValue": "{volts}W",
+ "telemetry_voltageValue": "{volts}V",
"@telemetry_voltageValue": {
"placeholders": {
"volts": {
@@ -1228,8 +1256,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 przekaźników",
"channelPath_noHopDetails": "Szczegóły dotyczące tego pakietu nie zostały podane.",
"channelPath_messageDetails": "Szczegóły wiadomości",
"channelPath_senderLabel": "Nadawca",
@@ -1237,7 +1265,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": {
@@ -1272,7 +1300,7 @@
}
},
"channelPath_unknownPath": "Nieznane",
- "channelPath_floodPath": "Powodzenie",
+ "channelPath_floodPath": "Zalew",
"channelPath_directPath": "Bezpośrednio",
"channelPath_observedZeroOf": "0 z {total} skoków",
"@channelPath_observedZeroOf": {
@@ -1294,7 +1322,7 @@
}
},
"channelPath_mapTitle": "Mapa ścieżek",
- "channelPath_noRepeaterLocations": "Brak dostępnych lokalizacji powtarzaczy dla tego ś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": {
@@ -1324,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",
@@ -1333,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",
@@ -1344,12 +1372,12 @@
}
}
},
- "repeater_neighbours": "Sąsiedzi",
- "repeater_neighboursSubtitle": "Wyświetl sąsiedztwo zerowych hopów.",
+ "repeater_neighbors": "Sąsiedzi",
+ "repeater_neighborsSubtitle": "Wyświetl sąsiadów zero-hop.",
"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_repeatersNeighbours": "Powtarzacze 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ł",
@@ -1473,7 +1501,9 @@
"community_deleteChannelsWarning": "Spowoduje to również usunięcie {count} kanału/kanałów i ich wiadomości.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Opuszczono społeczność \"{name}\"",
@@ -1484,5 +1514,602 @@
"community_regularHashtagDesc": "Publiczny hashtag (każdy może dołączyć)",
"community_communityHashtag": "Hashtag Społeczności",
"community_communityHashtagDesc": "Dostępne tylko dla członków społeczności",
- "community_forCommunity": "Dla {name}"
+ "community_forCommunity": "Dla {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "Zregeneruj",
+ "community_secretRegenerated": "Hasło ponownie wygenerowane dla \"{name}\"",
+ "community_regenerateSecret": "Zregeneruj sekret",
+ "community_regenerateSecretConfirm": "Regeneruj tajny klucz dla \"{name}\"? Wszyscy członkowie będą musieli zeskanować nowy kod QR, aby kontynuować komunikację.",
+ "community_scanToUpdateSecret": "Skanuj nowy kod QR, aby zaktualizować sekret dla \"{name}\"",
+ "community_secretUpdated": "Hasło zaktualizowane dla \"{name}\"",
+ "community_updateSecret": "Zaktualizuj tajny klucz",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Ty",
+ "pathTrace_failed": "Śledzenie ścieżki nie powiodło się.",
+ "pathTrace_notAvailable": "Ścieżka śledzenia niedostępna.",
+ "contacts_pathTrace": "Śledzenie Ścieżek",
+ "contacts_ping": "Pingować",
+ "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 przekaźnika",
+ "contacts_pathTraceTo": "Śledź trasę do {name}",
+ "contacts_chatTraceRoute": "Śledź trasę promienia",
+ "appSettings_languageRu": "Rosyjski",
+ "appSettings_languageUk": "Ukraińska",
+ "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": "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 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",
+ "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 przekaźniki / roomservery do GPX",
+ "settings_gpxExportRepeatersSubtitle": "Eksportuje przekaźniki / roomservery 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 przekaźników i roomserverów",
+ "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.",
+ "settings_gpxExportAllContacts": "Wszystkie lokalizacje kontaktów",
+ "settings_gpxExportNoContacts": "Brak kontaktów do wyeksportowania.",
+ "settings_gpxExportChat": "Lokalizacje towarzyszy",
+ "settings_gpxExportShareText": "Dane mapy wyeksportowane z meshcore-open",
+ "settings_gpxExportShareSubject": "Eksport danych mapy GPX meshcore-open",
+ "pathTrace_someHopsNoLocation": "Jeden lub więcej z chmieli nie ma określonej lokalizacji!",
+ "map_pathTraceCancelled": "Śledzenie ścieżki anulowano.",
+ "map_runTrace": "Uruchom ślad ścieżki",
+ "pathTrace_clearTooltip": "Wyczyść ścieżkę",
+ "map_removeLast": "Usuń ostatni",
+ "map_tapToAdd": "Kliknij na węzły, aby dodać je do ścieżki.",
+ "scanner_bluetoothOffMessage": "Prosimy włączyć Bluetooth, aby przeskanować urządzenia.",
+ "scanner_chromeRequired": "Wymagana przeglądarka Chrome",
+ "scanner_chromeRequiredMessage": "Ta aplikacja internetowa wymaga przeglądarki Google Chrome lub opartej na Chromium do obsługi Bluetooth.",
+ "scanner_bluetoothOff": "Bluetooth jest wyłączony",
+ "scanner_enableBluetooth": "Włącz Bluetooth",
+ "snrIndicator_lastSeen": "Ostatnio widziany",
+ "snrIndicator_nearByRepeaters": "Pobliskie przekaźniki",
+ "chat_ShowAllPaths": "Pokaż wszystkie ścieżki",
+ "settings_clientRepeatSubtitle": "Pozwól temu urządzeniu powtarzać pakiety danych dla innych urządzeń.",
+ "settings_clientRepeat": "Powtórzenie: Niezależne od sieci",
+ "settings_clientRepeatFreqWarning": "Powtórka poza siecią wymaga częstotliwości 433, 869 lub 918 MHz.",
+ "settings_aboutOpenMeteoAttribution": "Dane wysokościowe LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Jednostki",
+ "appSettings_unitsMetric": "Metryczne (m / km)",
+ "appSettings_unitsImperial": "Imperialne (ft / mi)",
+ "map_lineOfSight": "Linia wzroku",
+ "map_losScreenTitle": "Linia wzroku",
+ "losSelectStartEnd": "Wybierz węzły początkowe i końcowe dla LOS.",
+ "losRunFailed": "Sprawdzenie pola widzenia nie powiodło się: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Wyczyść wszystkie punkty",
+ "losRunToViewElevationProfile": "Uruchom LOS, aby wyświetlić profil wysokości",
+ "losMenuTitle": "Menu LOS",
+ "losMenuSubtitle": "Stuknij węzły lub naciśnij i przytrzymaj mapę, aby uzyskać niestandardowe punkty",
+ "losShowDisplayNodes": "Pokaż węzły wyświetlające",
+ "losCustomPoints": "Punkty niestandardowe",
+ "losCustomPointLabel": "Niestandardowe {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punkt A",
+ "losPointB": "Punkt B",
+ "losAntennaA": "Antena A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antena B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Uruchom LOS-a",
+ "losNoElevationData": "Brak danych o wysokości",
+ "losProfileClear": "{distance} {distanceUnit}, czysty LOS, minimalny prześwit {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, zablokowane przez {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: sprawdzam...",
+ "losStatusNoData": "LOS: brak danych",
+ "losStatusSummary": "LOS: {clear}/{total} jasne, {blocked} zablokowane, {unknown} nieznane",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Dane dotyczące wysokości są niedostępne dla jednej lub większej liczby próbek.",
+ "losErrorInvalidInput": "Nieprawidłowe dane punktów/wysokości do obliczenia LOS.",
+ "losRenameCustomPoint": "Zmień nazwę punktu niestandardowego",
+ "losPointName": "Nazwa punktu",
+ "losShowPanelTooltip": "Pokaż panel LOS",
+ "losHidePanelTooltip": "Ukryj panel LOS",
+ "losElevationAttribution": "Dane dotyczące wysokości: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Horyzont radiowy",
+ "losLegendLosBeam": "Linia widoczności",
+ "losLegendTerrain": "Teren",
+ "losFrequencyLabel": "Częstotliwość",
+ "losFrequencyInfoTooltip": "Zobacz szczegóły obliczenia",
+ "losFrequencyDialogTitle": "Obliczanie horyzontu radiowego",
+ "losFrequencyDialogDescription": "Zaczynając od k={baselineK} przy {baselineFreq} MHz, obliczenia korygują współczynnik k dla bieżącego pasma {frequencyMHz} MHz, które definiuje zakrzywiony limit horyzontu radiowego.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Usuń z ulubionych",
+ "listFilter_addToFavorites": "Dodaj do ulubionych",
+ "listFilter_favorites": "Ulubione",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Nieprzeczytane",
+ "contacts_searchContactsNoNumber": "Wyszukaj kontakty...",
+ "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} 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 przekaźników",
+ "contactsSettings_autoAddRepeatersSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie odkrytych przekaźników.",
+ "contactsSettings_autoAddRoomServersTitle": "Automatycznie dodaj roomservery",
+ "contactsSettings_autoAddUsersTitle": "Automatycznie dodaj użytkowników",
+ "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 roomserverów.",
+ "contactsSettings_autoAddSensorsTitle": "Automatycznie dodaj czujniki",
+ "discoveredContacts_searchHint": "Wyszukaj odkryte kontakty",
+ "discoveredContacts_contactAdded": "Kontakt dodany",
+ "discoveredContacts_addContact": "Dodaj kontakt",
+ "discoveredContacts_copyContact": "Kopiuj kontakt do schowka",
+ "contactsSettings_overwriteOldestTitle": "Nadpisz najstarszy",
+ "discoveredContacts_Title": "Odkryte Kontakty",
+ "contactsSettings_autoAddSensorsSubtitle": "Zezwól towarzyszowi na automatyczne dodawanie wykrytych czujników.",
+ "discoveredContacts_noMatching": "Brak pasujących kontaktów",
+ "discoveredContacts_deleteContact": "Usuń kontakt",
+ "contactsSettings_overwriteOldestSubtitle": "Gdy lista kontaktów jest pełna, najstarszy nieulubiony kontakt zostanie zastąpiony.",
+ "common_deleteAll": "Usuń wszystko",
+ "discoveredContacts_deleteContactAllContent": "Czy na pewno chcesz usunąć wszystkie znalezione kontakty?",
+ "discoveredContacts_deleteContactAll": "Usuń wszystkie odkryte kontakty",
+ "map_guessedLocation": "Przypuszczalna lokalizacja",
+ "map_showGuessedLocations": "Pokaż przypuszczalne lokalizacje węzłów",
+ "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",
+ "usbScreenStatus": "Wybierz urządzenie USB",
+ "usbScreenNote": "Port szeregowy USB jest aktywny na urządzeniach z systemem Android i platformach stacjonarnych, które go obsługują.",
+ "usbScreenEmptyState": "Nie znaleziono żadnych urządzeń USB. Podłącz jedno i zaktualizuj.",
+ "usbErrorPermissionDenied": "Zostało odrzucone żądanie dostępu przez USB.",
+ "usbErrorDeviceMissing": "Wybór urządzenia USB już nie jest dostępny.",
+ "usbErrorInvalidPort": "Wybierz prawidłowe urządzenie USB.",
+ "usbErrorBusy": "Kolejne żądanie połączenia przez USB jest już w trakcie realizacji.",
+ "usbErrorNotConnected": "Brak podłączonego urządzenia USB.",
+ "usbErrorOpenFailed": "Nie udało się otworzyć wybranego urządzenia USB.",
+ "usbErrorConnectFailed": "Nie udało się połączyć z wybranym urządzeniem USB.",
+ "usbErrorUnsupported": "Port szeregowy USB nie jest obsługiwany na tym urządzeniu.",
+ "usbErrorAlreadyActive": "Połączenie USB jest już aktywne.",
+ "usbErrorNoDeviceSelected": "Nie został wybrany żaden urządzenie USB.",
+ "usbErrorPortClosed": "Połączenie USB nie jest aktywne.",
+ "usbFallbackDeviceName": "Urządzenie do komunikacji przez sieć (seria)",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Wyszukiwanie urządzeń USB...",
+ "usbStatus_connecting": "Połączenie z urządzeniem USB...",
+ "usbStatus_notConnected": "Wybierz urządzenie USB",
+ "usbConnectionFailed": "Błąd połączenia USB: {error}",
+ "usbErrorConnectTimedOut": "Połączenie nie zostało nawiązane. Upewnij się, że urządzenie posiada oprogramowanie \"USB Companion\".",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpScreenTitle": "Połącz się za pomocą protokołu TCP",
+ "tcpHostLabel": "Adres IP",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Wprowadź adres URL i połącz",
+ "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 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",
+ "map_setAsMyLocation": "Ustaw jako moją lokalizację",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_allowByContact": "Zezwalaj według flag kontaktowych",
+ "settings_allowAll": "Zezwalaj na wszystko",
+ "settings_telemetryLocationMode": "Tryb położenia telemetrycznego",
+ "settings_telemetryEnvironmentMode": "Tryb środowiska telemetrycznego",
+ "settings_advertLocation": "Lokalizacja reklamowa",
+ "settings_advertLocationSubtitle": "Uwzględnij lokalizację w ogłoszeniu",
+ "settings_denyAll": "Odmów wszystkim",
+ "settings_privacySubtitle": "Kontroluj jakie informacje są udostępniane.",
+ "settings_privacy": "Ustawienia prywatności",
+ "settings_privacySettingsDescription": "Wybierz jakie informacje urządzenie udostępni innym.",
+ "contact_info": "Informacje kontaktowe",
+ "settings_telemetryBaseMode": "Tryb podstawowy telemetrii",
+ "contact_teleBase": "Baza telemetryczna",
+ "contact_teleLoc": "Lokalizacja telemetryczna",
+ "contact_teleLocSubtitle": "Zezwalaj na udostępnianie danych lokalizacji",
+ "contact_teleEnv": "Środowisko telemetryczne",
+ "contact_teleEnvSubtitle": "Zezwalaj na udostępnianie danych czujników środowiskowych",
+ "contact_telemetry": "Telemetryka",
+ "contact_clearChat": "Wyczyść czat",
+ "contact_settings": "Ustawienia kontaktowe",
+ "contact_lastSeen": "Ostatnio widziany",
+ "contact_teleBaseSubtitle": "Pozwól na udostępnianie poziomu naładowania baterii i podstawowych danych telemetrycznych",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "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",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Tryb telemetryczny zaktualizowany",
+ "settings_multiAck": "Wielokrotne ACK: {value}",
+ "map_showOverlaps": "Nakładające się klucze przekaźników",
+ "map_runTraceWithReturnPath": "Wróć tą samą ścieżką",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_languageHu": "Węgierski",
+ "appSettings_jumpToOldestUnreadSubtitle": "Przy otwieraniu czatu z nieodczytanymi wiadomościami, przewijaj, aby przejść do pierwszej nieodczytanej wiadomości, zamiast do najnowszej.",
+ "appSettings_jumpToOldestUnread": "Przejdź do najstarszego nieodczytanej wiadomości",
+ "chat_sendCooldown": "Prosimy o chwilowe oczekiwanie przed ponownym wysłaniem.",
+ "appSettings_languageJa": "Japoński",
+ "appSettings_languageKo": "Koreański",
+ "radioStats_tooltip": "Statystyki dotyczące radia i siatki",
+ "radioStats_screenTitle": "Statystyki radiowe",
+ "radioStats_notConnected": "Połącz się z urządzeniem, aby wyświetlić statystyki radiowe.",
+ "radioStats_firmwareTooOld": "Statystyki radiowe wymagają towarzyszącej oprogramowania w wersji 8 lub nowszej.",
+ "radioStats_waiting": "Czekam na dane…",
+ "radioStats_noiseFloor": "Poziom szumów: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Ostatni poziom RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Ostatni poziom SNR: {snr} dB",
+ "radioStats_txAir": "Czas emisji w stacji TX (całkowity): {seconds} s",
+ "radioStats_rxAir": "Czas wykorzystania kanału RX (całkowity): {seconds} s",
+ "radioStats_chartCaption": "Poziom szumów (dBm) w ostatnich próbkach.",
+ "radioStats_stripNoise": "Poziom szumów: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Pobieranie danych dotyczących radia…",
+ "radioStats_settingsTile": "Statystyki radiowe",
+ "radioStats_settingsSubtitle": "Szum tła, RSSI, SNR oraz czas dostępny",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Przekład przed wysłaniem",
+ "translation_title": "Tłumaczenie",
+ "translation_enableTitle": "Włącz tłumaczenie",
+ "translation_enableSubtitle": "Tłumaczenie otrzymywanych wiadomości oraz umożliwienie tłumaczenia przed wysłaniem.",
+ "translation_composerSubtitle": "Kontroluje domyślny stan ikony tłumaczenia w edytorze.",
+ "translation_targetLanguage": "Język docelowy",
+ "translation_useAppLanguage": "Użyj języka aplikacji",
+ "translation_downloadedModelLabel": "Pobudowany model",
+ "translation_presetModelLabel": "Wspólny model Hugging Face",
+ "translation_manualUrlLabel": "Adres URL do wersji manualnej",
+ "translation_downloadModel": "Pobierz model",
+ "translation_downloading": "Pobieranie...",
+ "translation_working": "Praca...",
+ "translation_stop": "Zatrzymaj się",
+ "translation_mergingChunks": "Scalanie pobranych fragmentów w jeden plik końcowy...",
+ "translation_downloadedModels": "Pobrane modele",
+ "translation_deleteModel": "Usuń model",
+ "translation_modelDownloaded": "Model tłumaczenia został pobrany.",
+ "translation_downloadStopped": "Pobieranie zakończone.",
+ "translation_downloadFailed": "Nie udało się pobrać: {error}",
+ "translation_enterUrlFirst": "Najpierw wprowadź adres URL modelu.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerEnabledHint": "Komunikaty zostaną przetłumaczone przed wysłaniem.",
+ "translation_translateBeforeSending": "Przekład przed wysłaniem",
+ "translation_composerDisabledHint": "Wysyłaj wiadomości w oryginalnym, wpisanym formacie.",
+ "translation_messageTranslation": "Tłumaczenie wiadomości",
+ "translation_translationOptions": "Opcje tłumaczenia",
+ "translation_systemLanguage": "Język systemu",
+ "translation_translateTo": "Tłumacz na {language}",
+ "scanner_linuxPairingShowPin": "Pokaż PIN",
+ "scanner_linuxPairingHidePin": "Ukryj PIN",
+ "scanner_linuxPairingPinPrompt": "Wprowadź kod PIN dla {deviceName} (pozostaw puste, jeśli brak).",
+ "scanner_linuxPairingPinTitle": "Kod PIN parowania Bluetooth",
+ "repeater_cliQuickClockSync": "Synchronizacja zegara",
+ "repeater_cliQuickDiscovery": "Odkryj Sąsiadów",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "Synchronizacja zegara po zalogowaniu",
+ "repeater_clockSyncAfterLoginSubtitle": "Automatycznie wysyłaj powiadomienie \"synchronizacja zegara\" po pomyślnym zalogowaniu.",
+ "chat_sendMessage": "Wyślij wiadomość",
+ "repeater_guestTools": "Narzędzia dla gości",
+ "repeater_guest": "Informacje dotyczące urządzenia powtarzającego",
+ "room_guest": "Informacje o serwerze"
}
diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb
index 34797be..bf3e893 100644
--- a/lib/l10n/app_pt.arb
+++ b/lib/l10n/app_pt.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Falha ao excluir o canal \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "pt",
"appTitle": "MeshCore Open",
"nav_contacts": "Contactos",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Número de Contatos",
"settings_infoChannelCount": "Número do Canal",
"settings_presets": "Presets",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frequência (MHz)",
"settings_frequencyHelper": "300,0 - 2500,0",
"settings_frequencyInvalid": "Frequência inválida (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX Potência (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Potência de TX inválida (0-22 dBm)",
- "settings_longRange": "Alcance Longo",
- "settings_fastSpeed": "Velocidade Rápida",
"settings_error": "Erro: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Novo Grupo",
"contacts_groupName": "Nome do grupo",
"contacts_groupNameRequired": "O nome do grupo é obrigatório.",
+ "contacts_groupNameReserved": "Este nome de grupo está reservado",
"contacts_groupAlreadyExists": "O grupo \"{name}\" já existe",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Canal público",
"channels_privateChannel": "Canal privado",
"channels_editChannel": "Editar canal",
+ "channels_muteChannel": "Silenciar canal",
+ "channels_unmuteChannel": "Ativar canal",
"channels_deleteChannel": "Excluir canal",
"channels_deleteChannelConfirm": "Excluir \"{name}\"? Não pode ser desfeito.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Abrir link?",
+ "chat_openLinkConfirmation": "Deseja abrir este link no seu navegador?",
+ "chat_open": "Abrir",
+ "chat_couldNotOpenLink": "Não foi possível abrir o link: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Formato de link inválido",
"map_title": "Mapa de Nós",
"map_noNodesWithLocation": "Não existem nós com dados de localização.",
"map_nodesNeedGps": "Os nós precisam partilhar as suas coordenadas GPS\npara aparecerem no mapa",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighbours": "Vizinhos",
+ "repeater_neighbors": "Vizinhos",
"neighbors_receivedData": "Dados dos Vizinhos Recebidos",
- "repeater_neighboursSubtitle": "Visualizar vizinhos de salto zero.",
+ "repeater_neighborsSubtitle": "Visualizar vizinhos de salto zero.",
"neighbors_requestTimedOut": "Vizinhos solicitam tempo limite esgotado.",
"neighbors_errorLoading": "Erro ao carregar vizinhos: {error}",
- "neighbors_repeatersNeighbours": "Repetidores Vizinhos",
+ "neighbors_repeatersNeighbors": "Repetidores Vizinhos",
"neighbors_noData": "Não estão disponíveis dados de vizinhos.",
"channels_createPrivateChannelDesc": "Protegido com uma chave secreta.",
"channels_joinPrivateChannelDesc": "Inserir uma chave secreta manualmente.",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Isso também excluirá {count} canal/canais e suas mensagens.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Saiu da comunidade \"{name}\"",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Hashtag público (qualquer pessoa pode participar)",
"community_communityHashtag": "Hashtag da Comunidade",
"community_communityHashtagDesc": "Apenas para membros da comunidade",
- "community_forCommunity": "Para {name}"
+ "community_forCommunity": "Para {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecretConfirm": "Regenerar a chave secreta para \"{name}\"? Todos os membros precisarão escanear o novo código QR para continuar a comunicação.",
+ "community_regenerateSecret": "Regenerar Senha Segura",
+ "community_secretRegenerated": "Senha secreta regenerada para \"{name}\"",
+ "community_regenerate": "Regenerar",
+ "community_secretUpdated": "Segredo atualizado para \"{name}\"",
+ "community_scanToUpdateSecret": "Scanar o novo código QR para atualizar o segredo para \"{name}\"\n\n\n+++++",
+ "community_updateSecret": "Atualizar Segredo",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Você",
+ "pathTrace_failed": "Falha no rastreamento de caminho.",
+ "pathTrace_notAvailable": "Traçado de caminho não disponível.",
+ "pathTrace_refreshTooltip": "Atualizar Path Trace.",
+ "contacts_pathTrace": "Traçado de Caminho",
+ "contacts_ping": "Pingar",
+ "contacts_repeaterPathTrace": "Traçar caminho para repetidor",
+ "contacts_repeaterPing": "Pingar repetidor",
+ "contacts_roomPathTrace": "Traçar caminho para o servidor da sala",
+ "contacts_roomPing": "Pingar servidor da sala",
+ "contacts_chatTraceRoute": "Rastrear rota do caminho",
+ "contacts_pathTraceTo": "Rastrear rota para {name}",
+ "contacts_invalidAdvertFormat": "Dados de Contato Inválidos",
+ "contacts_clipboardEmpty": "Área de Transferência Está Vazia.",
+ "appSettings_languageUk": "Ucraniano",
+ "contacts_contactImported": "Contato foi importado.",
+ "contacts_zeroHopAdvert": "Anúncio Zero Hop",
+ "contacts_copyAdvertToClipboard": "Copiar Anúncio para Área de Transferência",
+ "contacts_addContactFromClipboard": "Adicionar Contato da Área de Transferência",
+ "appSettings_languageRu": "Russo",
+ "appSettings_enableMessageTracing": "Ativar rastreamento de mensagens",
+ "appSettings_enableMessageTracingSubtitle": "Mostrar metadados detalhados de roteamento e tempo para as mensagens",
+ "contacts_ShareContact": "Copiar contato para Área de Transferência",
+ "contacts_contactImportFailed": "Contato falhou ao ser importado.",
+ "contacts_zeroHopContactAdvertSent": "Enviou contato por anúncio.",
+ "contacts_contactAdvertCopied": "Anúncio copiado para a Área de Transferência.",
+ "contacts_floodAdvert": "Anúncio de Inundação",
+ "contacts_contactAdvertCopyFailed": "Cópia do anúncio para a Área de Transferência falhou.",
+ "contacts_ShareContactZeroHop": "Compartilhar contato por anúncio",
+ "contacts_zeroHopContactAdvertFailed": "Falha ao enviar contato.",
+ "notification_activityTitle": "Atividade MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{mensagem} other{mensagens}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{mensagem de canal} other{mensagens de canal}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{novo nó} other{novos nós}}",
+ "notification_newTypeDiscovered": "Novo {contactType} descoberto",
+ "notification_receivedNewMessage": "Nova mensagem recebida",
+ "settings_gpxExportRepeaters": "Exportar repetidores / servidor de sala para GPX",
+ "settings_gpxExportRepeatersSubtitle": "Exporta repetidores / roomserver com localização para arquivo GPX.",
+ "settings_gpxExportSuccess": "Arquivo GPX exportado com sucesso.",
+ "settings_gpxExportAllSubtitle": "Exporta todos os contatos com uma localização para um arquivo GPX.",
+ "settings_gpxExportNotAvailable": "Não suportado no seu dispositivo/SO",
+ "settings_gpxExportError": "Ocorreu um erro ao exportar.",
+ "settings_gpxExportAll": "Exportar todos os contatos para GPX",
+ "settings_gpxExportContacts": "Exportar companheiros para GPX",
+ "settings_gpxExportContactsSubtitle": "Exporta companheiros com uma localização para um arquivo GPX.",
+ "settings_gpxExportRepeatersRoom": "Localizações do servidor de repetidor e sala",
+ "settings_gpxExportChat": "Localizações de companheiros",
+ "settings_gpxExportNoContacts": "Nenhum contato para exportar.",
+ "settings_gpxExportAllContacts": "Todos os locais de contatos",
+ "settings_gpxExportShareText": "Dados do mapa exportados do meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open exportação de dados de mapa GPX",
+ "pathTrace_someHopsNoLocation": "Um ou mais dos lúpulos estão sem localização!",
+ "map_runTrace": "Executar Traçado de Caminho",
+ "map_pathTraceCancelled": "Rastreamento de caminho cancelado.",
+ "pathTrace_clearTooltip": "Limpar caminho",
+ "map_removeLast": "Remover Último",
+ "map_tapToAdd": "Toque nos nós para adicioná-los ao caminho.",
+ "scanner_enableBluetooth": "Ative o Bluetooth",
+ "scanner_bluetoothOff": "Bluetooth está desativado",
+ "scanner_bluetoothOffMessage": "Por favor, ative o Bluetooth para escanear por dispositivos.",
+ "scanner_chromeRequired": "Navegador Chrome necessário",
+ "scanner_chromeRequiredMessage": "Esta aplicação web requer o Google Chrome ou um navegador baseado no Chromium para suporte de Bluetooth.",
+ "snrIndicator_nearByRepeaters": "Repetidores Próximos",
+ "snrIndicator_lastSeen": "Visto pela última vez",
+ "chat_ShowAllPaths": "Mostrar todos os caminhos",
+ "settings_clientRepeatFreqWarning": "A repetição fora da rede requer frequências de 433, 869 ou 918 MHz.",
+ "settings_clientRepeat": "Repetição sem rede",
+ "settings_clientRepeatSubtitle": "Permita que este dispositivo repita pacotes de rede para outros dispositivos.",
+ "settings_aboutOpenMeteoAttribution": "Dados de elevação LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Unidades",
+ "appSettings_unitsMetric": "Métrico (m/km)",
+ "appSettings_unitsImperial": "Imperial (ft/mi)",
+ "map_lineOfSight": "Linha de visão",
+ "map_losScreenTitle": "Linha de visão",
+ "losSelectStartEnd": "Selecione nós iniciais e finais para LOS.",
+ "losRunFailed": "Falha na verificação da linha de visão: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Limpe todos os pontos",
+ "losRunToViewElevationProfile": "Execute o LOS para visualizar o perfil de elevação",
+ "losMenuTitle": "Menu LOS",
+ "losMenuSubtitle": "Toque nos nós ou mantenha pressionado o mapa para obter pontos personalizados",
+ "losShowDisplayNodes": "Mostrar nós de exibição",
+ "losCustomPoints": "Pontos personalizados",
+ "losCustomPointLabel": "{index} personalizado",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Ponto A",
+ "losPointB": "Ponto B",
+ "losAntennaA": "Antena A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antena B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Executar LOS",
+ "losNoElevationData": "Sem dados de elevação",
+ "losProfileClear": "{distance} {distanceUnit}, limpar LOS, liberação mínima {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, bloqueado por {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: verificando...",
+ "losStatusNoData": "LOS: sem dados",
+ "losStatusSummary": "LOS: {clear}/{total} limpo, {blocked} bloqueado, {unknown} desconhecido",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Dados de elevação indisponíveis para uma ou mais amostras.",
+ "losErrorInvalidInput": "Dados de pontos/elevação inválidos para cálculo de LOS.",
+ "losRenameCustomPoint": "Renomear ponto personalizado",
+ "losPointName": "Nome do ponto",
+ "losShowPanelTooltip": "Mostrar painel LOS",
+ "losHidePanelTooltip": "Ocultar painel LOS",
+ "losElevationAttribution": "Dados de elevação: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Horizonte de rádio",
+ "losLegendLosBeam": "Linha de visada",
+ "losLegendTerrain": "Terreno",
+ "losFrequencyLabel": "Frequência",
+ "losFrequencyInfoTooltip": "Ver detalhes do cálculo",
+ "losFrequencyDialogTitle": "Cálculo do horizonte de rádio",
+ "losFrequencyDialogDescription": "Começando em k={baselineK} em {baselineFreq} MHz, o cálculo ajusta o fator k para a banda atual de {frequencyMHz} MHz, que define o limite do horizonte de rádio curvo.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_addToFavorites": "Adicionar aos favoritos",
+ "listFilter_removeFromFavorites": "Remover da lista de favoritos",
+ "listFilter_favorites": "Favoritos",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "Pesquisar {number}{str} Repetidores...",
+ "contacts_searchFavorites": "Pesquisar {number}{str} Favoritos...",
+ "contacts_searchUsers": "Pesquisar {number}{str} Usuários...",
+ "contacts_searchContactsNoNumber": "Pesquisar Contatos...",
+ "contacts_unread": "Não lido",
+ "contacts_searchRoomServers": "Pesquisar {number}{str} servidores de sala...",
+ "settings_contactSettings": "Configurações de Contato",
+ "contactsSettings_otherTitle": "Outras configurações relacionadas a contatos",
+ "contactsSettings_title": "Configurações de contatos",
+ "contactsSettings_autoAddTitle": "Descoberta Automática",
+ "settings_contactSettingsSubtitle": "Configurações para como os contatos são adicionados",
+ "contactsSettings_autoAddUsersTitle": "Adicionar usuários automaticamente",
+ "contactsSettings_autoAddRepeatersSubtitle": "Permitir que o companheiro adicione automaticamente os repetidores descobertos.",
+ "contactsSettings_autoAddRoomServersTitle": "Adicionar automaticamente servidores de sala",
+ "contactsSettings_overwriteOldestTitle": "Sobrescrever o Mais Antigo",
+ "contactsSettings_autoAddSensorsTitle": "Adicionar sensores automaticamente",
+ "discoveredContacts_Title": "Contatos Descobertos",
+ "contactsSettings_autoAddUsersSubtitle": "Permitir que o companheiro adicione automaticamente os usuários descobertos.",
+ "contactsSettings_autoAddRepeatersTitle": "Adicionar repetidores automaticamente",
+ "discoveredContacts_noMatching": "Nenhum contato correspondente",
+ "contactsSettings_autoAddRoomServersSubtitle": "Permitir que o companheiro adicione automaticamente os servidores de salas descobertos.",
+ "discoveredContacts_searchHint": "Pesquisar contatos descobertos",
+ "contactsSettings_autoAddSensorsSubtitle": "Permitir que o companheiro adicione automaticamente sensores descobertos.",
+ "discoveredContacts_copyContact": "Copiar Contato para a área de transferência",
+ "discoveredContacts_deleteContact": "Excluir Contato",
+ "discoveredContacts_contactAdded": "Contato adicionado",
+ "discoveredContacts_addContact": "Adicionar Contato",
+ "contactsSettings_overwriteOldestSubtitle": "Quando a lista de contatos estiver cheia, o contato mais antigo não favoritado será substituído.",
+ "common_deleteAll": "Excluir Tudo",
+ "discoveredContacts_deleteContactAll": "Excluir Todos os Contatos Descobertos",
+ "discoveredContacts_deleteContactAllContent": "Tem certeza de que deseja excluir todos os contatos descobertos?",
+ "map_guessedLocation": "Localização estimada",
+ "map_showGuessedLocations": "Mostrar as localizações dos nós estimados",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenTitle": "Conecte via USB",
+ "usbScreenSubtitle": "Selecione um dispositivo serial detectado e conecte-o diretamente ao seu nó MeshCore.",
+ "usbScreenStatus": "Selecione um dispositivo USB",
+ "usbScreenNote": "A comunicação serial via USB está ativa em dispositivos Android e plataformas de desktop compatíveis.",
+ "usbScreenEmptyState": "Nenhum dispositivo USB encontrado. Conecte um e atualize.",
+ "usbErrorPermissionDenied": "A permissão para acesso via USB foi negada.",
+ "usbErrorDeviceMissing": "O dispositivo USB selecionado não está mais disponível.",
+ "usbErrorInvalidPort": "Selecione um dispositivo USB válido.",
+ "usbErrorBusy": "Já existe uma solicitação de conexão USB em andamento.",
+ "usbErrorNotConnected": "Não há nenhum dispositivo USB conectado.",
+ "usbErrorOpenFailed": "Não foi possível abrir o dispositivo USB selecionado.",
+ "usbErrorConnectFailed": "Não foi possível conectar ao dispositivo USB selecionado.",
+ "usbErrorUnsupported": "A comunicação serial via USB não é suportada nesta plataforma.",
+ "usbErrorAlreadyActive": "A conexão USB já está ativa.",
+ "usbErrorNoDeviceSelected": "Nenhum dispositivo USB foi selecionado.",
+ "usbErrorPortClosed": "A conexão USB não está ativa.",
+ "usbFallbackDeviceName": "Dispositivo de Serial para a Web",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Procurando por dispositivos USB...",
+ "usbStatus_notConnected": "Selecione um dispositivo USB",
+ "usbConnectionFailed": "Falha na conexão USB: {error}",
+ "usbStatus_connecting": "Conectando ao dispositivo USB...",
+ "usbErrorConnectTimedOut": "A conexão expirou. Verifique se o dispositivo possui o firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostLabel": "Endereço IP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpScreenTitle": "Estabelecer conexão via TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpPortLabel": "Porta",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Insira o endereço final e conecte-se.",
+ "tcpStatus_connectingTo": "Conectando a {endpoint}...",
+ "tcpErrorHostRequired": "É necessário fornecer um endereço IP.",
+ "tcpErrorPortInvalid": "O valor do porto deve estar entre 1 e 65535.",
+ "tcpErrorUnsupported": "O protocolo TCP não é suportado nesta plataforma.",
+ "tcpErrorTimedOut": "A conexão TCP expirou.",
+ "tcpConnectionFailed": "Falha na conexão TCP: {error}",
+ "map_showDiscoveryContacts": "Mostrar Contatos de Descoberta",
+ "map_setAsMyLocation": "Defina minha localização",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacySettingsDescription": "Escolha quais informações o seu dispositivo compartilha com os outros.",
+ "settings_allowByContact": "Permitir por bandeiras de contato",
+ "settings_telemetryLocationMode": "Modo de Localização de Telemetria",
+ "settings_telemetryEnvironmentMode": "Modo de Ambiente de Telemetria",
+ "settings_advertLocation": "Localização do Anúncio",
+ "settings_advertLocationSubtitle": "Incluir localização no anúncio",
+ "settings_privacySubtitle": "Controle o que é compartilhado.",
+ "settings_denyAll": "Negar todos",
+ "settings_allowAll": "Permitir todos",
+ "settings_privacy": "Configurações de Privacidade",
+ "contact_info": "Informações de Contato",
+ "settings_telemetryBaseMode": "Modo Base de Telemetria",
+ "contact_teleBase": "Base de Telemetria",
+ "contact_teleLoc": "Localização de Telemetria",
+ "contact_teleLocSubtitle": "Permitir compartilhamento de dados de localização",
+ "contact_teleEnv": "Ambiente de Telemetria",
+ "contact_teleEnvSubtitle": "Permitir compartilhamento de dados do sensor de ambiente",
+ "contact_lastSeen": "Visto pela última vez",
+ "contact_clearChat": "Limpar Chat",
+ "contact_telemetry": "Telemetria",
+ "contact_settings": "Configurações de Contato",
+ "contact_teleBaseSubtitle": "Permitir compartilhamento do nível da bateria e telemetria básica",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeight": "Peso Inicial da Rota",
+ "appSettings_maxRouteWeight": "Peso Máximo da Rota",
+ "appSettings_maxRouteWeightSubtitle": "Peso máximo que um determinado percurso pode acumular com entregas bem-sucedidas.",
+ "appSettings_initialRouteWeightSubtitle": "Peso inicial para novos caminhos descobertos",
+ "appSettings_routeWeightSuccessIncrement": "Aumento do peso para indicar sucesso",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Peso adicionado a um caminho após a entrega bem-sucedida.",
+ "appSettings_routeWeightFailureDecrement": "Redução do peso da falha",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Peso removido de um caminho após uma tentativa de entrega malsucedida.",
+ "appSettings_maxMessageRetries": "Número máximo de tentativas de envio de mensagens",
+ "appSettings_maxMessageRetriesSubtitle": "Número de tentativas de reenvio antes de classificar uma mensagem como falha.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Modo de telemetria atualizado",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "map_showOverlaps": "Sobreposições da Chave Repeater",
+ "map_runTraceWithReturnPath": "Retornar ao mesmo caminho.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_jumpToOldestUnread": "Vá para a mensagem mais antiga não lida",
+ "chat_sendCooldown": "Por favor, aguarde um momento antes de reenviar.",
+ "appSettings_languageHu": "Húngaro",
+ "appSettings_jumpToOldestUnreadSubtitle": "Ao abrir uma conversa com mensagens não lidas, role para a primeira mensagem não lida, em vez da mais recente.",
+ "appSettings_languageJa": "Japonês",
+ "appSettings_languageKo": "Coreano",
+ "radioStats_tooltip": "Estatísticas de rádio e malha",
+ "radioStats_screenTitle": "Estatísticas de rádio",
+ "radioStats_notConnected": "Conecte-se a um dispositivo para visualizar estatísticas de rádio.",
+ "radioStats_firmwareTooOld": "As estatísticas de rádio exigem o firmware v8 ou uma versão mais recente.",
+ "radioStats_waiting": "Aguardando dados…",
+ "radioStats_noiseFloor": "Nível de ruído: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Último RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Último SNR: {snr} dB",
+ "radioStats_txAir": "Tempo de transmissão da TX (total): {seconds} s",
+ "radioStats_rxAir": "Tempo de uso do RX (total): {seconds} s",
+ "radioStats_chartCaption": "Nível de ruído (dBm) em amostras recentes.",
+ "radioStats_stripNoise": "Nível de ruído: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Obtendo estatísticas de rádio…",
+ "radioStats_settingsTile": "Estatísticas de rádio",
+ "radioStats_settingsSubtitle": "Nível de ruído, RSSI, SNR e tempo de transmissão",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Traduza antes de enviar",
+ "translation_enableSubtitle": "Traduzir mensagens recebidas e permitir a tradução antes do envio.",
+ "translation_enableTitle": "Ativar a tradução",
+ "translation_title": "Tradução",
+ "translation_composerSubtitle": "Controla o estado padrão do ícone de tradução do compositor.",
+ "translation_targetLanguage": "Língua-alvo",
+ "translation_useAppLanguage": "Utilize o idioma da aplicação",
+ "translation_downloadedModelLabel": "Modelo baixado",
+ "translation_presetModelLabel": "Modelo pré-definido da Hugging Face",
+ "translation_manualUrlLabel": "URL do modelo manual",
+ "translation_downloading": "Baixando...",
+ "translation_downloadModel": "Baixar modelo",
+ "translation_working": "Trabalhando...",
+ "translation_stop": "Pare",
+ "translation_mergingChunks": "Combinando os fragmentos baixados em um único arquivo...",
+ "translation_downloadedModels": "Modelos baixados",
+ "translation_deleteModel": "Excluir modelo",
+ "translation_modelDownloaded": "Modelo de tradução baixado.",
+ "translation_downloadStopped": "Download interrompido.",
+ "translation_downloadFailed": "Falha na descarga: {error}",
+ "translation_enterUrlFirst": "Insira primeiro a URL do modelo.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_messageTranslation": "Tradução da mensagem",
+ "translation_translateBeforeSending": "Traduzir antes de enviar",
+ "translation_composerEnabledHint": "As mensagens serão traduzidas antes de serem enviadas.",
+ "translation_composerDisabledHint": "Envie mensagens no idioma original, conforme digitado.",
+ "translation_translateTo": "Traduzir para {language}",
+ "translation_translationOptions": "Opções de tradução",
+ "translation_systemLanguage": "Idioma do sistema",
+ "scanner_linuxPairingShowPin": "Mostrar PIN",
+ "scanner_linuxPairingHidePin": "Ocultar PIN",
+ "scanner_linuxPairingPinPrompt": "Insira o PIN para {deviceName} (deixe em branco se não houver).",
+ "scanner_linuxPairingPinTitle": "PIN de emparelhamento Bluetooth",
+ "repeater_cliQuickClockSync": "Sincronização do Relógio",
+ "repeater_cliQuickDiscovery": "Descobrir Vizinhos",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Enviar automaticamente a sincronização do \"relógio\" após um login bem-sucedido.",
+ "repeater_clockSyncAfterLogin": "Sincronização do relógio após o login",
+ "room_guest": "Informações do Servidor",
+ "chat_sendMessage": "Enviar mensagem",
+ "repeater_guest": "Informações sobre repetidores",
+ "repeater_guestTools": "Ferramentas para hóspedes"
}
diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb
new file mode 100644
index 0000000..a83d139
--- /dev/null
+++ b/lib/l10n/app_ru.arb
@@ -0,0 +1,1317 @@
+{
+ "channels_channelDeleteFailed": "Не удалось удалить канал {name}.",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@@locale": "ru",
+ "appTitle": "MeshCore Open",
+ "nav_contacts": "Контакты",
+ "nav_channels": "Каналы",
+ "nav_map": "Карта",
+ "common_cancel": "Отмена",
+ "common_ok": "OK",
+ "common_connect": "Коннект",
+ "common_unknownDevice": "Неизвестное устройство",
+ "common_save": "Сохранить",
+ "common_delete": "Удалить",
+ "common_close": "Закрыть",
+ "common_edit": "Изменить",
+ "common_add": "Добавить",
+ "common_settings": "Настройки",
+ "common_disconnect": "Отключить",
+ "common_connected": "Подключено",
+ "common_disconnected": "Отключено",
+ "common_create": "Создать",
+ "common_continue": "Продолжить",
+ "common_share": "Поделиться",
+ "common_copy": "Копировать",
+ "common_retry": "Повторить",
+ "common_hide": "Скрыть",
+ "common_remove": "Убрать",
+ "common_enable": "Включить",
+ "common_disable": "Выключить",
+ "common_reboot": "Перезагрузить",
+ "common_loading": "Загрузка...",
+ "common_notAvailable": "—",
+ "common_voltageValue": "{volts} В",
+ "common_percentValue": "{percent}%",
+ "scanner_title": "MeshCore Open",
+ "scanner_scanning": "Поиск устройств...",
+ "scanner_connecting": "Подключение...",
+ "scanner_disconnecting": "Отключение...",
+ "scanner_notConnected": "Не подключено",
+ "scanner_connectedTo": "Подключено к {deviceName}",
+ "scanner_searchingDevices": "Поиск устройств MeshCore...",
+ "scanner_tapToScan": "Нажмите для поиска MeshCore устройств",
+ "scanner_connectionFailed": "Подключение не удалось: {error}",
+ "scanner_stop": "Стоп",
+ "scanner_scan": "Сканирование",
+ "device_quickSwitch": "Быстрое переключение",
+ "device_meshcore": "MeshCore",
+ "settings_title": "Настройки",
+ "settings_deviceInfo": "Информация об устройстве",
+ "settings_appSettings": "Настройки приложения",
+ "settings_appSettingsSubtitle": "Уведомления, сообщения и настройки карты",
+ "settings_nodeSettings": "Настройки ноды",
+ "settings_nodeName": "Имя ноды",
+ "settings_nodeNameNotSet": "Не установлено",
+ "settings_nodeNameHint": "Введите имя ноды",
+ "settings_nodeNameUpdated": "Имя обновлено",
+ "settings_radioSettings": "Настройки радио",
+ "settings_radioSettingsSubtitle": "Частота, мощность и коэффициент распространения",
+ "settings_radioSettingsUpdated": "Настройки радио обновлены",
+ "settings_location": "Позиция",
+ "settings_locationSubtitle": "Координаты GPS",
+ "settings_locationUpdated": "Позиция и настройки GPS обновлены",
+ "settings_locationBothRequired": "Введите широту и долготу.",
+ "settings_locationInvalid": "Неверная широта или долгота.",
+ "settings_locationGPSEnable": "Включить GPS",
+ "settings_locationGPSEnableSubtitle": "Включение GPS для автоматического обновления позиции.",
+ "settings_locationIntervalSec": "Интервал для позиционирования GPS (секунды)",
+ "settings_locationIntervalInvalid": "Интервал должен составлять не менее 60 секунд и не более 86400 секунд.",
+ "settings_latitude": "Широта",
+ "settings_longitude": "Долгота",
+ "settings_privacyMode": "Режим конфиденциальности",
+ "settings_privacyModeSubtitle": "Скрыть имя/позицию в анонсировании",
+ "settings_privacyModeToggle": "Включите режим конфиденциальности, чтобы скрыть свое имя и местоположение в анонсировании.",
+ "settings_privacyModeEnabled": "Режим конфиденциальности включен",
+ "settings_privacyModeDisabled": "Режим конфиденциальности выключен",
+ "settings_actions": "Действия",
+ "settings_sendAdvertisement": "Отправить анонсирование",
+ "settings_sendAdvertisementSubtitle": "Отправить анонсирование о присутствии сейчас",
+ "settings_advertisementSent": "Анонсирование отправлено",
+ "settings_syncTime": "Синхронизация времени",
+ "settings_syncTimeSubtitle": "Синхронизировать время с телефоном",
+ "settings_timeSynchronized": "Время синхронизировано",
+ "settings_refreshContacts": "Обновить контакты",
+ "settings_refreshContactsSubtitle": "Перезагрузить список контактов с устройства",
+ "settings_rebootDevice": "Перезагрузить устройство",
+ "settings_rebootDeviceSubtitle": "Перезапустить устройство MeshCore",
+ "settings_rebootDeviceConfirm": "Вы уверены, что хотите перезагрузить устройство? Вы будете отключены.",
+ "settings_debug": "Отладка",
+ "settings_bleDebugLog": "Журнал отладки BLE",
+ "settings_bleDebugLogSubtitle": "Команды BLE, ответы и сырые данные",
+ "settings_appDebugLog": "Журнал отладки приложения",
+ "settings_appDebugLogSubtitle": "Сообщения отладки приложения",
+ "settings_about": "О программе",
+ "settings_aboutVersion": "MeshCore Open v{version}",
+ "settings_aboutLegalese": "2026 MeshCore Open Source Project",
+ "settings_aboutDescription": "Открытое клиентское приложение на Flutter для устройств MeshCore с LoRa-сетями.",
+ "settings_infoName": "Имя",
+ "settings_infoId": "ID",
+ "settings_infoStatus": "Статус",
+ "settings_infoBattery": "Батарея",
+ "settings_infoPublicKey": "Публичный ключ",
+ "settings_infoContactsCount": "Количество контактов",
+ "settings_infoChannelCount": "Количество каналов",
+ "settings_presets": "Пресеты",
+ "settings_frequency": "Частота (МГц)",
+ "settings_frequencyHelper": "300.0 – 2500.0",
+ "settings_frequencyInvalid": "Недопустимая частота (300–2500 МГц)",
+ "settings_bandwidth": "Полоса пропускания",
+ "settings_spreadingFactor": "Коэффициент расширения",
+ "settings_codingRate": "Коэффициент кодирования",
+ "settings_txPower": "Мощность передачи (дБм)",
+ "settings_txPowerHelper": "0 – 22",
+ "settings_txPowerInvalid": "Недопустимая мощность передачи (0–22 дБм)",
+ "settings_error": "Ошибка: {message}",
+ "appSettings_title": "Настройки приложения",
+ "appSettings_appearance": "Внешний вид",
+ "appSettings_theme": "Тема",
+ "appSettings_themeSystem": "Как в системе",
+ "appSettings_themeLight": "Светлая",
+ "appSettings_themeDark": "Тёмная",
+ "appSettings_language": "Язык",
+ "appSettings_languageSystem": "Как в системе",
+ "appSettings_languageEn": "Английский",
+ "appSettings_languageFr": "Французский",
+ "appSettings_languageEs": "Испанский",
+ "appSettings_languageDe": "Немецкий",
+ "appSettings_languagePl": "Польский",
+ "appSettings_languageSl": "Словенский",
+ "appSettings_languagePt": "Португальский",
+ "appSettings_languageIt": "Итальянский",
+ "appSettings_languageZh": "Китайский",
+ "appSettings_languageSv": "Шведский",
+ "appSettings_languageNl": "Нидерландский",
+ "appSettings_languageSk": "Словацкий",
+ "appSettings_languageBg": "Болгарский",
+ "appSettings_languageRu": "Русский",
+ "appSettings_notifications": "Уведомления",
+ "appSettings_enableNotifications": "Включить уведомления",
+ "appSettings_enableNotificationsSubtitle": "Получать уведомления о сообщениях и оповещениях",
+ "appSettings_notificationPermissionDenied": "Разрешение на уведомления отклонено",
+ "appSettings_notificationsEnabled": "Уведомления включены",
+ "appSettings_notificationsDisabled": "Уведомления отключены",
+ "appSettings_messageNotifications": "Уведомления о сообщениях",
+ "appSettings_messageNotificationsSubtitle": "Показывать уведомление при получении новых сообщений",
+ "appSettings_channelMessageNotifications": "Уведомления о сообщениях в каналах",
+ "appSettings_channelMessageNotificationsSubtitle": "Показывать уведомление при получении сообщений в каналах",
+ "appSettings_advertisementNotifications": "Уведомления об анонсированиях",
+ "appSettings_advertisementNotificationsSubtitle": "Показывать уведомление при обнаружении новых нод",
+ "appSettings_messaging": "Обмен сообщениями",
+ "appSettings_clearPathOnMaxRetry": "Сбросить маршрут после максимального числа попыток",
+ "appSettings_clearPathOnMaxRetrySubtitle": "Сбросить маршрут контакта после 5 неудачных попыток отправки",
+ "appSettings_pathsWillBeCleared": "Маршруты будут сброшены после 5 неудачных попыток",
+ "appSettings_pathsWillNotBeCleared": "Маршруты не будут автоматически сбрасываться",
+ "appSettings_autoRouteRotation": "Автоматическое переключение маршрутов",
+ "appSettings_autoRouteRotationSubtitle": "Циклически переключаться между лучшими маршрутами и режимом рассылки",
+ "appSettings_autoRouteRotationEnabled": "Автоматическое переключение маршрутов включено",
+ "appSettings_autoRouteRotationDisabled": "Автоматическое переключение маршрутов отключено",
+ "appSettings_battery": "Батарея",
+ "appSettings_batteryChemistry": "Химия батареи",
+ "appSettings_batteryChemistryPerDevice": "Установить для устройства ({deviceName})",
+ "appSettings_batteryChemistryConnectFirst": "Подключитесь к устройству, чтобы выбрать",
+ "appSettings_batteryNmc": "18650 NMC (3.0–4.2 В)",
+ "appSettings_batteryLifepo4": "LiFePO4 (2.6–3.65 В)",
+ "appSettings_batteryLipo": "LiPo (3.0–4.2 В)",
+ "appSettings_mapDisplay": "Отображение карты",
+ "appSettings_showRepeaters": "Показывать репитеры",
+ "appSettings_showRepeatersSubtitle": "Отображать репитеры на карте",
+ "appSettings_showChatNodes": "Показывать чат-ноды",
+ "appSettings_showChatNodesSubtitle": "Отображать чат-ноды на карте",
+ "appSettings_showOtherNodes": "Показывать другие ноды",
+ "appSettings_showOtherNodesSubtitle": "Отображать другие типы нод на карте",
+ "appSettings_timeFilter": "Фильтр по времени",
+ "appSettings_timeFilterShowAll": "Показывать все ноды",
+ "appSettings_timeFilterShowLast": "Показывать ноды за последние {hours} ч",
+ "appSettings_mapTimeFilter": "Временной фильтр карты",
+ "appSettings_showNodesDiscoveredWithin": "Показывать ноды, обнаруженные за:",
+ "appSettings_allTime": "Всё время",
+ "appSettings_lastHour": "Последний час",
+ "appSettings_last6Hours": "Последние 6 часов",
+ "appSettings_last24Hours": "Последние 24 часа",
+ "appSettings_lastWeek": "Последнюю неделю",
+ "appSettings_offlineMapCache": "Кэш офлайн-карты",
+ "appSettings_noAreaSelected": "Область не выбрана",
+ "appSettings_areaSelectedZoom": "Область выбрана (масштаб {minZoom}–{maxZoom})",
+ "appSettings_debugCard": "Отладка",
+ "appSettings_appDebugLogging": "Журнал отладки приложения",
+ "appSettings_appDebugLoggingSubtitle": "Записывать отладочные сообщения приложения для диагностики",
+ "appSettings_appDebugLoggingEnabled": "Журнал отладки приложения включён",
+ "appSettings_appDebugLoggingDisabled": "Журнал отладки приложения отключён",
+ "contacts_title": "Контакты",
+ "contacts_noContacts": "Контактов пока нет",
+ "contacts_contactsWillAppear": "Контакты появятся, когда устройства начнут рассылать оповещения",
+ "contacts_searchContacts": "Поиск контактов...",
+ "contacts_noUnreadContacts": "Нет непрочитанных контактов",
+ "contacts_noContactsFound": "Контакты или группы не найдены",
+ "contacts_deleteContact": "Удалить контакт",
+ "contacts_removeConfirm": "Удалить {contactName} из контактов?",
+ "contacts_manageRepeater": "Управление репитером",
+ "contacts_manageRoom": "Управление сервером комнат",
+ "contacts_roomLogin": "Вход на сервер комнат",
+ "contacts_openChat": "Открыть чат",
+ "contacts_editGroup": "Изменить группу",
+ "contacts_deleteGroup": "Удалить группу",
+ "contacts_deleteGroupConfirm": "Удалить \"{groupName}\"?",
+ "contacts_newGroup": "Новая группа",
+ "contacts_groupName": "Имя группы",
+ "contacts_groupNameRequired": "Имя группы обязательно",
+ "contacts_groupNameReserved": "Это имя группы зарезервировано",
+ "contacts_groupAlreadyExists": "Группа \"{name}\" уже существует",
+ "contacts_filterContacts": "Фильтр контактов...",
+ "contacts_noContactsMatchFilter": "Нет контактов, соответствующих фильтру",
+ "contacts_noMembers": "Нет участников",
+ "contacts_lastSeenNow": "Видели только что",
+ "contacts_lastSeenMinsAgo": "Видели {minutes} мин назад",
+ "contacts_lastSeenHourAgo": "Видели 1 час назад",
+ "contacts_lastSeenHoursAgo": "Видели {hours} ч назад",
+ "contacts_lastSeenDayAgo": "Видели 1 день назад",
+ "contacts_lastSeenDaysAgo": "Видели {days} дн. назад",
+ "channels_title": "Каналы",
+ "channels_noChannelsConfigured": "Каналы не настроены",
+ "channels_addPublicChannel": "Добавить публичный канал",
+ "channels_searchChannels": "Поиск каналов...",
+ "channels_noChannelsFound": "Каналы не найдены",
+ "channels_channelIndex": "Канал {index}",
+ "channels_hashtagChannel": "Хэштег-канал",
+ "channels_public": "Публичный",
+ "channels_private": "Приватный",
+ "channels_publicChannel": "Публичный канал",
+ "channels_privateChannel": "Приватный канал",
+ "channels_editChannel": "Изменить канал",
+ "channels_muteChannel": "Отключить уведомления канала",
+ "channels_unmuteChannel": "Включить уведомления канала",
+ "channels_deleteChannel": "Удалить канал",
+ "channels_deleteChannelConfirm": "Удалить \"{name}\"? Это действие нельзя отменить.",
+ "channels_channelDeleted": "Канал \"{name}\" удалён",
+ "channels_addChannel": "Добавить канал",
+ "channels_channelIndexLabel": "Индекс канала",
+ "channels_channelName": "Имя канала",
+ "channels_usePublicChannel": "Использовать публичный канал",
+ "channels_standardPublicPsk": "Стандартный публичный PSK",
+ "channels_pskHex": "PSK (Hex)",
+ "channels_generateRandomPsk": "Сгенерировать случайный PSK",
+ "channels_enterChannelName": "Введите имя канала",
+ "channels_pskMustBe32Hex": "PSK должен содержать 32 шестнадцатеричных символа",
+ "channels_channelAdded": "Канал \"{name}\" добавлен",
+ "channels_editChannelTitle": "Изменить канал {index}",
+ "channels_smazCompression": "Сжатие SMAZ",
+ "channels_channelUpdated": "Канал \"{name}\" обновлён",
+ "channels_publicChannelAdded": "Публичный канал добавлен",
+ "channels_sortBy": "Сортировка",
+ "channels_sortManual": "Вручную",
+ "channels_sortAZ": "По алфавиту",
+ "channels_sortLatestMessages": "По последним сообщениям",
+ "channels_sortUnread": "По непрочитанным",
+ "channels_createPrivateChannel": "Создать приватный канал",
+ "channels_createPrivateChannelDesc": "Защищён секретным ключом.",
+ "channels_joinPrivateChannel": "Присоединиться к приватному каналу",
+ "channels_joinPrivateChannelDesc": "Введите секретный ключ вручную.",
+ "channels_joinPublicChannel": "Присоединиться к публичному каналу",
+ "channels_joinPublicChannelDesc": "К этому каналу может присоединиться любой.",
+ "channels_joinHashtagChannel": "Присоединиться к хэштег-каналу",
+ "channels_joinHashtagChannelDesc": "К хэштег-каналам может присоединиться любой.",
+ "channels_scanQrCode": "Сканировать QR-код",
+ "channels_scanQrCodeComingSoon": "Скоро будет",
+ "channels_enterHashtag": "Введите хэштег",
+ "channels_hashtagHint": "например, #команда",
+ "chat_noMessages": "Сообщений пока нет",
+ "chat_sendMessageToStart": "Отправьте сообщение, чтобы начать",
+ "chat_originalMessageNotFound": "Исходное сообщение не найдено",
+ "chat_replyingTo": "Ответ для {name}",
+ "chat_replyTo": "Ответить {name}",
+ "chat_location": "Местоположение",
+ "chat_sendMessageTo": "Отправить сообщение {contactName}",
+ "chat_typeMessage": "Напишите сообщение...",
+ "chat_messageTooLong": "Сообщение слишком длинное (макс. {maxBytes} байт).",
+ "chat_messageCopied": "Сообщение скопировано",
+ "chat_messageDeleted": "Сообщение удалено",
+ "chat_retryingMessage": "Повтор отправки сообщения",
+ "chat_retryCount": "Попытка {current}/{max}",
+ "chat_sendGif": "Отправить GIF",
+ "chat_reply": "Ответить",
+ "chat_addReaction": "Добавить реакцию",
+ "chat_me": "Я",
+ "emojiCategorySmileys": "Смайлы",
+ "emojiCategoryGestures": "Жесты",
+ "emojiCategoryHearts": "Сердечки",
+ "emojiCategoryObjects": "Предметы",
+ "gifPicker_title": "Выберите GIF",
+ "gifPicker_searchHint": "Поиск GIF...",
+ "gifPicker_poweredBy": "Работает на GIPHY",
+ "gifPicker_noGifsFound": "GIF не найдены",
+ "gifPicker_failedLoad": "Не удалось загрузить GIF",
+ "gifPicker_failedSearch": "Не удалось выполнить поиск GIF",
+ "gifPicker_noInternet": "Нет подключения к интернету",
+ "debugLog_appTitle": "Журнал отладки приложения",
+ "debugLog_bleTitle": "Журнал отладки BLE",
+ "debugLog_copyLog": "Копировать журнал",
+ "debugLog_clearLog": "Очистить журнал",
+ "debugLog_copied": "Журнал отладки скопирован",
+ "debugLog_bleCopied": "Журнал BLE скопирован",
+ "debugLog_noEntries": "Журнал отладки пока пуст",
+ "debugLog_enableInSettings": "Включите запись журнала отладки в настройках",
+ "debugLog_frames": "Фреймы",
+ "debugLog_rawLogRx": "Сырой журнал приёма",
+ "debugLog_noBleActivity": "Активность BLE пока отсутствует",
+ "debugFrame_length": "Длина фрейма: {count} байт",
+ "debugFrame_command": "Команда: 0x{value}",
+ "debugFrame_textMessageHeader": "Фрейм текстового сообщения:",
+ "debugFrame_destinationPubKey": "- Публичный ключ получателя: {pubKey}",
+ "debugFrame_timestamp": "- Временная метка: {timestamp}",
+ "debugFrame_flags": "- Флаги: 0x{value}",
+ "debugFrame_textType": "- Тип текста: {type} ({label})",
+ "debugFrame_textTypeCli": "CLI",
+ "debugFrame_textTypePlain": "Обычный",
+ "debugFrame_text": "- Текст: \"{text}\"",
+ "debugFrame_hexDump": "Шестнадцатеричный дамп:",
+ "chat_pathManagement": "Управление маршрутами",
+ "chat_routingMode": "Режим маршрутизации",
+ "chat_autoUseSavedPath": "Авто (использовать сохранённый маршрут)",
+ "chat_forceFloodMode": "Принудительный режим рассылки",
+ "chat_recentAckPaths": "Недавние подтверждённые маршруты (нажмите, чтобы использовать):",
+ "chat_pathHistoryFull": "История маршрутов заполнена. Удалите записи, чтобы добавить новые.",
+ "chat_hopSingular": "хоп",
+ "chat_hopPlural": "хопов",
+ "chat_hopsCount": "{count} {count, plural, one{хоп} few{хопа} many{хопов} other{хопов}}",
+ "chat_successes": "успешно",
+ "chat_removePath": "Удалить маршрут",
+ "chat_noPathHistoryYet": "История маршрутов пока пуста.\nОтправьте сообщение, чтобы обнаружить маршруты.",
+ "chat_pathActions": "Действия с маршрутом:",
+ "chat_setCustomPath": "Указать маршрут вручную",
+ "chat_setCustomPathSubtitle": "Вручную задать маршрут передачи",
+ "chat_clearPath": "Очистить маршрут",
+ "chat_clearPathSubtitle": "Принудительно обновить маршрут при следующей отправке",
+ "chat_pathCleared": "Маршрут очищен. Следующее сообщение обновит маршрут.",
+ "chat_floodModeSubtitle": "Используйте переключатель маршрутизации в панели приложения",
+ "chat_floodModeEnabled": "Режим рассылки включён. Отключите через значок маршрутизации в панели приложения.",
+ "chat_fullPath": "Полный маршрут",
+ "chat_pathDetailsNotAvailable": "Детали маршрута ещё недоступны. Попробуйте отправить сообщение для обновления.",
+ "chat_pathSetHops": "Маршрут установлен: {hopCount} {hopCount, plural, one{хоп} few{хопа} many{хопов} other{хопов}} — {status}",
+ "chat_pathSavedLocally": "Сохранено локально. Подключитесь для синхронизации.",
+ "chat_pathDeviceConfirmed": "Подтверждено устройством.",
+ "chat_pathDeviceNotConfirmed": "Ещё не подтверждено устройством.",
+ "chat_type": "Тип",
+ "chat_path": "Маршрут",
+ "chat_publicKey": "Публичный ключ",
+ "chat_compressOutgoingMessages": "Сжимать исходящие сообщения",
+ "chat_floodForced": "Рассылка (принудительно)",
+ "chat_directForced": "Прямой (принудительно)",
+ "chat_hopsForced": "{count} хоп(ов) (принудительно)",
+ "chat_floodAuto": "Рассылка (авто)",
+ "chat_direct": "Прямой",
+ "chat_poiShared": "Точка интереса отправлена",
+ "chat_unread": "Непрочитанных: {count}",
+ "map_title": "Карта нод",
+ "map_noNodesWithLocation": "Нет нод с данными о местоположении",
+ "map_nodesNeedGps": "Ноды должны передавать свои GPS-координаты, чтобы отображаться на карте",
+ "map_nodesCount": "Нод: {count}",
+ "map_pinsCount": "Меток: {count}",
+ "map_chat": "Чат",
+ "map_repeater": "Репитер",
+ "map_room": "Комната",
+ "map_sensor": "Сенсор",
+ "map_pinDm": "Метка (ЛС)",
+ "map_pinPrivate": "Метка (Приватная)",
+ "map_pinPublic": "Метка (Публичная)",
+ "map_lastSeen": "Последнее появление",
+ "map_disconnectConfirm": "Вы уверены, что хотите отключиться от этого устройства?",
+ "map_from": "От",
+ "map_source": "Источник",
+ "map_flags": "Флаги",
+ "map_shareMarkerHere": "Поделиться меткой здесь",
+ "map_pinLabel": "Метка",
+ "map_label": "Подпись",
+ "map_pointOfInterest": "Точка интереса",
+ "map_sendToContact": "Отправить контакту",
+ "map_sendToChannel": "Отправить в канал",
+ "map_noChannelsAvailable": "Нет доступных каналов",
+ "map_publicLocationShare": "Публичная передача местоположения",
+ "map_publicLocationShareConfirm": "Вы собираетесь поделиться местоположением в {channelLabel}. Этот канал публичный, и любой, у кого есть PSK, сможет его увидеть.",
+ "map_connectToShareMarkers": "Подключитесь к устройству, чтобы делиться метками",
+ "map_filterNodes": "Фильтр нод",
+ "map_nodeTypes": "Типы нод",
+ "map_chatNodes": "Чат-ноды",
+ "map_repeaters": "Репитеры",
+ "map_otherNodes": "Другие ноды",
+ "map_keyPrefix": "Префикс ключа",
+ "map_filterByKeyPrefix": "Фильтр по префиксу ключа",
+ "map_publicKeyPrefix": "Префикс публичного ключа",
+ "map_markers": "Метки",
+ "map_showSharedMarkers": "Показывать общие метки",
+ "map_lastSeenTime": "Время последнего появления",
+ "map_sharedPin": "Общая метка",
+ "map_joinRoom": "Присоединиться к комнате",
+ "map_manageRepeater": "Управление репитером",
+ "mapCache_title": "Кэш офлайн-карты",
+ "mapCache_selectAreaFirst": "Сначала выберите область для кэширования",
+ "mapCache_noTilesToDownload": "Нет плиток для загрузки в этой области",
+ "mapCache_downloadTilesTitle": "Загрузить плитки",
+ "mapCache_downloadTilesPrompt": "Загрузить {count} плиток для офлайн-использования?",
+ "mapCache_downloadAction": "Загрузить",
+ "mapCache_cachedTiles": "Закэшировано {count} плиток",
+ "mapCache_cachedTilesWithFailed": "Закэшировано {downloaded} плиток ({failed} не загружено)",
+ "mapCache_clearOfflineCacheTitle": "Очистить офлайн-кэш",
+ "mapCache_clearOfflineCachePrompt": "Удалить все закэшированные плитки карты?",
+ "mapCache_offlineCacheCleared": "Офлайн-кэш очищен",
+ "mapCache_noAreaSelected": "Область не выбрана",
+ "mapCache_cacheArea": "Область кэширования",
+ "mapCache_useCurrentView": "Использовать текущий вид",
+ "mapCache_zoomRange": "Диапазон масштаба",
+ "mapCache_estimatedTiles": "Оценочное количество плиток: {count}",
+ "mapCache_downloadedTiles": "Загружено {completed} из {total}",
+ "mapCache_downloadTilesButton": "Загрузить плитки",
+ "mapCache_clearCacheButton": "Очистить кэш",
+ "mapCache_failedDownloads": "Неудачных загрузок: {count}",
+ "mapCache_boundsLabel": "С {north}, Ю {south}, В {east}, З {west}",
+ "time_justNow": "Только что",
+ "time_minutesAgo": "{minutes} мин назад",
+ "time_hoursAgo": "{hours} ч назад",
+ "time_daysAgo": "{days} дн. назад",
+ "time_hour": "час",
+ "time_hours": "часов",
+ "time_day": "день",
+ "time_days": "дней",
+ "time_week": "неделя",
+ "time_weeks": "недель",
+ "time_month": "месяц",
+ "time_months": "месяцев",
+ "time_minutes": "минут",
+ "time_allTime": "Всё время",
+ "dialog_disconnect": "Отключиться",
+ "dialog_disconnectConfirm": "Вы уверены, что хотите отключиться от этого устройства?",
+ "login_repeaterLogin": "Вход в репитер",
+ "login_roomLogin": "Вход на сервер комнат",
+ "login_password": "Пароль",
+ "login_enterPassword": "Введите пароль",
+ "login_savePassword": "Сохранить пароль",
+ "login_savePasswordSubtitle": "Пароль будет надёжно сохранён на этом устройстве",
+ "login_repeaterDescription": "Введите пароль репитера для доступа к настройкам и статусу.",
+ "login_roomDescription": "Введите пароль комнаты для доступа к настройкам и статусу.",
+ "login_routing": "Маршрутизация",
+ "login_routingMode": "Режим маршрутизации",
+ "login_autoUseSavedPath": "Авто (использовать сохранённый маршрут)",
+ "login_forceFloodMode": "Принудительный режим рассылки",
+ "login_managePaths": "Управление маршрутами",
+ "login_login": "Войти",
+ "login_attempt": "Попытка {current}/{max}",
+ "login_failed": "Ошибка входа: {error}",
+ "login_failedMessage": "Не удалось войти. Либо пароль неверен, либо репитер недоступен.",
+ "common_reload": "Обновить",
+ "common_clear": "Очистить",
+ "path_currentPath": "Текущий маршрут: {path}",
+ "path_usingHopsPath": "Используется маршрут из {count} {count, plural, one{хоп} few{хопа} many{хопов} other{хопов}}",
+ "path_enterCustomPath": "Введите маршрут вручную",
+ "path_currentPathLabel": "Текущий маршрут",
+ "path_hexPrefixInstructions": "Введите 2-символьные шестнадцатеричные префиксы для каждого хопа, разделённые запятыми.",
+ "path_hexPrefixExample": "Пример: A1,F2,3C (каждый узел использует первый байт своего публичного ключа)",
+ "path_labelHexPrefixes": "Маршрут (шестнадцатеричные префиксы)",
+ "path_helperMaxHops": "Максимум 64 хопа. Каждый префикс — 2 шестнадцатеричных символа (1 байт)",
+ "path_selectFromContacts": "Или выберите из контактов:",
+ "path_noRepeatersFound": "Репитеры или серверы комнат не найдены.",
+ "path_customPathsRequire": "Пользовательские маршруты требуют промежуточных узлов, способных ретранслировать сообщения.",
+ "path_invalidHexPrefixes": "Недопустимые шестнадцатеричные префиксы: {prefixes}",
+ "path_tooLong": "Маршрут слишком длинный. Максимум 64 хопа.",
+ "path_setPath": "Установить маршрут",
+ "repeater_management": "Управление репитером",
+ "room_management": "Управление сервером комнат",
+ "repeater_managementTools": "Инструменты управления",
+ "repeater_status": "Статус",
+ "repeater_statusSubtitle": "Просмотр статуса, статистики и соседей репитера",
+ "repeater_telemetry": "Телеметрия",
+ "repeater_telemetrySubtitle": "Просмотр телеметрии датчиков и системной статистики",
+ "repeater_cli": "CLI",
+ "repeater_cliSubtitle": "Отправка команд репитеру",
+ "repeater_neighbors": "Соседи",
+ "repeater_neighborsSubtitle": "Просмотр соседей на нулевом хопе.",
+ "repeater_settings": "Настройки",
+ "repeater_settingsSubtitle": "Настройка параметров репитера",
+ "repeater_statusTitle": "Статус репитера",
+ "repeater_routingMode": "Режим маршрутизации",
+ "repeater_autoUseSavedPath": "Авто (использовать сохранённый маршрут)",
+ "repeater_forceFloodMode": "Принудительный режим рассылки",
+ "repeater_pathManagement": "Управление маршрутами",
+ "repeater_refresh": "Обновить",
+ "repeater_statusRequestTimeout": "Время ожидания статуса истекло.",
+ "repeater_errorLoadingStatus": "Ошибка загрузки статуса: {error}",
+ "repeater_systemInformation": "Системная информация",
+ "repeater_battery": "Батарея",
+ "repeater_clockAtLogin": "Время (при входе)",
+ "repeater_uptime": "Время работы",
+ "repeater_queueLength": "Длина очереди",
+ "repeater_debugFlags": "Флаги отладки",
+ "repeater_radioStatistics": "Радиостатистика",
+ "repeater_lastRssi": "Последний RSSI",
+ "repeater_lastSnr": "Последний SNR",
+ "repeater_noiseFloor": "Уровень шума",
+ "repeater_txAirtime": "Время эфира (передача)",
+ "repeater_rxAirtime": "Время эфира (приём)",
+ "repeater_packetStatistics": "Статистика пакетов",
+ "repeater_sent": "Отправлено",
+ "repeater_received": "Получено",
+ "repeater_duplicates": "Дубликаты",
+ "repeater_daysHoursMinsSecs": "{days} дн. {hours}ч {minutes}м {seconds}с",
+ "repeater_packetTxTotal": "Всего: {total}, Рассылка: {flood}, Прямые: {direct}",
+ "repeater_packetRxTotal": "Всего: {total}, Рассылка: {flood}, Прямые: {direct}",
+ "repeater_duplicatesFloodDirect": "Рассылка: {flood}, Прямые: {direct}",
+ "repeater_duplicatesTotal": "Всего: {total}",
+ "repeater_settingsTitle": "Настройки репитера",
+ "repeater_basicSettings": "Основные настройки",
+ "repeater_repeaterName": "Имя репитера",
+ "repeater_repeaterNameHelper": "Отображаемое имя этого репитера",
+ "repeater_adminPassword": "Пароль администратора",
+ "repeater_adminPasswordHelper": "Пароль с полным доступом",
+ "repeater_guestPassword": "Гостевой пароль",
+ "repeater_guestPasswordHelper": "Пароль для доступа только для чтения",
+ "repeater_radioSettings": "Настройки радио",
+ "repeater_frequencyMhz": "Частота (МГц)",
+ "repeater_frequencyHelper": "300–2500 МГц",
+ "repeater_txPower": "Мощность передачи",
+ "repeater_txPowerHelper": "1–30 дБм",
+ "repeater_bandwidth": "Полоса пропускания",
+ "repeater_spreadingFactor": "Коэффициент расширения",
+ "repeater_codingRate": "Коэффициент кодирования",
+ "repeater_locationSettings": "Настройки местоположения",
+ "repeater_latitude": "Широта",
+ "repeater_latitudeHelper": "В десятичных градусах (напр., 37.7749)",
+ "repeater_longitude": "Долгота",
+ "repeater_longitudeHelper": "В десятичных градусах (напр., -122.4194)",
+ "repeater_features": "Функции",
+ "repeater_packetForwarding": "Пересылка пакетов",
+ "repeater_packetForwardingSubtitle": "Разрешить репитеру пересылать пакеты",
+ "repeater_guestAccess": "Гостевой доступ",
+ "repeater_guestAccessSubtitle": "Разрешить гостевой доступ только для чтения",
+ "repeater_privacyMode": "Режим конфиденциальности",
+ "repeater_privacyModeSubtitle": "Скрывать имя/местоположение в оповещениях",
+ "repeater_advertisementSettings": "Настройки анонсирования",
+ "repeater_localAdvertInterval": "Интервал локальных анонсирований",
+ "repeater_localAdvertIntervalMinutes": "{minutes} минут",
+ "repeater_floodAdvertInterval": "Интервал анонсирований рассылкой (flood)",
+ "repeater_floodAdvertIntervalHours": "{hours} часов",
+ "repeater_encryptedAdvertInterval": "Интервал зашифрованных анонсирований",
+ "repeater_dangerZone": "Опасная зона",
+ "repeater_rebootRepeater": "Перезагрузить репитер",
+ "repeater_rebootRepeaterSubtitle": "Перезапустить устройство репитера",
+ "repeater_rebootRepeaterConfirm": "Вы уверены, что хотите перезагрузить этот репитер?",
+ "repeater_regenerateIdentityKey": "Пересоздать ключ идентификации",
+ "repeater_regenerateIdentityKeySubtitle": "Сгенерировать новую пару публичного/приватного ключей",
+ "repeater_regenerateIdentityKeyConfirm": "Это создаст новую идентичность для репитера. Продолжить?",
+ "repeater_eraseFileSystem": "Стереть файловую систему",
+ "repeater_eraseFileSystemSubtitle": "Отформатировать файловую систему репитера",
+ "repeater_eraseFileSystemConfirm": "ВНИМАНИЕ: это удалит все данные на репитере. Действие нельзя отменить!",
+ "repeater_eraseSerialOnly": "Очистка доступна только через последовательную консоль.",
+ "repeater_commandSent": "Команда отправлена: {command}",
+ "repeater_errorSendingCommand": "Ошибка отправки команды: {error}",
+ "repeater_confirm": "Подтвердить",
+ "repeater_settingsSaved": "Настройки успешно сохранены",
+ "repeater_errorSavingSettings": "Ошибка сохранения настроек: {error}",
+ "repeater_refreshBasicSettings": "Обновить основные настройки",
+ "repeater_refreshRadioSettings": "Обновить настройки радио",
+ "repeater_refreshTxPower": "Обновить мощность передачи",
+ "repeater_refreshLocationSettings": "Обновить настройки местоположения",
+ "repeater_refreshPacketForwarding": "Обновить пересылку пакетов",
+ "repeater_refreshGuestAccess": "Обновить гостевой доступ",
+ "repeater_refreshPrivacyMode": "Обновить режим конфиденциальности",
+ "repeater_refreshAdvertisementSettings": "Обновить настройки анонсирований",
+ "repeater_refreshed": "{label} обновлён",
+ "repeater_errorRefreshing": "Ошибка обновления {label}",
+ "repeater_cliTitle": "CLI репитера",
+ "repeater_debugNextCommand": "Отладка следующей команды",
+ "repeater_commandHelp": "Справка по командам",
+ "repeater_clearHistory": "Очистить историю",
+ "repeater_noCommandsSent": "Команды ещё не отправлялись",
+ "repeater_typeCommandOrUseQuick": "Введите команду ниже или используйте быстрые команды",
+ "repeater_enterCommandHint": "Введите команду...",
+ "repeater_previousCommand": "Предыдущая команда",
+ "repeater_nextCommand": "Следующая команда",
+ "repeater_enterCommandFirst": "Сначала введите команду",
+ "repeater_cliCommandFrameTitle": "Фрейм CLI-команды",
+ "repeater_cliCommandError": "Ошибка: {error}",
+ "repeater_cliQuickGetName": "Получить имя",
+ "repeater_cliQuickGetRadio": "Получить радио",
+ "repeater_cliQuickGetTx": "Получить TX",
+ "repeater_cliQuickNeighbors": "Соседи",
+ "repeater_cliQuickVersion": "Версия",
+ "repeater_cliQuickAdvertise": "Анонсировать",
+ "repeater_cliQuickClock": "Время",
+ "repeater_cliHelpAdvert": "Отправляет пакет анонсирования",
+ "repeater_cliHelpReboot": "Перезагружает устройство. (обычно вы получите «Тайм-аут» — это нормально)",
+ "repeater_cliHelpClock": "Показывает текущее время по часам устройства.",
+ "repeater_cliHelpPassword": "Устанавливает новый пароль администратора для устройства.",
+ "repeater_cliHelpVersion": "Показывает версию устройства и дату сборки прошивки.",
+ "repeater_cliHelpClearStats": "Сбрасывает различные счётчики статистики в ноль.",
+ "repeater_cliHelpSetAf": "Устанавливает коэффициент времени в эфире.",
+ "repeater_cliHelpSetTx": "Устанавливает мощность передачи LoRa в дБм. (требуется перезагрузка)",
+ "repeater_cliHelpSetRepeat": "Включает или отключает роль репитера для этой ноды.",
+ "repeater_cliHelpSetAllowReadOnly": "(Сервер комнат) Если «on», то вход без пароля разрешён, но публиковать в комнату нельзя (только чтение)",
+ "repeater_cliHelpSetFloodMax": "Устанавливает максимальное число хопов для входящих пакетов в режиме рассылки (если >= макс., пакет не пересылается)",
+ "repeater_cliHelpSetIntThresh": "Устанавливает порог интерференции (в дБ). По умолчанию 14. Установите 0, чтобы отключить обнаружение помех.",
+ "repeater_cliHelpSetAgcResetInterval": "Устанавливает интервал сброса автоматической регулировки усиления. Установите 0, чтобы отключить.",
+ "repeater_cliHelpSetMultiAcks": "Включает или отключает функцию «двойных ACK».",
+ "repeater_cliHelpSetAdvertInterval": "Устанавливает интервал (в минутах) отправки локального (нулевой хоп) анонсирования. Установите 0, чтобы отключить.",
+ "repeater_cliHelpSetFloodAdvertInterval": "Устанавливает интервал (в часах) отправки анонсирований рассылкой. Установите 0, чтобы отключить.",
+ "repeater_cliHelpSetGuestPassword": "Устанавливает/обновляет гостевой пароль. (для репитеров гости могут отправлять запрос «Get Stats»)",
+ "repeater_cliHelpSetName": "Устанавливает имя в оповещениях.",
+ "repeater_cliHelpSetLat": "Устанавливает широту для карты в оповещениях. (десятичные градусы)",
+ "repeater_cliHelpSetLon": "Устанавливает долготу для карты в оповещениях. (десятичные градусы)",
+ "repeater_cliHelpSetRadio": "Устанавливает полностью новые параметры радио и сохраняет их в настройки. Требуется команда «reboot» для применения.",
+ "repeater_cliHelpSetRxDelay": "Устанавливает (экспериментально) базовую задержку (>1 для эффекта) для принятых пакетов на основе качества сигнала. Установите 0, чтобы отключить.",
+ "repeater_cliHelpSetTxDelay": "Устанавливает множитель времени в эфире для пакета в режиме рассылки и применяет случайную задержку перед пересылкой (чтобы уменьшить коллизии).",
+ "repeater_cliHelpSetDirectTxDelay": "То же, что txdelay, но для случайной задержки пересылки пакетов в прямом режиме.",
+ "repeater_cliHelpSetBridgeEnabled": "Включить/выключить мост.",
+ "repeater_cliHelpSetBridgeDelay": "Установить задержку перед ретрансляцией пакетов.",
+ "repeater_cliHelpSetBridgeSource": "Выбрать, будет ли мост ретранслировать полученные или отправленные пакеты.",
+ "repeater_cliHelpSetBridgeBaud": "Установить скорость последовательного соединения для мостов RS232.",
+ "repeater_cliHelpSetBridgeSecret": "Установить секрет моста для мостов ESP-NOW.",
+ "repeater_cliHelpSetAdcMultiplier": "Устанавливает пользовательский коэффициент коррекции напряжения батареи (поддерживается только на некоторых платах).",
+ "repeater_cliHelpTempRadio": "Устанавливает временные параметры радио на заданное число минут, затем возвращает исходные. (НЕ сохраняется в настройки).",
+ "repeater_cliHelpSetPerm": "Изменяет ACL. Удаляет запись (по префиксу публичного ключа), если «permissions» равен нулю. Добавляет новую запись, если указан полный ключ и он отсутствует в ACL. Обновляет запись по совпадению префикса. Биты прав зависят от роли прошивки, но младшие 2 бита: 0 (Гость), 1 (Только чтение), 2 (Чтение/запись), 3 (Админ)",
+ "repeater_cliHelpGetBridgeType": "Получает тип моста: none, rs232, espnow",
+ "repeater_cliHelpLogStart": "Начинает запись пакетов в файловую систему.",
+ "repeater_cliHelpLogStop": "Останавливает запись пакетов в файловую систему.",
+ "repeater_cliHelpLogErase": "Удаляет журналы пакетов из файловой системы.",
+ "repeater_cliHelpNeighbors": "Показывает список других репитеров, услышанных через оповещения нулевого хопа. Каждая строка: префикс-id-в-hex:временная-метка:snr×4",
+ "repeater_cliHelpNeighborRemove": "Удаляет первую подходящую запись (по префиксу публичного ключа в hex) из списка соседей.",
+ "repeater_cliHelpRegion": "(только через последовательный порт) Показывает все определённые регионы и текущие права на рассылку.",
+ "repeater_cliHelpRegionLoad": "ПРИМЕЧАНИЕ: это специальная многострочная команда. Каждая следующая строка — имя региона (с отступом пробелами для указания иерархии, минимум один пробел). Завершается пустой строкой.",
+ "repeater_cliHelpRegionGet": "Ищет регион по префиксу имени (или «*» для глобальной области). Отвечает: «-> имя-региона (родитель) 'F'»",
+ "repeater_cliHelpRegionPut": "Добавляет или обновляет определение региона с заданным именем.",
+ "repeater_cliHelpRegionRemove": "Удаляет определение региона с заданным именем. (должно точно совпадать и не иметь дочерних регионов)",
+ "repeater_cliHelpRegionAllowf": "Разрешает рассылку («F»lood) для заданного региона. («*» для глобальной/устаревшей области)",
+ "repeater_cliHelpRegionDenyf": "Запрещает рассылку («F»lood) для заданного региона. (НЕ рекомендуется для глобальной области!)",
+ "repeater_cliHelpRegionHome": "Показывает текущий «домашний» регион. (Пока не используется, зарезервировано на будущее)",
+ "repeater_cliHelpRegionHomeSet": "Устанавливает «домашний» регион.",
+ "repeater_cliHelpRegionSave": "Сохраняет список/карту регионов в память.",
+ "repeater_cliHelpGps": "Показывает статус GPS. Если GPS выключен — отвечает только «off». Если включён — показывает статус, фиксацию, количество спутников.",
+ "repeater_cliHelpGpsOnOff": "Переключает состояние питания GPS.",
+ "repeater_cliHelpGpsSync": "Синхронизирует время ноды с часами GPS.",
+ "repeater_cliHelpGpsSetLoc": "Устанавливает позицию ноды по координатам GPS и сохраняет в настройки.",
+ "repeater_cliHelpGpsAdvert": "Показывает конфигурацию передачи местоположения в анонсированиях:\n- none: не включать местоположение\n- share: передавать GPS-координаты (из SensorManager)\n- prefs: передавать координаты из настроек",
+ "repeater_cliHelpGpsAdvertSet": "Устанавливает конфигурацию передачи местоположения.",
+ "repeater_commandsListTitle": "Список команд",
+ "repeater_commandsListNote": "ПРИМЕЧАНИЕ: для большинства команд «set ...» существуют соответствующие команды «get ...».",
+ "repeater_general": "Общие",
+ "repeater_settingsCategory": "Настройки",
+ "repeater_bridge": "Мост",
+ "repeater_logging": "Журналирование",
+ "repeater_neighborsRepeaterOnly": "Соседи (только для репитеров)",
+ "repeater_regionManagementRepeaterOnly": "Управление регионами (только для репитеров)",
+ "repeater_regionNote": "Команды регионов введены для управления определениями регионов и правами доступа.",
+ "repeater_gpsManagement": "Управление GPS",
+ "repeater_gpsNote": "Команда gps введена для управления параметрами, связанными с местоположением.",
+ "telemetry_receivedData": "Полученные телеметрические данные",
+ "telemetry_requestTimeout": "Время ожидания телеметрии истекло.",
+ "telemetry_errorLoading": "Ошибка загрузки телеметрии: {error}",
+ "telemetry_noData": "Данные телеметрии недоступны.",
+ "telemetry_channelTitle": "Канал {channel}",
+ "telemetry_batteryLabel": "Батарея",
+ "telemetry_voltageLabel": "Напряжение",
+ "telemetry_mcuTemperatureLabel": "Температура МК",
+ "telemetry_temperatureLabel": "Температура",
+ "telemetry_currentLabel": "Ток",
+ "telemetry_batteryValue": "{percent}% / {volts}В",
+ "telemetry_voltageValue": "{volts}В",
+ "telemetry_currentValue": "{amps}А",
+ "telemetry_temperatureValue": "{celsius}°C / {fahrenheit}°F",
+ "neighbors_receivedData": "Полученные данные о соседях",
+ "neighbors_requestTimedOut": "Время ожидания данных о соседях истекло.",
+ "neighbors_errorLoading": "Ошибка загрузки соседей: {error}",
+ "neighbors_repeatersNeighbors": "Соседи репитеров",
+ "neighbors_noData": "Данные о соседях недоступны.",
+ "neighbors_unknownContact": "Неизвестный {pubkey}",
+ "neighbors_heardA ago": "Слышали: {time} назад",
+ "channelPath_title": "Путь пакета",
+ "channelPath_viewMap": "Посмотреть на карте",
+ "channelPath_otherObservedPaths": "Другие наблюдаемые пути",
+ "channelPath_repeaterHops": "Хопы через репитеры",
+ "channelPath_noHopDetails": "Детали хопов для этого пакета не предоставлены.",
+ "channelPath_messageDetails": "Детали сообщения",
+ "channelPath_senderLabel": "Отправитель",
+ "channelPath_timeLabel": "Время",
+ "channelPath_repeatsLabel": "Повторы",
+ "channelPath_pathLabel": "Путь {index}",
+ "channelPath_observedLabel": "Наблюдаемый",
+ "channelPath_observedPathTitle": "Наблюдаемый путь {index} • {hops}",
+ "channelPath_noLocationData": "Нет данных о местоположении",
+ "channelPath_timeWithDate": "{day}/{month} {time}",
+ "channelPath_timeOnly": "{time}",
+ "channelPath_unknownPath": "Неизвестный",
+ "channelPath_floodPath": "Рассылка",
+ "channelPath_directPath": "Прямой",
+ "channelPath_observedZeroOf": "0 из {total} хопов",
+ "channelPath_observedSomeOf": "{observed} из {total} хопов",
+ "channelPath_mapTitle": "Карта пути",
+ "channelPath_noRepeaterLocations": "Нет данных о местоположении репитеров для этого пути.",
+ "channelPath_primaryPath": "Путь {index} (Основной)",
+ "channelPath_pathLabelTitle": "Путь",
+ "channelPath_observedPathHeader": "Наблюдаемый путь",
+ "channelPath_selectedPathLabel": "{label} • {prefixes}",
+ "channelPath_noHopDetailsAvailable": "Детали хопов для этого пакета недоступны.",
+ "channelPath_unknownRepeater": "Неизвестный репитер",
+ "community_title": "Сообщество",
+ "community_create": "Создать сообщество",
+ "community_createDesc": "Создать новое сообщество и поделиться через QR-код.",
+ "community_join": "Присоединиться",
+ "community_joinTitle": "Присоединиться к сообществу",
+ "community_joinConfirmation": "Вы хотите присоединиться к сообществу \"{name}\"?",
+ "community_scanQr": "Сканировать QR-код сообщества",
+ "community_scanInstructions": "Наведите камеру на QR-код сообщества",
+ "community_showQr": "Показать QR-код",
+ "community_publicChannel": "Публичный канал сообщества",
+ "community_hashtagChannel": "Хэштег-канал сообщества",
+ "community_name": "Имя сообщества",
+ "community_enterName": "Введите имя сообщества",
+ "community_created": "Сообщество \"{name}\" создано",
+ "community_joined": "Присоединились к сообществу \"{name}\"",
+ "community_qrTitle": "Поделиться сообществом",
+ "community_qrInstructions": "Отсканируйте этот QR-код, чтобы присоединиться к \"{name}\"",
+ "community_hashtagPrivacyHint": "Хэштег-каналы сообщества доступны только его участникам",
+ "community_invalidQrCode": "Недопустимый QR-код сообщества",
+ "community_alreadyMember": "Уже участник",
+ "community_alreadyMemberMessage": "Вы уже участник сообщества \"{name}\".",
+ "community_addPublicChannel": "Добавить публичный канал сообщества",
+ "community_addPublicChannelHint": "Автоматически добавить публичный канал для этого сообщества",
+ "community_noCommunities": "Вы ещё не присоединились ни к одному сообществу",
+ "community_scanOrCreate": "Отсканируйте QR-код или создайте сообщество, чтобы начать",
+ "community_manageCommunities": "Управление сообществами",
+ "community_delete": "Покинуть сообщество",
+ "community_deleteConfirm": "Покинуть \"{name}\"?",
+ "community_deleteChannelsWarning": "Это также удалит {count} канал(ов) и их сообщения.",
+ "community_deleted": "Покинули сообщество \"{name}\"",
+ "community_regenerateSecret": "Пересоздать секрет",
+ "community_regenerateSecretConfirm": "Пересоздать секретный ключ для \"{name}\"? Все участники должны будут отсканировать новый QR-код для продолжения общения.",
+ "community_regenerate": "Пересоздать",
+ "community_secretRegenerated": "Секрет пересоздан для \"{name}\"",
+ "community_updateSecret": "Обновить секрет",
+ "community_secretUpdated": "Секрет обновлён для \"{name}\"",
+ "community_scanToUpdateSecret": "Отсканируйте новый QR-код, чтобы обновить секрет для \"{name}\"",
+ "community_addHashtagChannel": "Добавить хэштег-канал сообщества",
+ "community_addHashtagChannelDesc": "Добавить хэштег-канал для этого сообщества",
+ "community_selectCommunity": "Выбрать сообщество",
+ "community_regularHashtag": "Обычный хэштег",
+ "community_regularHashtagDesc": "Публичный хэштег (любой может присоединиться)",
+ "community_communityHashtag": "Хэштег сообщества",
+ "community_communityHashtagDesc": "Доступен только участникам сообщества",
+ "community_forCommunity": "Для {name}",
+ "listFilter_tooltip": "Фильтр и сортировка",
+ "listFilter_sortBy": "Сортировка по",
+ "listFilter_latestMessages": "Последние сообщения",
+ "listFilter_heardRecently": "Слышали недавно",
+ "listFilter_az": "По алфавиту",
+ "listFilter_filters": "Фильтры",
+ "listFilter_all": "Все",
+ "listFilter_users": "Пользователи",
+ "listFilter_repeaters": "Репитеры",
+ "listFilter_roomServers": "Серверы комнат",
+ "listFilter_unreadOnly": "Только непрочитанные",
+ "listFilter_newGroup": "Новая группа",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_open": "Открыть",
+ "chat_couldNotOpenLink": "Не удалось открыть ссылку: {url}",
+ "chat_openLink": "Открыть ссылку?",
+ "chat_openLinkConfirmation": "Хотите открыть эту ссылку в вашем браузере?",
+ "neighbors_heardAgo": "Слушал(а): {time} назад",
+ "chat_invalidLink": "Неправильный формат ссылки",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Вы",
+ "pathTrace_failed": "Путь трассировки не выполнен.",
+ "pathTrace_notAvailable": "Трассировка пути недоступна.",
+ "pathTrace_refreshTooltip": "Обновить Path Trace",
+ "contacts_pathTrace": "Трассировка пути",
+ "contacts_ping": "Пинговать",
+ "contacts_repeaterPathTrace": "Отследить путь к ретранслятору",
+ "contacts_repeaterPing": "Пинговать повторитель",
+ "contacts_roomPathTrace": "Трассировка пути к серверу комнаты",
+ "contacts_roomPing": "Пинговать сервер комнаты",
+ "contacts_chatTraceRoute": "Трассировка маршрута",
+ "contacts_pathTraceTo": "Показать маршрут к {name}",
+ "contacts_contactImported": "Контакт был импортирован",
+ "contacts_contactImportFailed": "Контакт не удалось импортировать",
+ "contacts_invalidAdvertFormat": "Недействительные контактные данные",
+ "contacts_zeroHopAdvert": "Реклама Zero Hop",
+ "appSettings_languageUk": "Українська",
+ "appSettings_enableMessageTracing": "Включить трассировку сообщений",
+ "appSettings_enableMessageTracingSubtitle": "Показывать подробные метаданные о маршрутизации и времени для сообщений",
+ "contacts_floodAdvert": "Рекламный поток",
+ "contacts_clipboardEmpty": "Буфер обмена пуст.",
+ "contacts_copyAdvertToClipboard": "Копировать рекламу в буфер обмена",
+ "contacts_ShareContact": "Копировать контакт в буфер обмена",
+ "contacts_zeroHopContactAdvertFailed": "Не удалось отправить контакт.",
+ "contacts_contactAdvertCopied": "Реклама скопирована в буфер обмена.",
+ "contacts_contactAdvertCopyFailed": "Копирование рекламы в буфер обмена не удалось.",
+ "contacts_addContactFromClipboard": "Добавить контакт из буфера обмена",
+ "contacts_ShareContactZeroHop": "Поделиться контактом по объявлению",
+ "contacts_zeroHopContactAdvertSent": "Отправлено сообщение по объявлению.",
+ "notification_activityTitle": "Активность MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{сообщение} few{сообщения} many{сообщений} other{сообщений}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{сообщение канала} few{сообщения канала} many{сообщений канала} other{сообщений канала}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{новый узел} few{новых узла} many{новых узлов} other{новых узлов}}",
+ "notification_newTypeDiscovered": "Обнаружен новый {contactType}",
+ "notification_receivedNewMessage": "Получено новое сообщение",
+ "settings_gpxExportRepeaters": "Экспортировать рипитеры / сервер комнаты в GPX",
+ "settings_gpxExportRepeatersSubtitle": "Экспортирует ретрансляторы / сервер комнат с местоположением в файл GPX.",
+ "settings_gpxExportContacts": "Экспортировать спутников в GPX",
+ "settings_gpxExportNotAvailable": "Не поддерживается на вашем устройстве/ОС",
+ "settings_gpxExportError": "Произошла ошибка при экспорте.",
+ "settings_gpxExportRepeatersRoom": "Местоположения повторителей и серверов комнат",
+ "settings_gpxExportChat": "Местоположения спутников",
+ "settings_gpxExportContactsSubtitle": "Экспортирует спутников с местоположением в файл GPX.",
+ "settings_gpxExportAll": "Экспортировать все контакты в GPX",
+ "settings_gpxExportAllSubtitle": "Экспортирует все контакты с местоположением в файл GPX.",
+ "settings_gpxExportAllContacts": "Все местоположения контактов",
+ "settings_gpxExportSuccess": "Успешно экспортирован файл GPX.",
+ "settings_gpxExportNoContacts": "Нет контактов для экспорта.",
+ "settings_gpxExportShareText": "Данные карты экспортированы из meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open экспорт данных карты GPX",
+ "pathTrace_someHopsNoLocation": "Одному или нескольким хмелям не указано местоположение!",
+ "map_tapToAdd": "Нажимайте на узлы, чтобы добавить их в путь.",
+ "map_removeLast": "Удалить последний",
+ "map_pathTraceCancelled": "Отмена трассировки пути",
+ "pathTrace_clearTooltip": "Очистить путь",
+ "map_runTrace": "Запустить трассировку пути",
+ "scanner_enableBluetooth": "Включите Bluetooth",
+ "scanner_bluetoothOff": "Bluetooth выключен",
+ "scanner_bluetoothOffMessage": "Пожалуйста, включите Bluetooth, чтобы найти устройства.",
+ "scanner_chromeRequired": "Требуется браузер Chrome",
+ "scanner_chromeRequiredMessage": "Для поддержки Bluetooth в этом веб-приложении требуется Google Chrome или браузер на базе Chromium.",
+ "snrIndicator_nearByRepeaters": "Ближайшие ретрансляторы",
+ "snrIndicator_lastSeen": "Последний раз видели",
+ "chat_ShowAllPaths": "Показать все пути",
+ "settings_clientRepeatFreqWarning": "Для работы в режиме \"без подключения к сети\" требуется частота 433, 869 или 918 МГц.",
+ "settings_clientRepeatSubtitle": "Позвольте этому устройству повторять пакеты данных для других устройств.",
+ "settings_clientRepeat": "Повторение \"вне сети\"",
+ "settings_aboutOpenMeteoAttribution": "Данные о высоте LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Единицы",
+ "appSettings_unitsMetric": "Метрическая (м/км)",
+ "appSettings_unitsImperial": "Имперская (ft / mi)",
+ "map_lineOfSight": "Линия видимости",
+ "map_losScreenTitle": "Линия видимости",
+ "losSelectStartEnd": "Выберите начальный и конечный узлы для LOS.",
+ "losRunFailed": "Проверка прямой видимости не удалась: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Очистить все точки",
+ "losRunToViewElevationProfile": "Запустите LOS, чтобы просмотреть профиль высот.",
+ "losMenuTitle": "ЛОС Меню",
+ "losMenuSubtitle": "Коснитесь узлов или нажмите и удерживайте карту для выбора пользовательских точек.",
+ "losShowDisplayNodes": "Показать узлы отображения",
+ "losCustomPoints": "Пользовательские точки",
+ "losCustomPointLabel": "Пользовательский {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Точка А",
+ "losPointB": "Точка Б",
+ "losAntennaA": "Антенна А: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Антенна Б: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Запустить ЛОС",
+ "losNoElevationData": "Нет данных о высоте",
+ "losProfileClear": "{distance} {distanceUnit}, свободная зона видимости, минимальный зазор {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, заблокирован {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "ЛОС: проверяю...",
+ "losStatusNoData": "ЛОС: нет данных",
+ "losStatusSummary": "LOS: {clear}/{total} очищено, {blocked} заблокировано, {unknown} неизвестно.",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Данные о высоте недоступны для одного или нескольких образцов.",
+ "losErrorInvalidInput": "Неверные данные о точках/высоте для расчета LOS.",
+ "losRenameCustomPoint": "Переименовать пользовательскую точку",
+ "losPointName": "Имя точки",
+ "losShowPanelTooltip": "Показать панель LOS",
+ "losHidePanelTooltip": "Скрыть панель LOS",
+ "losElevationAttribution": "Данные о высоте: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Радиогоризонт",
+ "losLegendLosBeam": "Линия прямой видимости",
+ "losLegendTerrain": "Рельеф",
+ "losFrequencyLabel": "Частота",
+ "losFrequencyInfoTooltip": "Просмотреть детали расчёта",
+ "losFrequencyDialogTitle": "Расчёт радиогоризонта",
+ "losFrequencyDialogDescription": "Начиная с k={baselineK} на частоте {baselineFreq} МГц, расчет корректирует коэффициент k для текущего диапазона {frequencyMHz} МГц, который определяет изогнутую границу радиогоризонта.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_addToFavorites": "Добавить в избранное",
+ "listFilter_favorites": "Избранное",
+ "listFilter_removeFromFavorites": "Удалить из избранного",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRepeaters": "Поиск {number}{str} ретрансляторов...",
+ "contacts_searchContactsNoNumber": "Поиск контактов...",
+ "contacts_unread": "Непрочитанное",
+ "contacts_searchRoomServers": "Поиск {number}{str} серверов комнат...",
+ "contacts_searchFavorites": "Поиск {number}{str} избранного...",
+ "contacts_searchUsers": "Поиск {number}{str} пользователей...",
+ "settings_contactSettings": "Настройки контактов",
+ "settings_contactSettingsSubtitle": "Настройки добавления контактов",
+ "contactsSettings_autoAddTitle": "Автоматическое обнаружение",
+ "contactsSettings_title": "Настройки контактов",
+ "contactsSettings_otherTitle": "Другие настройки, связанные с контактами",
+ "contactsSettings_autoAddUsersSubtitle": "Разрешить компаньону автоматически добавлять обнаруженных пользователей",
+ "contactsSettings_autoAddRoomServersTitle": "Автоматически добавлять серверы комнат",
+ "contactsSettings_autoAddSensorsTitle": "Автоматически добавлять датчики",
+ "contactsSettings_autoAddSensorsSubtitle": "Разрешить компаньону автоматически добавлять обнаруженные датчики",
+ "contactsSettings_autoAddUsersTitle": "Автоматически добавлять пользователей",
+ "contactsSettings_overwriteOldestTitle": "Перезаписать самое старое",
+ "contactsSettings_autoAddRepeatersTitle": "Автоматически добавлять ретрансляторы",
+ "contactsSettings_autoAddRepeatersSubtitle": "Разрешить спутнику автоматически добавлять обнаруженные ретрансляторы",
+ "contactsSettings_autoAddRoomServersSubtitle": "Разрешить компаньону автоматически добавлять обнаруженные сервера комнат.",
+ "discoveredContacts_noMatching": "Нет совпадающих контактов",
+ "discoveredContacts_searchHint": "Найденные контакты поиска",
+ "discoveredContacts_contactAdded": "Контакт добавлен",
+ "discoveredContacts_copyContact": "Копировать контакт в буфер обмена",
+ "discoveredContacts_addContact": "Добавить контакт",
+ "discoveredContacts_Title": "Обнаруженные контакты",
+ "discoveredContacts_deleteContact": "Удалить контакт",
+ "contactsSettings_overwriteOldestSubtitle": "Когда список контактов заполнен, будет заменен самый старый контакт, который не находится в избранном.",
+ "common_deleteAll": "Удалить все",
+ "discoveredContacts_deleteContactAllContent": "Вы уверены, что хотите удалить все обнаруженные контакты?",
+ "discoveredContacts_deleteContactAll": "Удалить Все Обнаруженные Контакты",
+ "map_guessedLocation": "Угаданное место",
+ "map_showGuessedLocations": "Отобразить предполагаемые места расположения узлов",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenSubtitle": "Выберите обнаруженное устройство с последовательным интерфейсом и подключите его напрямую к вашему узлу MeshCore.",
+ "usbScreenTitle": "Подключение через USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenStatus": "Выберите USB-устройство",
+ "usbScreenNote": "USB-серийный порт активен на поддерживаемых устройствах Android и на настольных платформах.",
+ "usbScreenEmptyState": "Не обнаружено устройств USB. Подключите одно из них и обновите список.",
+ "usbErrorPermissionDenied": "Запрос на доступ через USB был отклонен.",
+ "usbErrorDeviceMissing": "Выбранное USB-устройство больше недоступно.",
+ "usbErrorInvalidPort": "Выберите действительное USB-устройство.",
+ "usbErrorBusy": "Еще одно запрошенное соединение через USB уже находится в процессе.",
+ "usbErrorNotConnected": "Ни одно USB-устройство не подключено.",
+ "usbErrorOpenFailed": "Не удалось открыть выбранное USB-устройство.",
+ "usbErrorConnectFailed": "Не удалось установить соединение с выбранным USB-устройством.",
+ "usbErrorUnsupported": "Поддержка последовательного USB отсутствует на данной платформе.",
+ "usbErrorAlreadyActive": "USB-соединение уже установлено.",
+ "usbErrorNoDeviceSelected": "Не было выбрано ни одно устройство USB.",
+ "usbErrorPortClosed": "USB-соединение не установлено.",
+ "usbFallbackDeviceName": "Устройство для последовательного подключения к сети",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Поиск USB-устройств...",
+ "usbStatus_connecting": "Подключение к USB-устройству...",
+ "usbConnectionFailed": "Не удалось установить соединение через USB: {error}",
+ "usbStatus_notConnected": "Выберите USB-устройство",
+ "usbErrorConnectTimedOut": "Соединение не установлено. Убедитесь, что устройство имеет установленное программное обеспечение USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostHint": "192.168.40.10",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostLabel": "IP-адрес",
+ "tcpScreenTitle": "Установить соединение по протоколу TCP",
+ "tcpPortLabel": "Порт",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Введите адрес и подключитесь.",
+ "tcpStatus_connectingTo": "Подключение к {endpoint}...",
+ "tcpErrorHostRequired": "Необходимо указать IP-адрес.",
+ "tcpErrorPortInvalid": "Порт должен находиться в диапазоне от 1 до 65535.",
+ "tcpErrorUnsupported": "Протокол TCP не поддерживается на этой платформе.",
+ "tcpErrorTimedOut": "Соединение TCP не удалось установить.",
+ "tcpConnectionFailed": "Не удалось установить соединение TCP: {error}",
+ "map_showDiscoveryContacts": "Показать контакты Discovery",
+ "map_setAsMyLocation": "Установить мое местоположение",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Настройки конфиденциальности",
+ "settings_privacySubtitle": "Контролируйте, какую информацию делиться.",
+ "settings_telemetryLocationMode": "Режим местоположения телеметрии",
+ "settings_telemetryEnvironmentMode": "Режим среды телеметрии",
+ "settings_advertLocation": "Местоположение рекламы",
+ "settings_advertLocationSubtitle": "Включить местоположение в объявление",
+ "settings_allowAll": "Разрешить все",
+ "settings_privacySettingsDescription": "Выберите, какую информацию ваше устройство будет делиться с другими.",
+ "settings_denyAll": "Отклонить все",
+ "settings_allowByContact": "Разрешить по флагам контактов",
+ "contact_info": "Контактная информация",
+ "settings_telemetryBaseMode": "Базовый режим телеметрии",
+ "contact_teleBase": "База телеметрии",
+ "contact_teleLoc": "Местоположение телеметрии",
+ "contact_teleLocSubtitle": "Разрешить обмен данными о местоположении",
+ "contact_teleEnv": "Среда телеметрии",
+ "contact_teleEnvSubtitle": "Разрешить обмен данными датчиков окружающей среды",
+ "contact_settings": "Настройки контактов",
+ "contact_telemetry": "Телеметрия",
+ "contact_clearChat": "Очистить чат",
+ "contact_lastSeen": "Последний раз видели",
+ "contact_teleBaseSubtitle": "Разрешить обмен уровнем заряда батареи и базовой телеметрией",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeight": "Максимальный допустимый вес маршрута",
+ "appSettings_maxRouteWeightSubtitle": "Максимальный вес, который может быть перевезён по определённому маршруту при успешных доставках.",
+ "appSettings_initialRouteWeightSubtitle": "Начальный вес для новых, только что открытых маршрутов",
+ "appSettings_initialRouteWeight": "Начальный вес маршрута",
+ "appSettings_routeWeightSuccessIncrement": "Увеличение веса успеха",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Вес, добавленный к маршруту после успешной доставки.",
+ "appSettings_routeWeightFailureDecrement": "Уменьшение веса неудачи",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Вес, который был удален с пути после неудачной доставки.",
+ "appSettings_maxMessageRetries": "Максимальное количество повторных попыток отправки сообщения",
+ "appSettings_maxMessageRetriesSubtitle": "Количество попыток повторной отправки сообщения перед тем, как пометить его как неудачное.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Режим телеметрии обновлен",
+ "settings_multiAck": "Мульти-ACK: {value}",
+ "map_showOverlaps": "Перекрытия ключа повтора",
+ "map_runTraceWithReturnPath": "Вернуться обратно по тому же пути",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Пожалуйста, подождите немного, прежде чем отправлять сообщение снова.",
+ "appSettings_jumpToOldestUnread": "Перейти к самому старому непрочитанному сообщению",
+ "appSettings_languageHu": "Венгерский",
+ "appSettings_jumpToOldestUnreadSubtitle": "При открытии чата с непрочитанными сообщениями, прокрутите страницу, чтобы увидеть первое непрочитанное сообщение, а не последнее.",
+ "appSettings_languageJa": "Японский",
+ "appSettings_languageKo": "Корейский",
+ "radioStats_tooltip": "Статистика радио и беспроводной сети",
+ "radioStats_screenTitle": "Статистика радиовещания",
+ "radioStats_notConnected": "Подключитесь к устройству, чтобы просмотреть статистику радио.",
+ "radioStats_firmwareTooOld": "Для работы радиостатистики требуется установленная версия прошивки v8 или более новая.",
+ "radioStats_waiting": "Ожидаем данных…",
+ "radioStats_noiseFloor": "Уровень шума: {noiseDbm} дБм",
+ "radioStats_lastRssi": "Последнее значение RSSI: {rssiDbm} дБм",
+ "radioStats_lastSnr": "Последнее значение SNR: {snr} дБ",
+ "radioStats_txAir": "Время эфира на телеканале TX (общее): {seconds} секунд",
+ "radioStats_rxAir": "Общее время использования RX (в секундах): {seconds} с",
+ "radioStats_chartCaption": "Уровень шума (дБм) на основе последних измерений.",
+ "radioStats_stripNoise": "Уровень шума: {noiseDbm} дБм",
+ "radioStats_stripWaiting": "Получение данных о радио…",
+ "radioStats_settingsTile": "Статистика радиовещания",
+ "radioStats_settingsSubtitle": "Уровень шума, RSSI, SNR и время передачи",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "Переводить входящие сообщения и позволять предварительный перевод перед отправкой.",
+ "translation_composerTitle": "Переводить перед отправкой",
+ "translation_title": "Перевод",
+ "translation_enableTitle": "Включить перевод",
+ "translation_composerSubtitle": "Управляет исходным состоянием значка перевода, предоставляемого редактором.",
+ "translation_targetLanguage": "Целевой язык",
+ "translation_useAppLanguage": "Используйте язык приложения",
+ "translation_downloadedModelLabel": "Загруженная модель",
+ "translation_presetModelLabel": "Предопределенная модель от Hugging Face",
+ "translation_manualUrlLabel": "Ссылка на руководство",
+ "translation_downloadModel": "Скачать модель",
+ "translation_downloading": "Загрузка...",
+ "translation_stop": "Прекратите",
+ "translation_working": "Работа...",
+ "translation_mergingChunks": "Объединение скачанных фрагментов в один финальный файл...",
+ "translation_downloadedModels": "Загруженные модели",
+ "translation_deleteModel": "Удалить модель",
+ "translation_modelDownloaded": "Модель перевода загружена.",
+ "translation_downloadStopped": "Процесс загрузки был прерван.",
+ "translation_downloadFailed": "Не удалось скачать: {error}",
+ "translation_enterUrlFirst": "Сначала введите URL модели.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_translateBeforeSending": "Перевести перед отправкой",
+ "translation_composerEnabledHint": "Сообщения будут переведены перед отправкой.",
+ "translation_messageTranslation": "Перевод сообщения",
+ "translation_composerDisabledHint": "Отправляйте сообщения на языке, в котором они были изначально набраны.",
+ "translation_translateTo": "Перевести на {language}",
+ "translation_translationOptions": "Варианты перевода",
+ "translation_systemLanguage": "Язык системы",
+ "scanner_linuxPairingShowPin": "Показать PIN",
+ "scanner_linuxPairingPinPrompt": "Введите PIN‑код для {deviceName} (оставьте пустым, если нет).",
+ "scanner_linuxPairingHidePin": "Скрыть PIN",
+ "scanner_linuxPairingPinTitle": "PIN‑код сопряжения Bluetooth",
+ "repeater_cliQuickDiscovery": "Обнаружить Соседей",
+ "repeater_cliQuickClockSync": "Синхронизация часов",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "Синхронизация часов после входа в систему",
+ "repeater_clockSyncAfterLoginSubtitle": "Автоматически отправлять сообщение \"синхронизация времени\" после успешной авторизации.",
+ "chat_sendMessage": "Отправить сообщение",
+ "repeater_guest": "Информация о ретрансляторе",
+ "room_guest": "Информация о сервере",
+ "repeater_guestTools": "Инструменты для гостей"
+}
diff --git a/lib/l10n/app_sk.arb b/lib/l10n/app_sk.arb
index d6ea7d8..e4466c3 100644
--- a/lib/l10n/app_sk.arb
+++ b/lib/l10n/app_sk.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Kanál \"{name}\" sa nepodarilo odstrániť",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "sk",
"appTitle": "MeshCore Open",
"nav_contacts": "Kontakty",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Počet kontaktov",
"settings_infoChannelCount": "Počet kanálov",
"settings_presets": "Prednastavenia",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frekvencia (MHz)",
"settings_frequencyHelper": "300,0 – 2500,0",
"settings_frequencyInvalid": "Neplatná frekvencia (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX Výkon (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Neplatná hodnota výkonu TX (0-22 dBm)",
- "settings_longRange": "Dlhý dosah",
- "settings_fastSpeed": "Rýchla rýchlosť",
"settings_error": "Chyba: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Nová skupina",
"contacts_groupName": "Názov skupiny",
"contacts_groupNameRequired": "Skupina musí mať názov.",
+ "contacts_groupNameReserved": "Tento názov skupiny je rezervovaný",
"contacts_groupAlreadyExists": "Skupina \"{name}\" už existuje",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Veľké verejne kanály",
"channels_privateChannel": "Osobné kanál",
"channels_editChannel": "Upraviť kanál",
+ "channels_muteChannel": "Stlmiť kanál",
+ "channels_unmuteChannel": "Zrušiť stlmenie kanála",
"channels_deleteChannel": "Odstrániť kanál",
"channels_deleteChannelConfirm": "Odstrániť \"{name}\"? To sa nedá zrušiť.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Otvoriť odkaz?",
+ "chat_openLinkConfirmation": "Chcete otvoriť tento odkaz v prehliadači?",
+ "chat_open": "Otvoriť",
+ "chat_couldNotOpenLink": "Nepodarilo sa otvoriť odkaz: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Neplatný formát odkazu",
"map_title": "Mapa uzlov",
"map_noNodesWithLocation": "Žiadne uzly s údajmi o polohe",
"map_nodesNeedGps": "Uholníky musia zdieľať svoje GPS súradnice, aby sa zobrazili na mape.",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighboursSubtitle": "Zobraziť susedné body bez skokov.",
+ "repeater_neighborsSubtitle": "Zobraziť susedné body bez skokov.",
"neighbors_requestTimedOut": "Súďia žiadajú o časové ukončenie.",
"neighbors_receivedData": "Obdielo dáta suseda",
- "repeater_neighbours": "Súsezný",
+ "repeater_neighbors": "Súsezný",
"neighbors_errorLoading": "Chyba pri načítaní susedov: {error}",
- "neighbors_repeatersNeighbours": "Opakovadlá Súsezná",
+ "neighbors_repeatersNeighbors": "Opakovadlá Súsezná",
"neighbors_noData": "Nie je dostupná žiadna informácia o susedoch.",
"channels_createPrivateChannel": "Vytvorte súkromný kanál",
"channels_joinPrivateChannel": "Pripojiť sa k súkromnému kanálu",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Tým sa tiež vymaže {count} kanál/kanálov a ich správy.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Opustená komunita \"{name}\"",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Veľký hashtag (ktočokoľvek sa môže pridať)",
"community_communityHashtag": "Komunitný Hashtag",
"community_communityHashtagDesc": "Špecifické pre členov komunity",
- "community_forCommunity": "Pre {name}"
+ "community_forCommunity": "Pre {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_secretRegenerated": "Záznam pre \"{name}\" bol regenerovaný tajne",
+ "community_regenerateSecretConfirm": "Znovu vygenerovať tajný kľúč pre \"{name}\"? Všetci členovia budú musieť skanovať nový QR kód, aby mohli nadviazať komunikáciu.",
+ "community_regenerate": "Znovu vygenerovať",
+ "community_regenerateSecret": "Zobraziť nový tajný kód",
+ "community_scanToUpdateSecret": "Skáňte nový QR kód na aktualizáciu tajného hesla pre \"{name}\"",
+ "community_updateSecret": "Aktualizovať tajné heslo",
+ "community_secretUpdated": "Zmena tajnej slova pre \"{name}\"",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Vy",
+ "pathTrace_failed": "Sledovanie cesty zlyhalo.",
+ "pathTrace_notAvailable": "Path trace nie je k dispozícii.",
+ "pathTrace_refreshTooltip": "Obnoviť Path Trace.",
+ "contacts_pathTrace": "Sledovanie lúčov",
+ "contacts_ping": "Pingovať",
+ "contacts_repeaterPathTrace": "Sledovanie cesty k opakovaču",
+ "contacts_repeaterPing": "Pingovať opakovač",
+ "contacts_roomPathTrace": "Sledovanie cesty k serveru miestnosti",
+ "contacts_roomPing": "Ping server miestnosti",
+ "contacts_chatTraceRoute": "Sledovať trasu lúča",
+ "contacts_pathTraceTo": "Sledovať trasu k {name}",
+ "contacts_clipboardEmpty": "Schránka je prázdna.",
+ "appSettings_languageUk": "Ukrajinská",
+ "contacts_contactImportFailed": "Kontakt sa nepodarilo importovať.",
+ "contacts_zeroHopAdvert": "Inzerát Zero Hop",
+ "contacts_floodAdvert": "Inzerát povodní",
+ "contacts_copyAdvertToClipboard": "Kopírovať reklamu do schránky",
+ "contacts_invalidAdvertFormat": "Neplatné kontaktné údaje",
+ "appSettings_languageRu": "Ruština",
+ "appSettings_enableMessageTracing": "Povoliť sledovanie správ",
+ "appSettings_enableMessageTracingSubtitle": "Zobraziť podrobné metadáta o smerovaní a časovaní správ",
+ "contacts_addContactFromClipboard": "Pridať kontakt z schránky",
+ "contacts_contactImported": "Kontakt bol importovaný.",
+ "contacts_zeroHopContactAdvertSent": "Poslal kontakt cez inzerát.",
+ "contacts_contactAdvertCopied": "Inzerát bol skopírovaný do schránky.",
+ "contacts_contactAdvertCopyFailed": "Kopírovanie inzerátu do schránky zlyhalo.",
+ "contacts_zeroHopContactAdvertFailed": "Zlyhalo odoslanie kontaktu.",
+ "contacts_ShareContactZeroHop": "Zdieľať kontakt cez inzerát",
+ "contacts_ShareContact": "Kopírovať kontakt do schránky",
+ "notification_activityTitle": "Aktivita MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{správa} few{správy} other{správ}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{správa kanálu} few{správy kanálu} other{správ kanálu}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{nový uzol} few{nové uzly} other{nových uzlov}}",
+ "notification_newTypeDiscovered": "Nový {contactType} objavený",
+ "notification_receivedNewMessage": "Prijatá nová správa",
+ "settings_gpxExportRepeatersSubtitle": "Exportuje repeater / roomserver s lokalitou do súboru GPX.",
+ "settings_gpxExportContacts": "Export sprievodcov do GPX",
+ "settings_gpxExportSuccess": "Úspešne exportovaný súbor GPX.",
+ "settings_gpxExportNoContacts": "Žiadne kontakty na export.",
+ "settings_gpxExportNotAvailable": "Nie je podporované na vašom zariadení/operáciomnom systéme",
+ "settings_gpxExportRepeatersRoom": "Umiestnenia opakovačov a serverov miestností",
+ "settings_gpxExportError": "Vyskytol sa chyba počas exportu.",
+ "settings_gpxExportAllSubtitle": "Exportuje všetky kontakty s lokalitou do súboru GPX.",
+ "settings_gpxExportContactsSubtitle": "Exportuje sprievodcov s umiestnením do súboru GPX.",
+ "settings_gpxExportRepeaters": "Exportovať repeater / server miestnosti do GPX",
+ "settings_gpxExportAll": "Exportovať všetky kontakty do GPX",
+ "settings_gpxExportAllContacts": "Všetky kontaktné lokality",
+ "settings_gpxExportChat": "Lokácie sprievodcov",
+ "settings_gpxExportShareText": "Mapové údaje exportované z meshcore-open",
+ "settings_gpxExportShareSubject": "meshcore-open export dát GPX mapových údajov",
+ "pathTrace_someHopsNoLocation": "Jedna alebo viac chmeľov chýba lokalita!",
+ "pathTrace_clearTooltip": "Zmazať cestu",
+ "map_tapToAdd": "Kliknite na uzly, aby ste ich pridali k ceste.",
+ "map_removeLast": "Odstrániť posledný",
+ "map_runTrace": "Spustiť trasovaním cesty",
+ "map_pathTraceCancelled": "Zrušenie stopáže cesty bolo zrušené.",
+ "scanner_bluetoothOffMessage": "Prosím, zapnite Bluetooth, aby ste mohli skenovať pre zariadenia.",
+ "scanner_chromeRequired": "Vyžaduje sa prehliadač Chrome",
+ "scanner_chromeRequiredMessage": "Táto webová aplikácia vyžaduje Google Chrome alebo prehliadač založený na Chromium pre podporu Bluetooth.",
+ "scanner_bluetoothOff": "Bluetooth je vypnutý",
+ "scanner_enableBluetooth": "Povolte Bluetooth",
+ "snrIndicator_lastSeen": "Naposledy videný",
+ "snrIndicator_nearByRepeaters": "Miestne opakovače",
+ "chat_ShowAllPaths": "Zobraziť všetky cesty",
+ "settings_clientRepeat": "Opätovné použitie bez elektrickej siete",
+ "settings_clientRepeatFreqWarning": "Použitie off-grid systému vyžaduje frekvencie 433, 869 alebo 918 MHz.",
+ "settings_clientRepeatSubtitle": "Umožnite, aby toto zariadenie opakovávalo siete pre ostatných.",
+ "settings_aboutOpenMeteoAttribution": "Údaje o nadmorskej výške LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Jednotky",
+ "appSettings_unitsMetric": "Metrické (m / km)",
+ "appSettings_unitsImperial": "Imperiálne (ft / mi)",
+ "map_lineOfSight": "Line of Sight",
+ "map_losScreenTitle": "Line of Sight",
+ "losSelectStartEnd": "Vyberte počiatočný a koncový uzol pre LOS.",
+ "losRunFailed": "Kontrola priamej viditeľnosti zlyhala: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Vymazať všetky body",
+ "losRunToViewElevationProfile": "Ak chcete zobraziť výškový profil, spustite LOS",
+ "losMenuTitle": "Menu LOS",
+ "losMenuSubtitle": "Klepnutím na uzly alebo dlhým stlačením mapy získate vlastné body",
+ "losShowDisplayNodes": "Zobraziť uzly zobrazenia",
+ "losCustomPoints": "Vlastné body",
+ "losCustomPointLabel": "Vlastné {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Bod A",
+ "losPointB": "Bod B",
+ "losAntennaA": "Anténa A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Anténa B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Spustite LOS",
+ "losNoElevationData": "Žiadne údaje o nadmorskej výške",
+ "losProfileClear": "{distance} {distanceUnit}, vymazať LOS, min. vôľa {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blokovaný {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: kontrolujem...",
+ "losStatusNoData": "LOS: žiadne údaje",
+ "losStatusSummary": "LOS: {clear}/{total} vymazané, {blocked} blokované, {unknown} neznáme",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Údaje o nadmorskej výške nie sú k dispozícii pre jednu alebo viacero vzoriek.",
+ "losErrorInvalidInput": "Neplatné body/údaje o nadmorskej výške pre výpočet LOS.",
+ "losRenameCustomPoint": "Premenovať vlastný bod",
+ "losPointName": "Názov bodu",
+ "losShowPanelTooltip": "Zobraziť panel LOS",
+ "losHidePanelTooltip": "Skryť panel LOS",
+ "losElevationAttribution": "Údaje o nadmorskej výške: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Rádiový horizont",
+ "losLegendLosBeam": "Priama viditeľnosť",
+ "losLegendTerrain": "Terén",
+ "losFrequencyLabel": "Frekvencia",
+ "losFrequencyInfoTooltip": "Zobraziť podrobnosti výpočtu",
+ "losFrequencyDialogTitle": "Výpočet rádiového horizontu",
+ "losFrequencyDialogDescription": "Počnúc od k={baselineK} pri {baselineFreq} MHz výpočet upraví k-faktor pre aktuálne pásmo {frequencyMHz} MHz, ktorý definuje zakrivený strop rádiového horizontu.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Odstrániť z označení",
+ "listFilter_addToFavorites": "Pridaj do obľúbených",
+ "listFilter_favorites": "Obľúbené",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "Hľadaj {number}{str} serverov miestností...",
+ "contacts_searchFavorites": "Hľadať {number}{str} obľúbené...",
+ "contacts_searchRepeaters": "Hľadať {number}{str} opakovače...",
+ "contacts_searchUsers": "Hľadať {number}{str} používateľov...",
+ "contacts_searchContactsNoNumber": "Hľadať kontakty...",
+ "contacts_unread": "Neprečítané",
+ "settings_contactSettingsSubtitle": "Nastavenia pre pridávanie kontaktov.",
+ "contactsSettings_autoAddUsersTitle": "Automaticky pridávať užívateľov",
+ "contactsSettings_autoAddUsersSubtitle": "Povoliť spoločníkovi automaticky pridávať objavených užívateľov.",
+ "contactsSettings_autoAddRepeatersTitle": "Automaticky pridávať opakovače",
+ "contactsSettings_autoAddRoomServersTitle": "Automaticky pridávať server miestnosti",
+ "contactsSettings_autoAddRoomServersSubtitle": "Povoliť spoločníkovi automaticky pridať objavené serverové miestnosti.",
+ "contactsSettings_autoAddTitle": "Automatické zisťovanie",
+ "contactsSettings_title": "Nastavenia kontaktov",
+ "contactsSettings_otherTitle": "Ďalšie nastavenia súvisiace s kontaktami",
+ "settings_contactSettings": "Nastavenia kontaktov",
+ "contactsSettings_autoAddSensorsTitle": "Automaticky pridávať senzory",
+ "discoveredContacts_noMatching": "Žiadne zhodné kontakty",
+ "discoveredContacts_searchHint": "Vyhľadať objavené kontakty",
+ "contactsSettings_autoAddRepeatersSubtitle": "Povoliť spoločníkovi automaticky pridávať objavené repeater.",
+ "discoveredContacts_contactAdded": "Kontakt bol pridaný",
+ "discoveredContacts_copyContact": "Kopírovať kontakt do schránky",
+ "discoveredContacts_deleteContact": "Zmazať kontakt",
+ "contactsSettings_autoAddSensorsSubtitle": "Povoliť spoločníkovi automaticky pridávať objavené senzory.",
+ "discoveredContacts_Title": "Objavené kontakty",
+ "contactsSettings_overwriteOldestTitle": "Prepísať najstaršie",
+ "discoveredContacts_addContact": "Pridať kontakt",
+ "contactsSettings_overwriteOldestSubtitle": "Keď je zoznam kontaktov plný, bude nahradený najstarší neoznačený kontakt.",
+ "discoveredContacts_deleteContactAll": "Zmazať všetky objavené kontakty",
+ "common_deleteAll": "Zmazať všetko",
+ "discoveredContacts_deleteContactAllContent": "Ste si istí, že chcete zmazať všetky objavené kontakty?",
+ "map_showGuessedLocations": "Zobraziť umiestnenia odhadnutých uzlov",
+ "map_guessedLocation": "Odhadnutá lokalita",
+ "usbScreenTitle": "Pripojte cez USB",
+ "usbScreenSubtitle": "Vyberte detekovaný sériový zariadenie a pripojte ho priamo k vašej MeshCore uzlu.",
+ "connectionChoiceUsbLabel": "USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "usbScreenStatus": "Vyberte USB zariadenie",
+ "usbScreenNote": "USB sériová komunikácia je aktívna na podporovaných zariadeniach s Androidom a na desktopových platformách.",
+ "usbScreenEmptyState": "Nenašli sa žiadne USB zariadenia. Pripojte jedno a obnovte.",
+ "usbErrorPermissionDenied": "Žiadosť o prístup cez USB bola zamietnutá.",
+ "usbErrorDeviceMissing": "Vybrané USB zariadenie už nie je dostupné.",
+ "usbErrorInvalidPort": "Vyberte platné USB zariadenie.",
+ "usbErrorBusy": "Ďalšia požiadavka na pripojenie cez USB je aktuálne v procese.",
+ "usbErrorNotConnected": "Nie je pripojené žiadne USB zariadenie.",
+ "usbErrorOpenFailed": "Nepodarilo sa otvoriť vybrané USB zariadenie.",
+ "usbErrorConnectFailed": "Nepodarilo sa sa sa pripojiť k vybranému USB zariadeniu.",
+ "usbErrorUnsupported": "Podpora USB sériového rozhrania nie je na tejto platforme dostupná.",
+ "usbErrorAlreadyActive": "Pripojenie cez USB je už aktivované.",
+ "usbErrorNoDeviceSelected": "Nebolo vybrané žiadne USB zariadenie.",
+ "usbErrorPortClosed": "Pripojenie cez USB nie je aktivované.",
+ "usbFallbackDeviceName": "Webový sériový zariadenie",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Hľadanie USB zariadení...",
+ "usbConnectionFailed": "Neúspešné pripojenie cez USB: {error}",
+ "usbStatus_notConnected": "Vyberte USB zariadenie",
+ "usbStatus_connecting": "Pripojenie k USB zariadeniu...",
+ "usbErrorConnectTimedOut": "Pripojenie nebolo úspešné. Uistite sa, že zariadenie má nainštalovaný firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostHint": "192.168.40.10",
+ "tcpHostLabel": "IP adresa",
+ "tcpScreenTitle": "Spojte sa pomocou protokolu TCP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Zadajte cieľovú adresu a pripojte sa.",
+ "tcpStatus_connectingTo": "Pripojenie k {endpoint}...",
+ "tcpErrorHostRequired": "Je potrebné zadať IP adresu.",
+ "tcpErrorPortInvalid": "Číslo portu musí byť medzi 1 a 65535.",
+ "tcpErrorUnsupported": "Prevoz prostredníctvom protokolu TCP nie je na tejto platforme podporovaný.",
+ "tcpErrorTimedOut": "Pripojenie TCP vypršalo.",
+ "tcpConnectionFailed": "Neúspešné vytvorenie TCP spojenia: {error}",
+ "map_showDiscoveryContacts": "Zobraziť kontakty objavov",
+ "map_setAsMyLocation": "Nastavte ako moju polohu",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Nastavenia súkromia",
+ "settings_privacySubtitle": "Ovládni, aké informácie sa zdieľajú.",
+ "settings_telemetryLocationMode": "Režim umiestnenia telemetrie",
+ "settings_telemetryBaseMode": "Základný režim telemetrie",
+ "settings_advertLocation": "Umiestnenie inzerátu",
+ "settings_telemetryEnvironmentMode": "Režim prostredia telemetrie",
+ "settings_advertLocationSubtitle": "Zahrnúť polohu do inzerátu",
+ "settings_allowAll": "Povoliť všetko",
+ "settings_privacySettingsDescription": "Vyberte, ktoré informácie váš zariadenie zdieľa s ostatnými.",
+ "settings_denyAll": "Zamietnuť všetko",
+ "settings_allowByContact": "Povoliť podľa kontaktových vlajok",
+ "contact_info": "Kontaktné informácie",
+ "contact_settings": "Nastavenia kontaktov",
+ "contact_teleBaseSubtitle": "Povoliť zdieľanie úrovne batérie a základnej telemetrie",
+ "contact_teleLoc": "Lokácia telemetrie",
+ "contact_teleLocSubtitle": "Povoliť zdieľanie údajov o lokalite",
+ "contact_teleEnv": "Prostredie telemetrie",
+ "contact_telemetry": "Telemetria",
+ "contact_clearChat": "Vymazať chat",
+ "contact_lastSeen": "Naposledy videný",
+ "contact_teleBase": "Báza telemetrie",
+ "contact_teleEnvSubtitle": "Povoliť zdieľanie údajov senzorov prostredia",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeightSubtitle": "Maximálna hmotnosť, ktorú môže trás prenášať vďaka úspešným zásielkam.",
+ "appSettings_initialRouteWeightSubtitle": "Počiatočná váha pre nové, objavené cesty",
+ "appSettings_initialRouteWeight": "Počiatočná váha trasy",
+ "appSettings_maxRouteWeight": "Maximálna hmotnosť trasy",
+ "appSettings_routeWeightSuccessIncrement": "Zvyšenie váhy úspechu",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Hmotnosť pridaná k trase po úspešnej doručení",
+ "appSettings_routeWeightFailureDecrement": "Sníženie váhy, ktorá sa používa na odhad rizika.",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Hmotnosť odstránená z cesty po neúspešnej doručenie",
+ "appSettings_maxMessageRetries": "Maximalný počet pokusov o doručenie správ",
+ "appSettings_maxMessageRetriesSubtitle": "Počet pokusov o odošleť pred označením správy ako neúspešnej",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Režim telemetrie bol aktualizovaný",
+ "settings_multiAck": "Viaceré ACK: {value}",
+ "map_showOverlaps": "Prekrývanie opakovača kľúča",
+ "map_runTraceWithReturnPath": "Vráťte sa späť po tej istej ceste.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Prosím, počkajte chvíľu, než zašlete znova.",
+ "appSettings_jumpToOldestUnread": "Presk oceň",
+ "appSettings_jumpToOldestUnreadSubtitle": "Pri otvorení chatu s neprečítanými správami, prejdite do prvého neprečítaného, namiesto poslednej.",
+ "appSettings_languageHu": "Maďarský",
+ "appSettings_languageJa": "Japonský",
+ "appSettings_languageKo": "Kórejský",
+ "radioStats_tooltip": "Statistiky rádiových a sieťových kanálov",
+ "radioStats_screenTitle": "Štatistiky rádiových vysielaní",
+ "radioStats_notConnected": "Pripojte sa k zariadeniu, aby ste mohli sledovať štatistiky rádiového vysielania.",
+ "radioStats_firmwareTooOld": "Statistické údaje z rádia vyžadujú sprievodný softvér verzie v8 alebo novšej.",
+ "radioStats_waiting": "Čakám na údaje…",
+ "radioStats_noiseFloor": "Úroveň hluku: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Posledný údaj RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Posledná hodnota SNR: {snr} dB",
+ "radioStats_txAir": "Čas vysielania na TX (celkový): {seconds} s",
+ "radioStats_rxAir": "Čas RX (celkový): {seconds} s",
+ "radioStats_chartCaption": "Úroveň šumu (dBm) pre posledné vzorky.",
+ "radioStats_stripNoise": "Úroveň hluku: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Získavanie údajov o rádiu…",
+ "radioStats_settingsTile": "Štatistiky rádiových vysielaní",
+ "radioStats_settingsSubtitle": "Úroveň hluku, RSSI, SNR a časové rozloženie",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "Prekladajte prichádzajúce správy a umožnite ich preklad pred odoslaním.",
+ "translation_enableTitle": "Aktivovať preklad",
+ "translation_composerTitle": "Preložte pred odeslaním",
+ "translation_title": "Preklad",
+ "translation_composerSubtitle": "Riadi výchoce stav ikony pre preklad, ktorú používa program.",
+ "translation_targetLanguage": "Cieľový jazyk",
+ "translation_useAppLanguage": "Použite jazyk aplikácie",
+ "translation_downloadedModelLabel": "Stiahnutý model",
+ "translation_presetModelLabel": "Prednastavený model od Hugging Face",
+ "translation_manualUrlLabel": "Odkaz na manuál (v elektronickej forme)",
+ "translation_downloadModel": "Stiahnuť model",
+ "translation_downloading": "Stiahnutie...",
+ "translation_working": "Práca...",
+ "translation_stop": "Zastavte",
+ "translation_mergingChunks": "Sliečenie stiahnutých častí do konečného súboru...",
+ "translation_downloadedModels": "Stiahnuté modely",
+ "translation_deleteModel": "Odstrániť model",
+ "translation_modelDownloaded": "Model pre preklad bol stiahnutý.",
+ "translation_downloadStopped": "Stiahnutie bolo prerušené.",
+ "translation_downloadFailed": "Neúspešné stiahnutie: {error}",
+ "translation_enterUrlFirst": "Najprv zadajte URL pre konkrétny model.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_linuxPairingHidePin": "Skryť PIN",
+ "scanner_linuxPairingShowPin": "Zobraziť PIN",
+ "scanner_linuxPairingPinTitle": "PIN pre párovanie cez Bluetooth",
+ "scanner_linuxPairingPinPrompt": "Zadajte PIN pre {deviceName} (ak neexistuje, nechajte prázdne).",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "Posielajte správy v pôvodnej písanom jazyku.",
+ "translation_composerEnabledHint": "Správy budú preložené, než budú odoslané.",
+ "translation_translateBeforeSending": "Preložte pred odeslaním",
+ "translation_messageTranslation": "Preklad textu",
+ "translation_translateTo": "Preložte do {language}",
+ "translation_translationOptions": "Možnosti prekladania",
+ "translation_systemLanguage": "Jazyk systému",
+ "repeater_cliQuickClockSync": "Synchronizácia hodin",
+ "repeater_cliQuickDiscovery": "Objaviť susedov",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "Synchronizácia hodiniek po prihlávení",
+ "repeater_clockSyncAfterLoginSubtitle": "Automaticky posielajte notifikáciu \"synchronizácia času\" po úspešnom prihládení.",
+ "chat_sendMessage": "Odoslať správu",
+ "repeater_guest": "Informácie o opakovači",
+ "room_guest": "Informácie o serveri",
+ "repeater_guestTools": "Nástroje pre hostí"
}
diff --git a/lib/l10n/app_sl.arb b/lib/l10n/app_sl.arb
index 09ee0bc..f6a317e 100644
--- a/lib/l10n/app_sl.arb
+++ b/lib/l10n/app_sl.arb
@@ -1,7 +1,15 @@
{
+ "channels_channelDeleteFailed": "Kanala {name} ni bilo mogoče izbrisati",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "sl",
"appTitle": "MeshCore Open",
- "nav_contacts": "Kontakti",
+ "nav_contacts": "Stiki",
"nav_channels": "Kanali",
"nav_map": "Karta",
"common_cancel": "Prekliči",
@@ -69,49 +77,49 @@
},
"scanner_stop": "Prekliči",
"scanner_scan": "Skeniraj",
- "device_quickSwitch": "Hitro preklopiti",
+ "device_quickSwitch": "Hitro preklop",
"device_meshcore": "MeshCore",
"settings_title": "Nastavitve",
"settings_deviceInfo": "Informacije o napravei",
"settings_appSettings": "Nastavitve aplikacije",
"settings_appSettingsSubtitle": "Obveščanja, sporoščanje in zemljevidi.",
- "settings_nodeSettings": "Nastavitve časa",
- "settings_nodeName": "Ime omrežno mesto",
- "settings_nodeNameNotSet": "Nezavedeno",
- "settings_nodeNameHint": "Vnesite ime časa",
+ "settings_nodeSettings": "Nastavitev časa",
+ "settings_nodeName": "Ime node-a",
+ "settings_nodeNameNotSet": "Ni nastavljeno",
+ "settings_nodeNameHint": "Vnesite ime node-a",
"settings_nodeNameUpdated": "Ime posodobljeno",
"settings_radioSettings": "Nastavitve radija",
- "settings_radioSettingsSubtitle": "Frekvenca, moč, razširni faktor",
+ "settings_radioSettingsSubtitle": "Frekvenca, moč, razširitveni faktor",
"settings_radioSettingsUpdated": "Radio nastavitve posodobljene",
"settings_location": "Lokacija",
"settings_locationSubtitle": "GPS koordinate",
"settings_locationUpdated": "Lokacija posodobljena",
"settings_locationBothRequired": "Vnesite širino in dolžino.",
- "settings_locationInvalid": "Neveljna zemeljska širina ali dolžina.",
+ "settings_locationInvalid": "Neveljavna zemeljska širina ali dolžina.",
"settings_latitude": "Širina",
"settings_longitude": "Dolžina",
- "settings_privacyMode": "Mod podjetja",
+ "settings_privacyMode": "Zasebnost",
"settings_privacyModeSubtitle": "Skrita imena/lokacije v oglasih",
"settings_privacyModeToggle": "Omogoči način zasebnosti, da skrijemo tvoje ime in lokacijo v oglasih.",
- "settings_privacyModeEnabled": "Privatni režim je omogočen.",
- "settings_privacyModeDisabled": "Privatni režim je onemogočen.",
+ "settings_privacyModeEnabled": "Privatni način je omogočen.",
+ "settings_privacyModeDisabled": "Privatni način je onemogočen.",
"settings_actions": "Akcije",
"settings_sendAdvertisement": "Pošlji Oglas",
"settings_sendAdvertisementSubtitle": "Trenutna prisotnost v oddajah",
"settings_advertisementSent": "Oglas poslan",
- "settings_syncTime": "Ugasniti čas",
- "settings_syncTimeSubtitle": "Nastavi uro naprave v čas telefona",
- "settings_timeSynchronized": "Sinhronizirano po času",
+ "settings_syncTime": "Nastavi uro",
+ "settings_syncTimeSubtitle": "Nastavi uro naprave na čas telefona",
+ "settings_timeSynchronized": "Ura sinhronizirana",
"settings_refreshContacts": "Ponovno obišči kontakte",
- "settings_refreshContactsSubtitle": "Ponovno naloži seznam kontaktov iz naprave",
- "settings_rebootDevice": "Restart Naprave",
- "settings_rebootDeviceSubtitle": "Ponovite zažetek naprave MeshCore",
- "settings_rebootDeviceConfirm": "Ste prepričani, da želite ponovno zagon napravke? Boste odvisni od omrežja.",
- "settings_debug": "Napravi popravek",
- "settings_bleDebugLog": "Logarjev zapis BLE",
- "settings_bleDebugLogSubtitle": "Navodila BLE, odgovori in surovo podatkovno",
- "settings_appDebugLog": "Log zapiske aplikacije",
- "settings_appDebugLogSubtitle": "Prijavni sporočila aplikacije",
+ "settings_refreshContactsSubtitle": "Ponovno naloži seznam stikov v napravi",
+ "settings_rebootDevice": "Ponovni zagon naprave",
+ "settings_rebootDeviceSubtitle": "Ponovno zaženi MeshCore napravo",
+ "settings_rebootDeviceConfirm": "Ste prepričani, da želite ponovno zagnati napravo? Povezava bo prekinjena.",
+ "settings_debug": "Debug",
+ "settings_bleDebugLog": "BLE debug log (razhroščevanje)",
+ "settings_bleDebugLogSubtitle": "BLE ukazi, odgovori in surovi podatki",
+ "settings_appDebugLog": "Logi aplikacije",
+ "settings_appDebugLogSubtitle": "Debug sporočila aplikacije",
"settings_about": "Oglejte si",
"settings_aboutVersion": "MeshCore Open v{version}",
"@settings_aboutVersion": {
@@ -121,30 +129,25 @@
}
}
},
- "settings_aboutLegalese": "MeshCore Odprtokodni Projekt 2024",
- "settings_aboutDescription": "Odprtokodni Flutter kličnik za naprave za LoRa mrežo MeshCore.",
+ "settings_aboutLegalese": "Odprtokodni projekt MeshCore 2024",
+ "settings_aboutDescription": "Odprtokodni Flutter klient za naprave za LoRa omrežje MeshCore.",
"settings_infoName": "Ime",
"settings_infoId": "ID",
"settings_infoStatus": "Status",
"settings_infoBattery": "Baterija",
- "settings_infoPublicKey": "Ključ javnega tipa",
- "settings_infoContactsCount": "Število kontaktov",
+ "settings_infoPublicKey": "Javni ključ",
+ "settings_infoContactsCount": "Število stikov",
"settings_infoChannelCount": "Število kanalov",
"settings_presets": "Prednastavitve",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frekvenca (MHz)",
"settings_frequencyHelper": "300,00 - 2500,00",
- "settings_frequencyInvalid": "Neveljčna frekvenca (300-2500 MHz)",
+ "settings_frequencyInvalid": "Neveljavna frekvenca (300-2500 MHz)",
"settings_bandwidth": "Pasovna širina",
"settings_spreadingFactor": "Razširitveni faktor",
"settings_codingRate": "Programska hitrost",
"settings_txPower": "TX Moč (dBm)",
"settings_txPowerHelper": "0 - 22",
- "settings_txPowerInvalid": "Neveljaven TX moč (0-22 dBm)",
- "settings_longRange": "Dolenje območje",
- "settings_fastSpeed": "Hitra hitrost",
+ "settings_txPowerInvalid": "Neveljavna TX moč (0-22 dBm)",
"settings_error": "Napaka: {message}",
"@settings_error": {
"placeholders": {
@@ -156,8 +159,8 @@
"appSettings_title": "Nastavitve aplikacije",
"appSettings_appearance": "Prikaži",
"appSettings_theme": "Tema",
- "appSettings_themeSystem": "Predpomnilnik sistema",
- "appSettings_themeLight": "Luč",
+ "appSettings_themeSystem": "Sistemska tema",
+ "appSettings_themeLight": "Svetlo",
"appSettings_themeDark": "Temno",
"appSettings_language": "Jezik",
"appSettings_languageSystem": "Sistemska privzeta vrednost",
@@ -174,8 +177,8 @@
"appSettings_languageNl": "Nederlands",
"appSettings_languageSk": "Slovenčina",
"appSettings_languageBg": "Български",
- "appSettings_notifications": "Obveščanja",
- "appSettings_enableNotifications": "Omogoči obveščanje",
+ "appSettings_notifications": "Obvestila",
+ "appSettings_enableNotifications": "Omogoči obvestila",
"appSettings_enableNotificationsSubtitle": "Prejmite obvestila o sporočilih in oglasih",
"appSettings_notificationPermissionDenied": "Odobritev obvestila zavrnjena",
"appSettings_notificationsEnabled": "Obvestila omogočena",
@@ -185,19 +188,19 @@
"appSettings_channelMessageNotifications": "Obvestila o sporočilih kanala",
"appSettings_channelMessageNotificationsSubtitle": "Pokaži obvestilo ob prejemanju sporočil kanala",
"appSettings_advertisementNotifications": "Opozorila o oglasih",
- "appSettings_advertisementNotificationsSubtitle": "Pokaži obvestilo, ko so novi vozlišči odkrivljeni.",
+ "appSettings_advertisementNotificationsSubtitle": "Pokaži obvestilo, ko so najdene nove naprave.",
"appSettings_messaging": "Komuniciranje",
"appSettings_clearPathOnMaxRetry": "Ponovite pot do cilja na največjem štetju",
"appSettings_clearPathOnMaxRetrySubtitle": "Ponovi pot zimske obveščevalne poti po 5 neuspešnih poskusih pošiljanja",
- "appSettings_pathsWillBeCleared": "Potnice bodo očiščene po 5 neuspešnih poskusih.",
- "appSettings_pathsWillNotBeCleared": "Potniški poti ne bodo samodejno čiščeni.",
- "appSettings_autoRouteRotation": "Avtomatsko Občutke in Rotacije",
- "appSettings_autoRouteRotationSubtitle": "Med spreminjanjem med najboljšimi potmi in plovilnim načinom",
+ "appSettings_pathsWillBeCleared": "Počisti pot po 5 neuspešnih poskusih.",
+ "appSettings_pathsWillNotBeCleared": "Poti ne bodo samodejno čiščene.",
+ "appSettings_autoRouteRotation": "Avtomatsko rotacija prenosne poti",
+ "appSettings_autoRouteRotationSubtitle": "Menjaj med boljšo potjo in flood načinom",
"appSettings_autoRouteRotationEnabled": "Samodejno krmilno rotiranje omogočeno",
"appSettings_autoRouteRotationDisabled": "Samodejno krmilno rotiranje je onemogočeno",
"appSettings_battery": "Baterija",
- "appSettings_batteryChemistry": "Razem z možnostmi",
- "appSettings_batteryChemistryPerDevice": "Nastavitve za naprave ({deviceName})",
+ "appSettings_batteryChemistry": "Kemija baterije",
+ "appSettings_batteryChemistryPerDevice": "Nastavitev za napravo ({deviceName})",
"@appSettings_batteryChemistryPerDevice": {
"placeholders": {
"deviceName": {
@@ -205,20 +208,20 @@
}
}
},
- "appSettings_batteryChemistryConnectFirst": "Povežite se z napravo za izbiro",
+ "appSettings_batteryChemistryConnectFirst": "Za izbiro se poveži z napravo",
"appSettings_batteryNmc": "18650 NMC (3,0-4,2V)",
"appSettings_batteryLifepo4": "LiFePO4 (2,6–3,65 V)",
"appSettings_batteryLipo": "LiPo (3,0-4,2V)",
- "appSettings_mapDisplay": "Prikaz zemljevide",
- "appSettings_showRepeaters": "Prikaži ponovitve",
- "appSettings_showRepeatersSubtitle": "Prikaži ponovljalne notranjosti na zemljeploscu",
- "appSettings_showChatNodes": "Prikaži čakalne notranjosti",
- "appSettings_showChatNodesSubtitle": "Prikaži pogovorni pike na zemljeploscu",
- "appSettings_showOtherNodes": "Pokaži druge vozlišča",
- "appSettings_showOtherNodesSubtitle": "Pokaži druge vrste notranjih elementov na zemljevalu.",
- "appSettings_timeFilter": "Filtri po času",
- "appSettings_timeFilterShowAll": "Pokaži vse notranje elemente",
- "appSettings_timeFilterShowLast": "Pokaži notranjosti iz zadnjih {hours} ur",
+ "appSettings_mapDisplay": "Prikaz zemljevida",
+ "appSettings_showRepeaters": "Prikaži repetitorje",
+ "appSettings_showRepeatersSubtitle": "Prikaži repetitorje na mapi",
+ "appSettings_showChatNodes": "Prikaži naprave za klepet",
+ "appSettings_showChatNodesSubtitle": "Prikaži naprave na zemljevidu",
+ "appSettings_showOtherNodes": "Pokaži druge naprave",
+ "appSettings_showOtherNodesSubtitle": "Pokaži druge vrste naprav na zemljevidu.",
+ "appSettings_timeFilter": "Filter po času",
+ "appSettings_timeFilterShowAll": "Pokaži vse naprave",
+ "appSettings_timeFilterShowLast": "Pokaži naprave v zadnjih {hours} urah",
"@appSettings_timeFilterShowLast": {
"placeholders": {
"hours": {
@@ -226,15 +229,15 @@
}
}
},
- "appSettings_mapTimeFilter": "Filtri časa zemljevida",
- "appSettings_showNodesDiscoveredWithin": "Pokaži notranje čepke, odkrivene v:",
- "appSettings_allTime": "Vse čase",
- "appSettings_lastHour": "Minuto nazaj",
+ "appSettings_mapTimeFilter": "Filter časa na zemljevidu",
+ "appSettings_showNodesDiscoveredWithin": "Pokaži naprave odkrite v:",
+ "appSettings_allTime": "Brez omejitev",
+ "appSettings_lastHour": "V zadnji uri",
"appSettings_last6Hours": "Zadnjih 6 ur",
"appSettings_last24Hours": "Zadnjih 24 ur",
- "appSettings_lastWeek": "Lepošno",
- "appSettings_offlineMapCache": "Omrezni Poudni Arhiv",
- "appSettings_noAreaSelected": "Nizkana označena površina",
+ "appSettings_lastWeek": "Prejšnji teden",
+ "appSettings_offlineMapCache": "Shramba zemljevidov brez povezave",
+ "appSettings_noAreaSelected": "Območje ni izbrano",
"appSettings_areaSelectedZoom": "Izbrano območje (povečava {minZoom}-{maxZoom})",
"@appSettings_areaSelectedZoom": {
"placeholders": {
@@ -246,19 +249,19 @@
}
}
},
- "appSettings_debugCard": "Napravi popravek",
- "appSettings_appDebugLogging": "Programski Log",
- "appSettings_appDebugLoggingSubtitle": "Log aplikacijske debug sporočila za odpravljanje težav",
- "appSettings_appDebugLoggingEnabled": "Omogočeno zaznamovanje napak v aplikaciji",
- "appSettings_appDebugLoggingDisabled": "Programski logi aplikacije so onemogočeni.",
- "contacts_title": "Kontakti",
- "contacts_noContacts": "Še ni kontaktov.",
- "contacts_contactsWillAppear": "Kontakti se bodo prikazali, ko naprave oglasijo.",
- "contacts_searchContacts": "Iskanje kontaktov...",
- "contacts_noUnreadContacts": "Nerešeno kontaktov.",
- "contacts_noContactsFound": "Niti ena oseba ali skupine ni najdena.",
- "contacts_deleteContact": "Izbrisati Kontakt",
- "contacts_removeConfirm": "Izbrisati {contactName} iz kontaktov?",
+ "appSettings_debugCard": "Razhroščevanje",
+ "appSettings_appDebugLogging": "Programski dnevnik",
+ "appSettings_appDebugLoggingSubtitle": "Dnevnik debug sporočil za odpravljanje težav",
+ "appSettings_appDebugLoggingEnabled": "Beleženje napak v aplikaciji omogočeno",
+ "appSettings_appDebugLoggingDisabled": "Beleženje napak v aplikacije onemogočeno.",
+ "contacts_title": "Stiki",
+ "contacts_noContacts": "Ni stikov.",
+ "contacts_contactsWillAppear": "Stiki se bodo prikazali, ko se naprave oglasijo.",
+ "contacts_searchContacts": "Iskanje stikov...",
+ "contacts_noUnreadContacts": "Ne prebrani stiki.",
+ "contacts_noContactsFound": "Stiki niso najdeni.",
+ "contacts_deleteContact": "Izbriši stik",
+ "contacts_removeConfirm": "Izbrišem {contactName} iz stikov?",
"@contacts_removeConfirm": {
"placeholders": {
"contactName": {
@@ -266,12 +269,12 @@
}
}
},
- "contacts_manageRepeater": "Upravljajte Ponovitve",
- "contacts_roomLogin": "Vnos v sobo",
- "contacts_openChat": "Odprta kleta",
- "contacts_editGroup": "Uredi Skupino",
- "contacts_deleteGroup": "Izbrisati Skupino",
- "contacts_deleteGroupConfirm": "Odpovedati {groupName}?",
+ "contacts_manageRepeater": "Upravljaj Ponovitve",
+ "contacts_roomLogin": "Prijava v sobo",
+ "contacts_openChat": "Odpri klepet",
+ "contacts_editGroup": "Uredi skupino",
+ "contacts_deleteGroup": "Izbriši skupino",
+ "contacts_deleteGroupConfirm": "Izbriši {groupName}?",
"@contacts_deleteGroupConfirm": {
"placeholders": {
"groupName": {
@@ -279,9 +282,10 @@
}
}
},
- "contacts_newGroup": "Novo skupino",
- "contacts_groupName": "Skupina imena",
+ "contacts_newGroup": "Nova skupina",
+ "contacts_groupName": "Ime skupine",
"contacts_groupNameRequired": "Ime skupine je obvezno.",
+ "contacts_groupNameReserved": "To ime skupine je rezervirano",
"contacts_groupAlreadyExists": "Skupina \"{name}\" že obstaja",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -290,11 +294,11 @@
}
}
},
- "contacts_filterContacts": "Filtri kontakt\\,...",
- "contacts_noContactsMatchFilter": "Niti ena oseba ne ustreza vašemu kriteriju.",
- "contacts_noMembers": "Nič članov.",
- "contacts_lastSeenNow": "Datum zadnjega vpisa zdaj",
- "contacts_lastSeenMinsAgo": "Zadnjič videti {minutes} minut nazaj",
+ "contacts_filterContacts": "Filtriraj stik\\,...",
+ "contacts_noContactsMatchFilter": "Noben stik ne ustreza vašemu kriteriju.",
+ "contacts_noMembers": "Ni članov.",
+ "contacts_lastSeenNow": "Nazadnje viden zdaj",
+ "contacts_lastSeenMinsAgo": "Zadnjič viden pred {minutes} minutami",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +306,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "Zadnjič ogledan pred 1 uro.",
- "contacts_lastSeenHoursAgo": "Zadnjič videti {hours} ur nazaj",
+ "contacts_lastSeenHourAgo": "Zadnjič viden pred 1 uro.",
+ "contacts_lastSeenHoursAgo": "Zadnjič viden pred {hours} urami",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +315,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "Zadnjič ogledan pred 1 dnem",
- "contacts_lastSeenDaysAgo": "Zadnjič videti {days} dni nazaj",
+ "contacts_lastSeenDayAgo": "Zadnjič viden pred 1 dnem",
+ "contacts_lastSeenDaysAgo": "Zadnjič viden pred {days} dnem",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -321,10 +325,10 @@
}
},
"channels_title": "Kanali",
- "channels_noChannelsConfigured": "Nekonfigurirane kanale",
- "channels_addPublicChannel": "Dodaj Objavni Kanal",
+ "channels_noChannelsConfigured": "Kanali še niso konfigurirani",
+ "channels_addPublicChannel": "Dodaj javni kanal",
"channels_searchChannels": "Poišči kanale...",
- "channels_noChannelsFound": "Niti kanalov najti ni.",
+ "channels_noChannelsFound": "Ne najdem kanalov.",
"channels_channelIndex": "Kanal {index}",
"@channels_channelIndex": {
"placeholders": {
@@ -334,13 +338,15 @@
}
},
"channels_hashtagChannel": "Hashtag kanal",
- "channels_public": "javno",
- "channels_private": "Zasebno",
- "channels_publicChannel": "Ogljišna skupina",
- "channels_privateChannel": "Zatemniščen kanal",
+ "channels_public": "Javni",
+ "channels_private": "Zasebni",
+ "channels_publicChannel": "Javni kanal",
+ "channels_privateChannel": "Zasebni kanal",
"channels_editChannel": "Uredi kanal",
+ "channels_muteChannel": "Utišaj kanal",
+ "channels_unmuteChannel": "Vklopi obvestila kanala",
"channels_deleteChannel": "Pošlji kanal",
- "channels_deleteChannelConfirm": "Izbrisati \"{name}\"? To se ne da povrniti.",
+ "channels_deleteChannelConfirm": "Izbrišem \"{name}\"? To se ne da povrniti.",
"@channels_deleteChannelConfirm": {
"placeholders": {
"name": {
@@ -424,8 +430,8 @@
}
}
},
- "chat_typeMessage": "Vnesite sporočilo...",
- "chat_messageTooLong": "Pošiljanje sporočila je onemogočeno, saj je preveliko (maksimalno {maxBytes} bajt).",
+ "chat_typeMessage": "Vnesi sporočilo...",
+ "chat_messageTooLong": "Pošiljanje sporočila je onemogočeno, saj je preveliko (maksimalno {maxBytes} byte-ov).",
"@chat_messageTooLong": {
"placeholders": {
"maxBytes": {
@@ -433,9 +439,9 @@
}
}
},
- "chat_messageCopied": "Pošljeno sporočilo",
- "chat_messageDeleted": "Pošiljanje sporočila izbrisano",
- "chat_retryingMessage": "Ponovna poskus.",
+ "chat_messageCopied": "Sporočilo poslano",
+ "chat_messageDeleted": "Sporočilo izbrisano",
+ "chat_retryingMessage": "Ponovni poskus.",
"chat_retryCount": "Ponovit {current}/{max}",
"@chat_retryCount": {
"placeholders": {
@@ -448,31 +454,31 @@
}
},
"chat_sendGif": "Pošlji GIF",
- "chat_reply": "Odpošlji",
- "chat_addReaction": "Dodaj Reakcijo",
+ "chat_reply": "Odgovori",
+ "chat_addReaction": "Dodaj reakcijo",
"chat_me": "jaz",
"emojiCategorySmileys": "Emoji",
"emojiCategoryGestures": "Gestikulacije",
"emojiCategoryHearts": "Srce",
"emojiCategoryObjects": "Predmeti",
"gifPicker_title": "Izberi GIF",
- "gifPicker_searchHint": "Iskalite GIF-e...",
- "gifPicker_poweredBy": "Naprodno z GIPHY",
- "gifPicker_noGifsFound": "Niti GIF-jev najti ni.",
- "gifPicker_failedLoad": "Neuspešno je naložilo GIF-e",
- "gifPicker_failedSearch": "Posodobit neuspešno.",
+ "gifPicker_searchHint": "Išči GIF-e...",
+ "gifPicker_poweredBy": "Napredno z GIPHY",
+ "gifPicker_noGifsFound": "Ne najdem GIF-ov.",
+ "gifPicker_failedLoad": "Neuspešno nalaganje GIF-a",
+ "gifPicker_failedSearch": "Iskanje neuspešno.",
"gifPicker_noInternet": "Ni internetne povezave",
"debugLog_appTitle": "Log zapiske aplikacije",
- "debugLog_bleTitle": "Logarjev zapis BLE",
- "debugLog_copyLog": "Kopiraj zapiske",
- "debugLog_clearLog": "Pasters log",
- "debugLog_copied": "Kopirana belež poteka.",
- "debugLog_bleCopied": "Kopirana beležke iz BLE",
- "debugLog_noEntries": "Še ni ustvarjenih debug zapisov.",
- "debugLog_enableInSettings": "Omogoči beleženje napak v aplikaciji v nastavitvah",
- "debugLog_frames": "Okna",
+ "debugLog_bleTitle": "Log zapis BLE",
+ "debugLog_copyLog": "Kopiraj dnevnik",
+ "debugLog_clearLog": "Briši log",
+ "debugLog_copied": "Beležka kopirana.",
+ "debugLog_bleCopied": "Kopirana beležka iz BLE",
+ "debugLog_noEntries": "Ni ustvarjenih debug zapisov.",
+ "debugLog_enableInSettings": "Omogoči beleženje napak v nastavitvah aplikacije",
+ "debugLog_frames": "Okvirji",
"debugLog_rawLogRx": "Svež Log-RX",
- "debugLog_noBleActivity": "Šele začnite z aktivnostjo BLE.",
+ "debugLog_noBleActivity": "Ni BLE aktivnosti.",
"debugFrame_length": "Izhodni rob: {count} bajtov",
"@debugFrame_length": {
"placeholders": {
@@ -542,8 +548,8 @@
"chat_forceFloodMode": "Nasilje obvezati v način",
"chat_recentAckPaths": "Nedavni poti ACK (tap za uporabo):",
"chat_pathHistoryFull": "Zapiske o poti so popolni. Izbriši vnose, da dodaš nove.",
- "chat_hopSingular": "skoč",
- "chat_hopPlural": "škrabec",
+ "chat_hopSingular": "skok",
+ "chat_hopPlural": "skokov",
"chat_hopsCount": "{count} {count, plural, =1{hop} other{hops}}",
"@chat_hopsCount": {
"placeholders": {
@@ -554,16 +560,16 @@
},
"chat_successes": "Uspešni",
"chat_removePath": "Izbriši pot",
- "chat_noPathHistoryYet": "Še ni shranjenih poti.\nPošlji sporočilo za odkrivanje poti.",
+ "chat_noPathHistoryYet": "Ni shranjenih poti.\nPošlji sporočilo za odkrivanje poti.",
"chat_pathActions": "Potni ukazi:",
"chat_setCustomPath": "Nastavi Prilozeno Pot",
"chat_setCustomPathSubtitle": "Ročno določite potniško pot.",
- "chat_clearPath": "Čista pot",
+ "chat_clearPath": "Počisti pot",
"chat_clearPathSubtitle": "Ob naslednji pošiljanju znova zbrati.",
"chat_pathCleared": "Pot je očiščena. Naslednje sporočilo bo ponovno odkril pot.",
"chat_floodModeSubtitle": "Uporabi tipko usmerjevanja v meniju aplikacije.",
"chat_floodModeEnabled": "Narejena je bila omrežna modaliteta. Vklopi jo znova preko ikone v meniju aplikacije.",
- "chat_fullPath": "Polni pot",
+ "chat_fullPath": "Polna pot",
"chat_pathDetailsNotAvailable": "Podrobnosti poti zaenkrat niso na voljo. Poskusite poslati sporočilo za osvežitev.",
"chat_pathSetHops": "Pot nastavljen: {hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
"@chat_pathSetHops": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Odpreti povezavo?",
+ "chat_openLinkConfirmation": "Ali želite odpreti to povezavo v brskalniku?",
+ "chat_open": "Odpri",
+ "chat_couldNotOpenLink": "Povezave ni bilo mogoče odpreti: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Neveljavna oblika povezave",
"map_title": "Mapa omrežja",
"map_noNodesWithLocation": "Nihče od notranjih elementov nima podatkov o lokaciji.",
"map_nodesNeedGps": "Omrežje morajo deliti svoje GPS koordinate,\nda se prikazao na zemljeobrazniku.",
@@ -1092,13 +1110,13 @@
}
}
},
- "repeater_cliQuickGetName": "Dobiti ime",
+ "repeater_cliQuickGetName": "Pridobi ime",
"repeater_cliQuickGetRadio": "Dobiti Radiopravo",
- "repeater_cliQuickGetTx": "Dobiti TX",
+ "repeater_cliQuickGetTx": "Pridobi TX",
"repeater_cliQuickNeighbors": "Sosedi",
"repeater_cliQuickVersion": "Različica",
"repeater_cliQuickAdvertise": "Oglasite",
- "repeater_cliQuickClock": "Urnik",
+ "repeater_cliQuickClock": "Ura",
"repeater_cliHelpAdvert": "Pošlje paket oglasov",
"repeater_cliHelpReboot": "Ponastavi naprave. (Opomba, lahko pride do 'Timeouta', kar je normalno)",
"repeater_cliHelpClock": "Prikaže trenutno uro po uri naprave.",
@@ -1130,7 +1148,7 @@
"repeater_cliHelpSetBridgeSecret": "Nastavi skrivni dostop za mostove ESPNOW.",
"repeater_cliHelpSetAdcMultiplier": "Nastavi prilagoditev faktorja za prilagoditev poravnalnega napetosti baterije (podprt le na izbranih ploščah).",
"repeater_cliHelpTempRadio": "Nastavi začasne radio parametre za določeno časovno obdobje, kar po preteku časa vrne originalne radio parametre. (ne shranjuje v preferencije).",
- "repeater_cliHelpSetPerm": "Modificira ACL. Odstrani ustreznu vnos (po predponi pubkeyja), če je \"permissions\" enako nič. Dodaja nov vnos, če je pubkey-hex v celoti in trenutno ni v ACL. Posodobi vnos po ustreznem predponi pubkeyja. Bitje dovoljenj se razlikuje glede na firmware vlogo, vendar so prvi dve bitki: 0 (Gost), 1 (Lezenje samo), 2 (Lezenje in pisanje), 3 (Administrator).",
+ "repeater_cliHelpSetPerm": "Modificira ACL. Odstrani ustrezen vnos (po predponi pubkeyja), če je \"permissions\" enako nič. Dodaja nov vnos, če je pubkey-hex v celoti in trenutno ni v ACL. Posodobi vnos po ustreznem predponi pubkeyja. Bitje dovoljenj se razlikuje glede na firmware vlogo, vendar so prvi dve bitki: 0 (Gost), 1 (Lezenje samo), 2 (Lezenje in pisanje), 3 (Administrator).",
"repeater_cliHelpGetBridgeType": "Dobrodošli pri izbiri vrste mostu: brez, rs232, espnow",
"repeater_cliHelpLogStart": "Začnete beleženje paketov v datotekovni sistem.",
"repeater_cliHelpLogStop": "Ustavite beleženje paketov v datotečno sistem.",
@@ -1159,8 +1177,8 @@
"repeater_settingsCategory": "Nastavitve",
"repeater_bridge": "Most",
"repeater_logging": "Logiranje",
- "repeater_neighborsRepeaterOnly": "Sosedi (le za ponovitelja)",
- "repeater_regionManagementRepeaterOnly": "Upravljanje regij (zgolj za ponovitve)",
+ "repeater_neighborsRepeaterOnly": "Sosedi (le za repetitorje)",
+ "repeater_regionManagementRepeaterOnly": "Upravljanje regij (zgolj za repetitorje)",
"repeater_regionNote": "Regionske ukazi so bili uvedeni za upravljanje z regijskimi definicijami in dovolili.",
"repeater_gpsManagement": "Upravljanje GPS",
"repeater_gpsNote": "GPS ukaz je bil uveden za upravljanje z vprašanji, povezanimi z lokacijo.",
@@ -1232,9 +1250,9 @@
"channelPath_repeaterHops": "Skoki ponovitelja",
"channelPath_noHopDetails": "Podrobnosti o paketu za dostavo niso navedene.",
"channelPath_messageDetails": "Podrobnosti sporočila",
- "channelPath_senderLabel": "Pošiljalec",
- "channelPath_timeLabel": "Čas",
- "channelPath_repeatsLabel": "Ponovi",
+ "channelPath_senderLabel": "Pošiljatelj",
+ "channelPath_timeLabel": "Ura",
+ "channelPath_repeatsLabel": "Ponovitve",
"channelPath_pathLabel": "Pot {index}",
"channelPath_observedLabel": "Opazovani",
"channelPath_observedPathTitle": "Opazovana pot {index} • {hops}",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighboursSubtitle": "Pogledati nič sosednjih hopjev.",
- "repeater_neighbours": "Sosedi",
+ "repeater_neighborsSubtitle": "Pogledati nič sosednjih hopjev.",
+ "repeater_neighbors": "Sosedi",
"neighbors_receivedData": "Prejeto podatke o sosedih",
"neighbors_requestTimedOut": "Sosedi zahtevajo izklop po dogovoru.",
"neighbors_errorLoading": "Napaka pri obnašanju sosedov: {error}",
- "neighbors_repeatersNeighbours": "Ponovitve Sosedi",
+ "neighbors_repeatersNeighbors": "Ponovitve Sosedi",
"neighbors_noData": "Niso na voljo podatki o sosedih.",
"channels_joinPrivateChannel": "Pridružite se zasebni skupini",
"channels_createPrivateChannelDesc": "Varno zaklenjeno s skrivnim ključem.",
@@ -1466,23 +1484,594 @@
"community_addPublicChannel": "Dodaj Objavni Kanal Komunitarja",
"community_addPublicChannelHint": "Samodejno dodaj javni kanal za to skupnost.",
"community_noCommunities": "Še nobena skupnost se ni pridružila.",
- "community_scanOrCreate": "Skenirajte QR kodo ali ustvarite skupnost za začetek.",
- "community_manageCommunities": "Upravljajte skupnosti",
+ "community_scanOrCreate": "Skeniraj QR kodo ali ustvari skupnost za začetek.",
+ "community_manageCommunities": "Upravljanje skupnosti",
"community_delete": "Opusti skupnost",
- "community_deleteConfirm": "Zapustiti \"{name}\"?",
+ "community_deleteConfirm": "Zapusti \"{name}\"?",
"community_deleteChannelsWarning": "To bo izbrisalo tudi {count} kanal/kanalov in njihova sporočila.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Zapustil skupnost \"{name}\"",
- "community_addHashtagChannel": "Dodaj Oznako Obštnine",
+ "community_addHashtagChannel": "Dodaj hashtag kanal",
"community_addHashtagChannelDesc": "Dodajte hashtag kanal za to skupnost.",
"community_selectCommunity": "Izberi skupnost",
"community_regularHashtag": "Oznaka s hashtagom",
- "community_regularHashtagDesc": "javna oznaka (kateri koli lahko sodelujejo)",
+ "community_regularHashtagDesc": "javna oznaka (kdorkoli lahko sodeluje)",
"community_communityHashtag": "Skupnostni hashtag",
"community_communityHashtagDesc": "Izključeno za uporabnike skupnosti",
- "community_forCommunity": "Za {name}"
+ "community_forCommunity": "Za {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_secretRegenerated": "Geslo za \"{name}\" ponovno ustvarjeno",
+ "community_regenerateSecret": "Ponovno ustvari geslo",
+ "community_regenerateSecretConfirm": "Preberite novo tajno geslo za \"{name}\"? Vsi članici morajo prebrati novo QR kodo, da lahko nadaljujejo s komunikacijo.",
+ "community_regenerate": "Preberi znova",
+ "community_scanToUpdateSecret": "Skeniraj novo QR kodo za posodabljanje ključa za {name}",
+ "community_updateSecret": "Ažuriraj ključ",
+ "community_secretUpdated": "Skrivnostno spremembo za \"{name}\"",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Ti",
+ "pathTrace_failed": "Sledenje poti ni uspelo.",
+ "pathTrace_notAvailable": "Potni sled ni na voljo.",
+ "pathTrace_refreshTooltip": "Osveži Path Trace.",
+ "contacts_pathTrace": "Sledenje poti",
+ "contacts_ping": "Pingati",
+ "contacts_repeaterPathTrace": "Sledi poti do ponavljalnika",
+ "contacts_repeaterPing": "Pinguj ponavljalnik",
+ "contacts_roomPathTrace": "Sledenje poti do strežnika sobe",
+ "contacts_roomPing": "Ping strežnik sobe",
+ "contacts_chatTraceRoute": "Slediti poti žarkov",
+ "contacts_pathTraceTo": "Trace route to {name}",
+ "appSettings_languageRu": "Ruščina",
+ "appSettings_languageUk": "Ukrajinsko",
+ "appSettings_enableMessageTracing": "Omogoči sledenje sporočilom",
+ "appSettings_enableMessageTracingSubtitle": "Prikaži podrobne metapodatke o usmerjanju in časovnem usklajevanju sporočil",
+ "contacts_contactImported": "Kontakt je bil uvožen.",
+ "contacts_contactImportFailed": "Kontakt ni bil uspešno uvožen.",
+ "contacts_zeroHopAdvert": "Reklama brez posrednikov",
+ "contacts_floodAdvert": "Poplavna oglás",
+ "contacts_invalidAdvertFormat": "Neveljavni kontaktne podatke",
+ "contacts_clipboardEmpty": "Odložišče je prazno.",
+ "contacts_copyAdvertToClipboard": "Kopiraj oglas v odložišče",
+ "contacts_addContactFromClipboard": "Dodaj stik iz odložišča",
+ "contacts_zeroHopContactAdvertSent": "Poslano po oglasu.",
+ "contacts_zeroHopContactAdvertFailed": "Pošiljanje kontakta ni uspelo.",
+ "contacts_contactAdvertCopied": "Oglas je bil kopiran v odložišče.",
+ "contacts_contactAdvertCopyFailed": "Kopiranje oglasa v odložišče je spodletelo.",
+ "contacts_ShareContactZeroHop": "Deliti kontakt prek oglasa",
+ "contacts_ShareContact": "Kopiraj stik v Odložišče",
+ "notification_activityTitle": "Aktivnost MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{sporočilo} =2{sporočili} few{sporočila} other{sporočil}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{sporočilo kanala} =2{sporočili kanala} few{sporočila kanala} other{sporočil kanala}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{novo vozlišče} =2{novi vozlišči} few{nova vozlišča} other{novih vozlišč}}",
+ "notification_newTypeDiscovered": "Odkrito novo {contactType}",
+ "notification_receivedNewMessage": "Prejeto novo sporočilo",
+ "settings_gpxExportAll": "Izvozi vse kontakte v GPX",
+ "settings_gpxExportContacts": "Izvoz spremljevalcev v GPX",
+ "settings_gpxExportRepeatersSubtitle": "Izvozi ponovljene oddajnike / strežnik sobe z lokacijo v datoteko GPX.",
+ "settings_gpxExportRepeaters": "Izvoz ponoviteljev / strežnika sobe v GPX",
+ "settings_gpxExportError": "Pri izvozu je prišlo do napake.",
+ "settings_gpxExportRepeatersRoom": "Lokacije ponovljivca in strežnika sobe",
+ "settings_gpxExportChat": "Lokacije spremljevalcev",
+ "settings_gpxExportAllContacts": "Lokacije vseh stikov",
+ "settings_gpxExportContactsSubtitle": "Izvozi spremljevalce z lokacijo v datoteko GPX.",
+ "settings_gpxExportAllSubtitle": "Izvozi vse kontakte z lokacijo v datoteko GPX.",
+ "settings_gpxExportSuccess": "Uspešno izvoz GPX datoteke.",
+ "settings_gpxExportShareText": "Podatki kart izvoženi iz meshcore-open",
+ "settings_gpxExportNoContacts": "Ni stikov za izvoz.",
+ "settings_gpxExportNotAvailable": "Ni podprto na vašem napravi/operacijskem sistemu",
+ "settings_gpxExportShareSubject": "meshcore-open izvoz podatkov GPX karte",
+ "pathTrace_someHopsNoLocation": "Ena ali več hmelju manjka lokacija!",
+ "map_tapToAdd": "Pritisnite na vozlišča, da jih dodate poti.",
+ "map_removeLast": "Odstrani Zadnji",
+ "map_runTrace": "Zaženi sledenje poti",
+ "pathTrace_clearTooltip": "Počisti pot",
+ "map_pathTraceCancelled": "Spremljanje poti je prekinjeno.",
+ "scanner_enableBluetooth": "Omogočite Bluetooth",
+ "scanner_bluetoothOffMessage": "Prosimo, vklopite Bluetooth, da lahko poiščete naprave.",
+ "scanner_chromeRequired": "Zahtevan brskalnik Chrome",
+ "scanner_chromeRequiredMessage": "Ta spletna aplikacija za podporo Bluetooth zahteva Google Chrome ali brskalnik na osnovi Chromiuma.",
+ "scanner_bluetoothOff": "Bluetooth je izklopljen",
+ "snrIndicator_lastSeen": "Zadnjič videno",
+ "snrIndicator_nearByRepeaters": "Bližnji ponovitelji",
+ "chat_ShowAllPaths": "Prikaži vse poti",
+ "settings_clientRepeatFreqWarning": "Za ponovni prenos na brezžični način so potrebne frekvence 433, 869 ali 918 MHz.",
+ "settings_clientRepeatSubtitle": "Omogočite temu naprave, da ponavlja paketne sporočila za druge.",
+ "settings_clientRepeat": "Neovadno ponavljanje",
+ "settings_aboutOpenMeteoAttribution": "Podatki o višini LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Enote",
+ "appSettings_unitsMetric": "Metrična (m/km)",
+ "appSettings_unitsImperial": "Imperialno (ft / mi)",
+ "map_lineOfSight": "Linija vida",
+ "map_losScreenTitle": "Linija vida",
+ "losSelectStartEnd": "Izberite začetno in končno vozlišče za LOS.",
+ "losRunFailed": "Preverjanje vidnega polja ni uspelo: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Počisti vse točke",
+ "losRunToViewElevationProfile": "Zaženite LOS za ogled višinskega profila",
+ "losMenuTitle": "LOS meni",
+ "losMenuSubtitle": "Tapnite vozlišča ali dolgo pritisnite na zemljevid za točke po meri",
+ "losShowDisplayNodes": "Pokaži prikazna vozlišča",
+ "losCustomPoints": "Točke po meri",
+ "losCustomPointLabel": "Po meri {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Točka A",
+ "losPointB": "Točka B",
+ "losAntennaA": "Antena A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antena B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Zaženi LOS",
+ "losNoElevationData": "Ni podatkov o višini",
+ "losProfileClear": "{distance} {distanceUnit}, čisti LOS, najmanjša razdalja {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blokiral {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: preverjam ...",
+ "losStatusNoData": "LOS: ni podatkov",
+ "losStatusSummary": "LOS: {clear}/{total} jasno, {blocked} blokirano, {unknown} neznano",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Podatki o nadmorski višini niso na voljo za enega ali več vzorcev.",
+ "losErrorInvalidInput": "Neveljavni podatki o točkah/višini za izračun LOS.",
+ "losRenameCustomPoint": "Preimenujte točko po meri",
+ "losPointName": "Ime točke",
+ "losShowPanelTooltip": "Pokaži ploščo LOS",
+ "losHidePanelTooltip": "Skrij ploščo LOS",
+ "losElevationAttribution": "Podatki o višini: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Radijski horizont",
+ "losLegendLosBeam": "Linija vidnosti",
+ "losLegendTerrain": "Teren",
+ "losFrequencyLabel": "Frekvenca",
+ "losFrequencyInfoTooltip": "Prikaži podrobnosti izračuna",
+ "losFrequencyDialogTitle": "Izračun radijskega horizonta",
+ "losFrequencyDialogDescription": "Začenši od k={baselineK} pri {baselineFreq} MHz, izračun prilagodi k-faktor za trenutni pas {frequencyMHz} MHz, ki določa ukrivljeno zgornjo mejo radijskega horizonta.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_favorites": "Priljubljene",
+ "listFilter_removeFromFavorites": "Odstrani iz priljubljenih",
+ "listFilter_addToFavorites": "Dodaj v priljubljene",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Neprebrano",
+ "contacts_searchFavorites": "Iskanje {number}{str} priljubljenih...",
+ "contacts_searchRoomServers": "Išči {number}{str} strežnikov sob...",
+ "contacts_searchContactsNoNumber": "Iskanje stikov...",
+ "contacts_searchRepeaters": "Išči {number}{str} ponavljalnike...",
+ "contacts_searchUsers": "Išči {number}{str} uporabnikov...",
+ "settings_contactSettings": "Nastavitve stika",
+ "contactsSettings_autoAddTitle": "Avtomatsko odkrivanje",
+ "contactsSettings_autoAddUsersTitle": "Avtomatsko dodaj uporabnike",
+ "contactsSettings_autoAddRepeatersTitle": "Avtomatsko dodaj ponovitelje",
+ "contactsSettings_autoAddRepeatersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite ponovitelje.",
+ "contactsSettings_autoAddRoomServersTitle": "Avtomatsko dodaj strežnike sob",
+ "contactsSettings_autoAddRoomServersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite strežnike sob.",
+ "contactsSettings_otherTitle": "Druge nastavitve v zvezi s stiki",
+ "settings_contactSettingsSubtitle": "Nastavitve za dodajanje stikov.",
+ "contactsSettings_title": "Nastavitve stikov",
+ "contactsSettings_autoAddSensorsTitle": "Avtomatsko dodaj senzorje",
+ "contactsSettings_autoAddUsersSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite uporabnike.",
+ "discoveredContacts_noMatching": "Ni ujemajočih stikov",
+ "contactsSettings_autoAddSensorsSubtitle": "Dovoli spremljevalcu, da samodejno doda odkrite senzorje.",
+ "discoveredContacts_addContact": "Dodaj stik",
+ "discoveredContacts_contactAdded": "Kontakt dodan",
+ "discoveredContacts_copyContact": "Kopiraj stik v odložišče",
+ "contactsSettings_overwriteOldestTitle": "Prepiši najstarejše",
+ "discoveredContacts_Title": "Odkriti stiki",
+ "discoveredContacts_searchHint": "Najdeni stiki po iskanju",
+ "discoveredContacts_deleteContact": "Izbriši stik",
+ "contactsSettings_overwriteOldestSubtitle": "Ko je seznam stikov poln, bo najstarejši nestarševski stik zamenjan.",
+ "common_deleteAll": "Izbriši vse",
+ "discoveredContacts_deleteContactAllContent": "Ste prepričani, da želite izbrisati vse odkrite kontakte?",
+ "discoveredContacts_deleteContactAll": "Izbriši vse odkrite kontakte",
+ "map_guessedLocation": "Predpostavljena lokacija",
+ "map_showGuessedLocations": "Pokaži lokacije domnevnih not.",
+ "usbScreenTitle": "Povežite preko USB",
+ "usbScreenSubtitle": "Izberite zaznano serijsko napravo in se neposredno povežite z vašo MeshCore napravo.",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenStatus": "Izberite USB naprave",
+ "usbScreenNote": "USB serijska povezava je aktivna na podprtih napravah Android in na desktop platformah.",
+ "usbScreenEmptyState": "Niti en USB naprave niso najdeni. Povežite eno in posodobite.",
+ "usbErrorPermissionDenied": "Dovoljenje za dostop preko USB-ja je bilo zavrnjeno.",
+ "usbErrorDeviceMissing": "Izbrani USB napravej je več ne.",
+ "usbErrorInvalidPort": "Izberite veljavno USB naprave.",
+ "usbErrorBusy": "Že je v teku zahteva za povezavo preko USB.",
+ "usbErrorNotConnected": "Ni priklopljenih USB naprave.",
+ "usbErrorOpenFailed": "Uspešno ni bilo mogo, da se odpre izbran naprave USB.",
+ "usbErrorConnectFailed": "Niso bilo mogoče uskladiti povezave z izbranim USB napom.",
+ "usbErrorUnsupported": "USB serijska komunikacija ni podprta na tej platformi.",
+ "usbErrorAlreadyActive": "USB povezava je že aktivirana.",
+ "usbErrorNoDeviceSelected": "Ni bilo izbranega USB naprave.",
+ "usbErrorPortClosed": "USB povezava ni aktivirana.",
+ "usbFallbackDeviceName": "Naprave za serijsko komunikacijo preko spleta",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_notConnected": "Izberite USB naprave.",
+ "usbStatus_connecting": "Povezava z USB napravo...",
+ "usbStatus_searching": "Iskanje USB naprav...",
+ "usbConnectionFailed": "Napaka pri povezavi preko USB: {error}",
+ "usbErrorConnectTimedOut": "Vzpostavitve ni bilo mogo. Prosimo, da se prepričate, da ima naprave trenutno nameštan firmware USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostLabel": "IP naslov",
+ "tcpHostHint": "192.168.40.10",
+ "tcpScreenTitle": "Komunicirajte preko protokola TCP",
+ "tcpPortLabel": "Vrata",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Vnesite končni naslov in se povežite",
+ "tcpStatus_connectingTo": "Povezava z {endpoint}...",
+ "tcpErrorHostRequired": "Potrebna je IP-naslov.",
+ "tcpErrorPortInvalid": "Port mora biti med 1 in 65535.",
+ "tcpErrorUnsupported": "Transport preko protokola TCP ni podprt na tej platformi.",
+ "tcpErrorTimedOut": "Povezava TCP je presegla časovno obdobje.",
+ "tcpConnectionFailed": "Napaka pri povezavi TCP: {error}",
+ "map_showDiscoveryContacts": "Prikaži odkritja kontaktov",
+ "map_setAsMyLocation": "Nastavite to kot mojo lokacijo",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Nastavitve zasebnosti",
+ "settings_privacySettingsDescription": "Izberite, katere informacije vaš naprava deli z drugimi.",
+ "settings_telemetryBaseMode": "Osnovni način telemetrije",
+ "settings_telemetryLocationMode": "Način delovanja telemetrije",
+ "settings_telemetryEnvironmentMode": "Način delovanja okolja telemetrije",
+ "settings_advertLocation": "Lokacija oglasa",
+ "settings_allowByContact": "Dovoli po kontaktnih zastavah",
+ "settings_denyAll": "Zavrniti vse",
+ "settings_allowAll": "Dovoli vse",
+ "settings_privacySubtitle": "Kontrolirajte, katere informacije so deljene.",
+ "contact_info": "Kontaktni podatki",
+ "contact_teleBase": "Baza telemetrije",
+ "contact_teleBaseSubtitle": "Dovoli deljenje stanja baterije in osnovne telemetrije",
+ "contact_teleLoc": "Lokacija telemetrije",
+ "contact_lastSeen": "Zadnjič videno",
+ "contact_settings": "Nastavitve stika",
+ "settings_advertLocationSubtitle": "Vključi lokacijo v oglas.",
+ "contact_telemetry": "Telemetrija",
+ "contact_clearChat": "Počisti klepet",
+ "contact_teleEnv": "Okolje telemetrije",
+ "contact_teleEnvSubtitle": "Dovoli deljenje podatkov okoljskih senzorjev",
+ "contact_teleLocSubtitle": "Dovoli deljenje podatkov o lokaciji",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeightSubtitle": "Največja teža, ki jo lahko pot doseže s uspešnimi dostavnami.",
+ "appSettings_initialRouteWeight": "Izvirna teža poti",
+ "appSettings_initialRouteWeightSubtitle": "Izguba teže za nove, odkriti poti",
+ "appSettings_maxRouteWeight": "Največja dovoljena teža poti",
+ "appSettings_routeWeightSuccessIncrement": "Učinkovitost: povečanje",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Težava, dodana poti po uspešni dostavi",
+ "appSettings_routeWeightFailureDecrement": "Zmanjšanje teže, ki je povezana s pomanjkanjem",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Težo, ki ni bila uspešno dostavljena, odstranili s poti.",
+ "appSettings_maxMessageRetries": "Najve število poskusov pošiljanja sporočil",
+ "appSettings_maxMessageRetriesSubtitle": "Število poskusov ponovnega poslanja, preden se sporočilo označuje kot neuspešno",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_multiAck": "Večkratni potrditvi: {value}",
+ "settings_telemetryModeUpdated": "Način telemetrije posodobljen",
+ "map_showOverlaps": "Prekrivanje ključa ponovnega predvajanja",
+ "map_runTraceWithReturnPath": "Vrni se nazaj po isti poti.",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_languageHu": "Madžarski",
+ "appSettings_jumpToOldestUnreadSubtitle": "Ko odpirate klepet z neprebranimi sporočili, se premaknite na prvo neprebrano sporočilo, namesto najnovejšega.",
+ "chat_sendCooldown": "Prosimo, počakajte trenutek, preden pošljete ponovno.",
+ "appSettings_jumpToOldestUnread": "Pritisnite za najstarejše nepročitano sporočilo",
+ "appSettings_languageJa": "Japonski",
+ "appSettings_languageKo": "Korejski",
+ "radioStats_tooltip": "Statistike za radio in mrežo",
+ "radioStats_notConnected": "Povežite se z napravo, da si ogledate statistiko o radiju.",
+ "radioStats_screenTitle": "Radijske statistike",
+ "radioStats_firmwareTooOld": "Statistika za radio zahteva združljivo programsko opremo v8 ali kasnejše.",
+ "radioStats_waiting": "Čakam na podatke…",
+ "radioStats_noiseFloor": "Število šuma: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Najkasnejše vrednost RSSI: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Najkasnejše vrednost SNR: {snr} dB",
+ "radioStats_txAir": "Čas na TX (skupno): {seconds} s",
+ "radioStats_rxAir": "Čas, namenjen RX-ju (skupno): {seconds} s",
+ "radioStats_chartCaption": "Ravnovredna raven šuma (dBm) za nedavne vzorce.",
+ "radioStats_stripNoise": "Število šuma: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Prejemanje statistike o radiju…",
+ "radioStats_settingsTile": "Radijske statistike",
+ "radioStats_settingsSubtitle": "Število šumov, RSSI, SNR in čas, ki ga je napolnila oprema",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Preprištejte, preden pošljete",
+ "translation_title": "Prevod",
+ "translation_enableSubtitle": "Prevedite vstopne sporočila in omogočite predhodno prevajanje.",
+ "translation_enableTitle": "Omogočite prevod",
+ "translation_composerSubtitle": "Ureja privzeto stanje ikone za prevod, ki jo uporablja avtor.",
+ "translation_targetLanguage": "Ciljna jezika",
+ "translation_useAppLanguage": "Uporabite jezik aplikacije",
+ "translation_downloadedModelLabel": "Naložen model",
+ "translation_presetModelLabel": "Prednastavljeni model Hugging Face",
+ "translation_manualUrlLabel": "URL za ročni model",
+ "translation_downloadModel": "Prenesite model",
+ "translation_downloading": "Izvajanje...",
+ "translation_working": "Delo...",
+ "translation_stop": "Prekliji",
+ "translation_mergingChunks": "Sklapljanje prenesenih delov v končni datoteko...",
+ "translation_downloadedModels": "Naloženi modeli",
+ "translation_deleteModel": "Izbrisati model",
+ "translation_modelDownloaded": "Model za prevajanje je bil naložen.",
+ "translation_downloadStopped": "Prenos je bil prekinjen.",
+ "translation_downloadFailed": "Izgovoritev ni bila uspešna: {error}",
+ "translation_enterUrlFirst": "Najprej vnesite URL model.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_translateBeforeSending": "Preprištejte, preden pošljete",
+ "translation_composerDisabledHint": "Pošljite sporočila v originalnem tipkanem jeziku.",
+ "translation_composerEnabledHint": "Vsebina sporočil bo prevedena, preden jih pošljemo.",
+ "translation_messageTranslation": "Prevod sporočila",
+ "translation_translateTo": "Prevesti v {language}",
+ "translation_translationOptions": "Možnosti prevoda",
+ "translation_systemLanguage": "Jezik sistema",
+ "scanner_linuxPairingShowPin": "Prikaži PIN",
+ "scanner_linuxPairingHidePin": "Skrij PIN",
+ "scanner_linuxPairingPinPrompt": "Vnesite PIN za {deviceName} (pustite prazno, če ga ni).",
+ "scanner_linuxPairingPinTitle": "Bluetooth PIN za seznanjanje",
+ "repeater_cliQuickDiscovery": "Odkrijte sosede",
+ "repeater_cliQuickClockSync": "Usklajevanje ure",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Samodejno po uspešnem vstopu pošljite obvestilo o sinhronizaciji časa.",
+ "repeater_clockSyncAfterLogin": "Sinhronizacija ure po prijavi",
+ "repeater_guest": "Informacije o ponovljalniku",
+ "chat_sendMessage": "Pošlji sporočilo",
+ "room_guest": "Informacije o strežniku",
+ "repeater_guestTools": "Naložila za goste"
}
diff --git a/lib/l10n/app_sv.arb b/lib/l10n/app_sv.arb
index 4d302a5..eab348c 100644
--- a/lib/l10n/app_sv.arb
+++ b/lib/l10n/app_sv.arb
@@ -1,4 +1,12 @@
{
+ "channels_channelDeleteFailed": "Det gick inte att ta bort kanalen \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "sv",
"appTitle": "MeshCore Open",
"nav_contacts": "Kontakter",
@@ -131,9 +139,6 @@
"settings_infoContactsCount": "Kontakterantal",
"settings_infoChannelCount": "Kanalantal",
"settings_presets": "Fördefinierade inställningar",
- "settings_preset915Mhz": "915 MHz",
- "settings_preset868Mhz": "868 MHz",
- "settings_preset433Mhz": "433 MHz",
"settings_frequency": "Frekvens (MHz)",
"settings_frequencyHelper": "300,0 - 2500,0",
"settings_frequencyInvalid": "Ogiltig frekvens (300-2500 MHz)",
@@ -143,8 +148,6 @@
"settings_txPower": "TX-effekt (dBm)",
"settings_txPowerHelper": "0 - 22",
"settings_txPowerInvalid": "Ogiltig TX-effekt (0-22 dBm)",
- "settings_longRange": "Lång räckvidd",
- "settings_fastSpeed": "Snabb hastighet",
"settings_error": "Fel: {message}",
"@settings_error": {
"placeholders": {
@@ -282,6 +285,7 @@
"contacts_newGroup": "Ny grupp",
"contacts_groupName": "Gruppnamn",
"contacts_groupNameRequired": "Gruppnamnet är obligatoriskt",
+ "contacts_groupNameReserved": "Detta gruppnamn är reserverat",
"contacts_groupAlreadyExists": "Gruppen \"{name}\" finns redan.",
"@contacts_groupAlreadyExists": {
"placeholders": {
@@ -339,6 +343,8 @@
"channels_publicChannel": "Allmänt kanal",
"channels_privateChannel": "Privat kanal",
"channels_editChannel": "Redigera kanal",
+ "channels_muteChannel": "Tysta kanal",
+ "channels_unmuteChannel": "Slå på ljud för kanal",
"channels_deleteChannel": "Ta bort kanal",
"channels_deleteChannelConfirm": "Radera \"{name}\"? Detta kan inte ångras.",
"@channels_deleteChannelConfirm": {
@@ -604,6 +610,18 @@
}
}
},
+ "chat_openLink": "Öppna länk?",
+ "chat_openLinkConfirmation": "Vill du öppna den här länken i din webbläsare?",
+ "chat_open": "Öppna",
+ "chat_couldNotOpenLink": "Kunde inte öppna länken: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Ogiltigt länkformat",
"map_title": "Nodkarta",
"map_noNodesWithLocation": "Inga noder med platsinformation",
"map_nodesNeedGps": "Noder måste dela sina GPS-koordinater\nför att visas på kartan",
@@ -1344,12 +1362,12 @@
}
}
},
- "repeater_neighbours": "Grannar",
- "repeater_neighboursSubtitle": "Visa noll hoppgrannar.",
+ "repeater_neighbors": "Grannar",
+ "repeater_neighborsSubtitle": "Visa noll hoppgrannar.",
"neighbors_receivedData": "Mottagna grannars data",
"neighbors_requestTimedOut": "Grannar begär tidsinställd utskick.",
"neighbors_errorLoading": "Fel vid inläsning av grannar: {error}",
- "neighbors_repeatersNeighbours": "Upprepar grannar",
+ "neighbors_repeatersNeighbors": "Upprepar grannar",
"neighbors_noData": "Inga grannuppgifter finns tillgängliga.",
"channels_createPrivateChannel": "Skapa en privat kanal",
"channels_joinPrivateChannel": "Gå med i en Privat Kanal",
@@ -1473,7 +1491,9 @@
"community_deleteChannelsWarning": "Detta kommer också att radera {count} kanal/kanaler och deras meddelanden.",
"@community_deleteChannelsWarning": {
"placeholders": {
- "count": {"type": "int"}
+ "count": {
+ "type": "int"
+ }
}
},
"community_deleted": "Lämnade community \"{name}\"",
@@ -1484,5 +1504,574 @@
"community_regularHashtagDesc": "Offentlig hashtag (alla kan gå med)",
"community_communityHashtagDesc": "Endast för medlemmar",
"community_forCommunity": "För {name}",
- "community_communityHashtag": "Community Hashtag"
+ "community_communityHashtag": "Community Hashtag",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "Regenerera",
+ "community_regenerateSecretConfirm": "Regenerera den hemliga nyckeln för \"{name}\"? Alla medlemmar måste scanna den nya QR-koden för att fortsätta kommunicera.",
+ "community_secretRegenerated": "Lösenord återskapad för \"{name}\"",
+ "community_regenerateSecret": "Regenerera hemlig kod",
+ "community_scanToUpdateSecret": "Skanna den nya QR-koden för att uppdatera hemligheten för \"{name}\"",
+ "community_secretUpdated": "Hemlighet uppdaterad för \"{name}\"",
+ "community_updateSecret": "Uppdatera hemlighet",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Du",
+ "pathTrace_failed": "Sökvägsföljning misslyckades.",
+ "pathTrace_notAvailable": "Path trace ej tillgänglig.",
+ "pathTrace_refreshTooltip": "Uppdatera Path Trace",
+ "contacts_pathTrace": "Path Trace",
+ "contacts_ping": "Ping",
+ "contacts_repeaterPathTrace": "Vägspårning till repeater",
+ "contacts_repeaterPing": "Ping-repeater",
+ "contacts_roomPathTrace": "Vägspårning till rumserver",
+ "contacts_roomPing": "Ping rumsserver",
+ "contacts_chatTraceRoute": "Spåra rutt",
+ "contacts_pathTraceTo": "Spåra rutt till {name}",
+ "contacts_clipboardEmpty": "Urklipp är tomt.",
+ "appSettings_languageRu": "Ryska",
+ "contacts_contactImportFailed": "Kontakt kunde inte importeras.",
+ "contacts_zeroHopAdvert": "Reklam med nollhopp",
+ "contacts_floodAdvert": "Översvämningsannons",
+ "contacts_copyAdvertToClipboard": "Kopiera annons till urklipp",
+ "contacts_invalidAdvertFormat": "Ogiltiga kontaktuppgifter",
+ "appSettings_languageUk": "Ukrainska",
+ "appSettings_enableMessageTracing": "Aktivera meddelandespårning",
+ "appSettings_enableMessageTracingSubtitle": "Visa detaljerade metadata om dirigering och tidsinställningar för meddelanden",
+ "contacts_addContactFromClipboard": "Lägg till kontakt från urklipp",
+ "contacts_contactImported": "Kontakt har importerats.",
+ "contacts_zeroHopContactAdvertSent": "Skickat kontakt via annons.",
+ "contacts_contactAdvertCopied": "Annons kopierad till Urklipp.",
+ "contacts_contactAdvertCopyFailed": "Kopiering av annons till Urklipp misslyckades.",
+ "contacts_ShareContact": "Kopiera kontakt till Urklipp",
+ "contacts_zeroHopContactAdvertFailed": "Misslyckades med att skicka kontakt.",
+ "contacts_ShareContactZeroHop": "Dela kontakt via annons",
+ "notification_activityTitle": "MeshCore Aktivitet",
+ "notification_messagesCount": "{count} {count, plural, =1{meddelande} other{meddelanden}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{kanalmeddelande} other{kanalmeddelanden}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{ny nod} other{nya noder}}",
+ "notification_newTypeDiscovered": "Ny {contactType} upptäckt",
+ "notification_receivedNewMessage": "Nytt meddelande mottaget",
+ "settings_gpxExportAll": "Exportera alla kontakter till GPX",
+ "settings_gpxExportRepeatersSubtitle": "Exporterar repeater / roomserver med plats till GPX-fil.",
+ "settings_gpxExportSuccess": "Har exporterat GPX-fil med framgång",
+ "settings_gpxExportNoContacts": "Inga kontakter att exportera.",
+ "settings_gpxExportNotAvailable": "Stöds inte på din enhet/operativsystem",
+ "settings_gpxExportRepeatersRoom": "Repeater- och rumsserverplatser",
+ "settings_gpxExportRepeaters": "Exportera repeater / rumsservrar till GPX",
+ "settings_gpxExportAllSubtitle": "Exporterar alla kontakter med en plats till GPX-fil.",
+ "settings_gpxExportContacts": "Exportera följeslagare till GPX",
+ "settings_gpxExportContactsSubtitle": "Exporterar följeslagare med en plats till GPX-fil.",
+ "settings_gpxExportChat": "Medhjälparplatser",
+ "settings_gpxExportError": "Det uppstod ett fel när data exporterades.",
+ "settings_gpxExportAllContacts": "Alla kontakters platser",
+ "settings_gpxExportShareSubject": "meshcore-open export av GPX-kartdata",
+ "settings_gpxExportShareText": "Kartdata exporterad från meshcore-open",
+ "pathTrace_someHopsNoLocation": "En eller flera av humlen saknar en plats!",
+ "pathTrace_clearTooltip": "Rensa väg",
+ "map_pathTraceCancelled": "Sökvägsspårning avbruten.",
+ "map_runTrace": "Kör spårsökning",
+ "map_tapToAdd": "Tryck på noder för att lägga till dem i banan.",
+ "map_removeLast": "Ta bort sista",
+ "scanner_enableBluetooth": "Aktivera Bluetooth",
+ "scanner_bluetoothOffMessage": "Vänligen aktivera Bluetooth för att söka efter enheter.",
+ "scanner_chromeRequired": "Chrome-webbläsare krävs",
+ "scanner_chromeRequiredMessage": "Denna webbapplikation kräver Google Chrome oder en Chromium-baserader webbläsare för Bluetooth-stöd.",
+ "scanner_bluetoothOff": "Bluetooth är avstängt",
+ "snrIndicator_lastSeen": "Senast sedd",
+ "snrIndicator_nearByRepeaters": "Närliggande uppreparstationer",
+ "chat_ShowAllPaths": "Visa alla vägar",
+ "settings_clientRepeatSubtitle": "Låt enheten repetera nätpaket för andra användare.",
+ "settings_clientRepeat": "Upprepa utan elnät",
+ "settings_clientRepeatFreqWarning": "För att kunna kommunicera utanför elnätet krävs frekvenserna 433, 869 eller 918 MHz.",
+ "settings_aboutOpenMeteoAttribution": "LOS-höjddata: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "Enheter",
+ "appSettings_unitsMetric": "Metriskt (m/km)",
+ "appSettings_unitsImperial": "Imperialt (ft / mi)",
+ "map_lineOfSight": "Synlinje",
+ "map_losScreenTitle": "Synlinje",
+ "losSelectStartEnd": "Välj start- och slutnoder för LOS.",
+ "losRunFailed": "Synlinjekontroll misslyckades: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Rensa alla punkter",
+ "losRunToViewElevationProfile": "Kör LOS för att se höjdprofil",
+ "losMenuTitle": "LOS-menyn",
+ "losMenuSubtitle": "Tryck på noder eller tryck länge på kartan för anpassade punkter",
+ "losShowDisplayNodes": "Visa displaynoder",
+ "losCustomPoints": "Anpassade poäng",
+ "losCustomPointLabel": "Anpassad {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Punkt A",
+ "losPointB": "Punkt B",
+ "losAntennaA": "Antenn A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Antenn B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Kör LOS",
+ "losNoElevationData": "Inga höjddata",
+ "losProfileClear": "{distance} {distanceUnit}, rensa LOS, min clearance {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, blockerad av {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: kollar...",
+ "losStatusNoData": "LOS: inga data",
+ "losStatusSummary": "LOS: {clear}/{total} rensa, {blocked} blockerad, {unknown} okänd",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Höjddata är inte tillgänglig för ett eller flera prover.",
+ "losErrorInvalidInput": "Ogiltiga poäng/höjddata för LOS-beräkning.",
+ "losRenameCustomPoint": "Byt namn på anpassad punkt",
+ "losPointName": "Punktnamn",
+ "losShowPanelTooltip": "Visa LOS-panelen",
+ "losHidePanelTooltip": "Dölj LOS-panelen",
+ "losElevationAttribution": "Höjddata: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Radiohorisont",
+ "losLegendLosBeam": "Siktlinje",
+ "losLegendTerrain": "Terräng",
+ "losFrequencyLabel": "Frekvens",
+ "losFrequencyInfoTooltip": "Visa detaljer om beräkningen",
+ "losFrequencyDialogTitle": "Beräkning av radiohorisonten",
+ "losFrequencyDialogDescription": "Med start från k={baselineK} vid {baselineFreq} MHz, justerar beräkningen k-faktorn för det aktuella {frequencyMHz} MHz-bandet, som definierar den böjda radiohorisonten.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Ta bort från favoriter",
+ "listFilter_addToFavorites": "Lägg till i favoriter",
+ "listFilter_favorites": "Favoriter",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_unread": "Oläst",
+ "contacts_searchContactsNoNumber": "Sök kontakter...",
+ "contacts_searchRepeaters": "Sök {number}{str} upprepningsenheter...",
+ "contacts_searchFavorites": "Sök {number}{str} Favoriter...",
+ "contacts_searchUsers": "Sök {number}{str} användare...",
+ "contacts_searchRoomServers": "Sök {number}{str} Room-servrar...",
+ "settings_contactSettingsSubtitle": "Inställningar för hur kontakter läggs till.",
+ "settings_contactSettings": "Kontaktinställningar",
+ "contactsSettings_autoAddTitle": "Automatisk upptäckt",
+ "contactsSettings_otherTitle": "Andra inställningar relaterade till kontakt",
+ "contactsSettings_autoAddUsersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta användare",
+ "contactsSettings_autoAddRepeatersTitle": "Lägg till upprepande enheter automatiskt",
+ "contactsSettings_autoAddRoomServersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta rumsservrar.",
+ "contactsSettings_autoAddSensorsTitle": "Lägg till sensorer automatiskt",
+ "contactsSettings_autoAddUsersTitle": "Lägg till användare automatiskt",
+ "contactsSettings_title": "Kontaktinställningar",
+ "contactsSettings_autoAddSensorsSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta sensorer.",
+ "contactsSettings_overwriteOldestTitle": "Skriv över äldst",
+ "contactsSettings_autoAddRepeatersSubtitle": "Tillåt kompanjonen att automatiskt lägga till upptäckta repeater.",
+ "contactsSettings_autoAddRoomServersTitle": "Lägg automatiskt till rumsservrar",
+ "discoveredContacts_noMatching": "Inga matchande kontakter",
+ "discoveredContacts_searchHint": "Sök uppfunna kontakter",
+ "discoveredContacts_deleteContact": "Ta bort kontakt",
+ "discoveredContacts_Title": "Upptäckta kontakter",
+ "discoveredContacts_contactAdded": "Kontakt tillagd",
+ "discoveredContacts_addContact": "Lägg till kontakt",
+ "discoveredContacts_copyContact": "Kopiera kontakt till urklipp",
+ "contactsSettings_overwriteOldestSubtitle": "När kontaktlistan är full ersätts den äldsta icke-favoriterade kontakten.",
+ "common_deleteAll": "Ta bort alla",
+ "discoveredContacts_deleteContactAllContent": "Är du säker på att du vill ta bort alla upptäckta kontakter?",
+ "discoveredContacts_deleteContactAll": "Ta bort alla upptäckta kontakter",
+ "map_guessedLocation": "Gissad plats",
+ "map_showGuessedLocations": "Visa upp de antagna nodernas placeringar",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenSubtitle": "Välj en detekterad seriell enhet och anslut direkt till din MeshCore-nod.",
+ "usbScreenTitle": "Anslut via USB",
+ "usbScreenStatus": "Välj en USB-enhet",
+ "usbScreenNote": "USB-seriell kommunikation är aktiv på stödda Android-enheter och på skrivbordsplattformar.",
+ "usbScreenEmptyState": "Inga USB-enheter hittades. Anslut en och uppdatera.",
+ "usbErrorPermissionDenied": "Tillgången via USB nekas.",
+ "usbErrorDeviceMissing": "Den valda USB-enheten är inte längre tillgänglig.",
+ "usbErrorInvalidPort": "Välj en giltig USB-enhet.",
+ "usbErrorBusy": "En annan förfrågan om USB-anslutning är redan pågående.",
+ "usbErrorNotConnected": "Ingen USB-enhet är ansluten.",
+ "usbErrorOpenFailed": "Misslyckades med att öppna det valda USB-enheten.",
+ "usbErrorConnectFailed": "Kunde inte ansluta till det valda USB-enheten.",
+ "usbErrorUnsupported": "USB-seriell kommunikation stöds inte på denna plattform.",
+ "usbErrorAlreadyActive": "En USB-anslutning är redan aktiv.",
+ "usbErrorNoDeviceSelected": "Ingen USB-enhet valdes.",
+ "usbErrorPortClosed": "USB-anslutningen är inte aktiv.",
+ "usbFallbackDeviceName": "Web-serieenhet",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_connecting": "Anslutning till USB-enhet...",
+ "usbStatus_notConnected": "Välj en USB-enhet",
+ "usbConnectionFailed": "Fel vid USB-anslutning: {error}",
+ "usbStatus_searching": "Söker efter USB-enheter...",
+ "usbErrorConnectTimedOut": "Anslutningen har tidsutgått. Se till att enheten har rätt USB-firmware.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostHint": "192.168.40.10",
+ "tcpHostLabel": "IP-adress",
+ "tcpScreenTitle": "Anslut via TCP",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpPortLabel": "Port",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Ange slutpunkt och anslut",
+ "tcpStatus_connectingTo": "Anslutning till {endpoint}...",
+ "tcpErrorHostRequired": "IP-adress krävs.",
+ "tcpErrorPortInvalid": "Porten måste vara mellan 1 och 65535.",
+ "tcpErrorUnsupported": "TCP-transport fungerar inte på denna plattform.",
+ "tcpErrorTimedOut": "TCP-anslutningen har tidsut gått.",
+ "tcpConnectionFailed": "Fel vid TCP-anslutning: {error}",
+ "map_showDiscoveryContacts": "Visa Discovery-kontakter",
+ "map_setAsMyLocation": "Ange som min plats",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacy": "Inställningar för sekretess",
+ "settings_allowAll": "Tillåt alla",
+ "settings_privacySubtitle": "Kontrollera vilken information som delas.",
+ "settings_telemetryEnvironmentMode": "Telemetri miljöläge",
+ "settings_telemetryBaseMode": "Telemetribasläge",
+ "settings_telemetryLocationMode": "Telemetritillstånd för plats",
+ "settings_advertLocation": "Annonsplacering",
+ "contact_info": "Kontaktinformation",
+ "contact_settings": "Kontaktinställningar",
+ "contact_telemetry": "Telemetri",
+ "settings_denyAll": "Neka alla",
+ "settings_allowByContact": "Tillåt via kontaktflaggor",
+ "settings_privacySettingsDescription": "Välj vilken information din enhet delar med andra.",
+ "contact_lastSeen": "Senast sedd",
+ "contact_clearChat": "Rensa Chatt",
+ "contact_teleEnv": "Telemetri Miljö",
+ "settings_advertLocationSubtitle": "Inkludera plats i annonsen",
+ "contact_teleEnvSubtitle": "Tillåt delning av miljösensordata",
+ "contact_teleBase": "Telemetribas",
+ "contact_teleBaseSubtitle": "Tillåt delning av batterinivå och grundläggande telemetri",
+ "contact_teleLoc": "Telemetridata plats",
+ "contact_teleLocSubtitle": "Tillåt delning av platsdata",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeightSubtitle": "Initial vikt för nyligen upptäckta vägar",
+ "appSettings_maxRouteWeight": "Maximalt tillåtet vikt för rutten",
+ "appSettings_maxRouteWeightSubtitle": "Maximal vikt som en leveransväg kan ackumulera från framgångsrika leveranser.",
+ "appSettings_initialRouteWeight": "Initial vikt för rutt",
+ "appSettings_routeWeightSuccessIncrement": "Ökning av vikt för framgång",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Vikt läggs till en väg efter en lyckad leverans.",
+ "appSettings_routeWeightFailureDecrement": "Minskning av vikten för misslyckande",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Vikt som tagits bort från en väg efter ett misslyckat leveransförsök",
+ "appSettings_maxMessageRetries": "Maximalt antal försök",
+ "appSettings_maxMessageRetriesSubtitle": "Antal försök att skicka om ett meddelande innan det markeras som misslyckat.",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Telemetri-läge uppdaterat",
+ "settings_multiAck": "Multi-ACKs: {value}",
+ "map_showOverlaps": "Repeater-nyckelöverlappningar",
+ "map_runTraceWithReturnPath": "Gå tillbaka på samma väg",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_jumpToOldestUnreadSubtitle": "När du öppnar en chatt med oinlästa meddelanden, scrolla till det första oinlästa meddelandet istället för det senaste.",
+ "chat_sendCooldown": "Vänligen vänta en stund innan du skickar igen.",
+ "appSettings_jumpToOldestUnread": "Gå direkt till det äldsta, obesvarade meddelandet",
+ "appSettings_languageHu": "Ungerskt",
+ "appSettings_languageJa": "Japanska",
+ "appSettings_languageKo": "Koreanska",
+ "radioStats_tooltip": "Radio- och mesh-statistik",
+ "radioStats_screenTitle": "Radiostation",
+ "radioStats_notConnected": "Anslut till en enhet för att visa radiostatistik.",
+ "radioStats_firmwareTooOld": "Radio statistik kräver kompatibel firmware version 8 eller senare.",
+ "radioStats_waiting": "Väntar på data…",
+ "radioStats_noiseFloor": "Bakgrundsnivå: {noiseDbm} dBm",
+ "radioStats_lastRssi": "Senaste RSSI-värde: {rssiDbm} dBm",
+ "radioStats_lastSnr": "Senaste SNR: {snr} dB",
+ "radioStats_txAir": "TX-tid (total): {seconds} sekunder",
+ "radioStats_rxAir": "RX-tid (total): {seconds} s",
+ "radioStats_chartCaption": "Ljudnivå (dBm) baserat på de senaste mätningarna.",
+ "radioStats_stripNoise": "Bakgrundsnivå: {noiseDbm} dBm",
+ "radioStats_stripWaiting": "Hämtar radiostatistik…",
+ "radioStats_settingsTile": "Radiostation",
+ "radioStats_settingsSubtitle": "Bakgrundsnivå, RSSI, SNR och tillgänglig tid",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_enableSubtitle": "Översätt inkommande meddelanden och möjliggör översättning före avsändning.",
+ "translation_enableTitle": "Aktivera översättning",
+ "translation_title": "Översättning",
+ "translation_composerTitle": "Översätt innan du skickar",
+ "translation_composerSubtitle": "Styr standardtillståndet för kompositorns översättningsikon.",
+ "translation_targetLanguage": "Målmedvetet språk",
+ "translation_useAppLanguage": "Använd appens språk",
+ "translation_downloadedModelLabel": "Nedladdad modell",
+ "translation_presetModelLabel": "Fördefinierat Hugging Face-modell",
+ "translation_manualUrlLabel": "Manualens URL",
+ "translation_downloadModel": "Ladda ner modellen",
+ "translation_downloading": "Nedladdning...",
+ "translation_working": "Arbeta...",
+ "translation_stop": "Stopp",
+ "translation_mergingChunks": "Slå samman de nedladdade delarna till en slutlig fil...",
+ "translation_downloadedModels": "Nedladdade modeller",
+ "translation_deleteModel": "Ta bort modell",
+ "translation_modelDownloaded": "Översättningsmodellen har laddats ner.",
+ "translation_downloadStopped": "Nedladdningen avbruten.",
+ "translation_downloadFailed": "Nedladdning misslyckades: {error}",
+ "translation_enterUrlFirst": "Ange först en URL för en specifik modell.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "Skicka meddelanden på det ursprungliga, stavade språket.",
+ "translation_translateBeforeSending": "Översätt innan du skickar",
+ "translation_composerEnabledHint": "Meddelandena kommer att översättas innan de skickas.",
+ "translation_messageTranslation": "Meddelandets översättning",
+ "translation_translateTo": "Översätt till {language}",
+ "translation_translationOptions": "Översättningsalternativ",
+ "translation_systemLanguage": "Språk för systemet",
+ "scanner_linuxPairingShowPin": "Visa PIN",
+ "scanner_linuxPairingPinTitle": "Bluetooth‑parnings‑PIN",
+ "scanner_linuxPairingPinPrompt": "Ange PIN för {deviceName} (lämna tomt om ingen).",
+ "scanner_linuxPairingHidePin": "Dölj PIN",
+ "repeater_cliQuickDiscovery": "Upptäck grannar",
+ "repeater_cliQuickClockSync": "Synkronisera klocka",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Automatiskt skicka \"klocksynkronisering\" efter en lyckad inloggning.",
+ "repeater_clockSyncAfterLogin": "Synkronisera klockan efter inloggning",
+ "repeater_guest": "Information om repetorer",
+ "chat_sendMessage": "Skicka meddelande",
+ "repeater_guestTools": "Gästverktyg",
+ "room_guest": "Information om servern"
}
diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb
new file mode 100644
index 0000000..a005b36
--- /dev/null
+++ b/lib/l10n/app_uk.arb
@@ -0,0 +1,2077 @@
+{
+ "channels_channelDeleteFailed": "Не вдалося видалити канал \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@@locale": "uk",
+ "appTitle": "MeshCore Open",
+ "nav_contacts": "Контакти",
+ "nav_channels": "Канали",
+ "nav_map": "Карта",
+ "common_cancel": "Скасувати",
+ "common_connect": "Підключити",
+ "common_unknownDevice": "Невідомий пристрій",
+ "common_save": "Зберегти",
+ "common_delete": "Видалити",
+ "common_close": "Закрити",
+ "common_edit": "Редагувати",
+ "common_add": "Додати",
+ "common_settings": "Налаштування",
+ "common_disconnect": "Відключити",
+ "common_connected": "Підключено",
+ "common_disconnected": "Відключено",
+ "common_create": "Створити",
+ "common_continue": "Продовжити",
+ "common_share": "Поділитися",
+ "common_copy": "Копіювати",
+ "common_retry": "Повторити",
+ "common_hide": "Приховати",
+ "common_remove": "Прибрати",
+ "common_enable": "Увімкнути",
+ "common_disable": "Вимкнути",
+ "common_reboot": "Перезавантажити",
+ "common_loading": "Завантаження...",
+ "common_notAvailable": "—",
+ "common_voltageValue": "{volts} В",
+ "@common_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "common_percentValue": "{percent}%",
+ "@common_percentValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ }
+ }
+ },
+ "scanner_title": "MeshCore Open",
+ "scanner_scanning": "Пошук пристроїв...",
+ "scanner_connecting": "Підключення...",
+ "scanner_disconnecting": "Відключення...",
+ "scanner_notConnected": "Не підключено",
+ "scanner_connectedTo": "Підключено до {deviceName}",
+ "@scanner_connectedTo": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_searchingDevices": "Пошук пристроїв MeshCore...",
+ "scanner_tapToScan": "Натисніть «Сканувати», щоб знайти пристрої MeshCore",
+ "scanner_connectionFailed": "Помилка підключення: {error}",
+ "@scanner_connectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_stop": "Стоп",
+ "scanner_scan": "Сканувати",
+ "device_quickSwitch": "Швидке перемикання",
+ "device_meshcore": "MeshCore",
+ "settings_title": "Налаштування",
+ "settings_deviceInfo": "Інформація про пристрій",
+ "settings_appSettings": "Налаштування програми",
+ "settings_appSettingsSubtitle": "Сповіщення, повідомлення та налаштування карти",
+ "settings_nodeSettings": "Налаштування вузла",
+ "settings_nodeName": "Ім'я вузла",
+ "settings_nodeNameNotSet": "Не встановлено",
+ "settings_nodeNameHint": "Введіть ім'я вузла",
+ "settings_nodeNameUpdated": "Ім'я оновлено",
+ "settings_radioSettings": "Налаштування радіо",
+ "settings_radioSettingsSubtitle": "Частота, потужність, коефіцієнт розширення",
+ "settings_radioSettingsUpdated": "Налаштування радіо оновлено",
+ "settings_location": "Розташування",
+ "settings_locationSubtitle": "GPS координати",
+ "settings_locationUpdated": "Розташування оновлено",
+ "settings_locationBothRequired": "Введіть широту та довготу.",
+ "settings_locationInvalid": "Некоректна широта або довгота.",
+ "settings_latitude": "Широта",
+ "settings_longitude": "Довгота",
+ "settings_privacyMode": "Режим приватності",
+ "settings_privacyModeSubtitle": "Приховати ім'я/розташування в оголошеннях",
+ "settings_privacyModeToggle": "Увімкніть режим приватності, щоб приховати своє ім'я та місцезнаходження в оголошеннях.",
+ "settings_privacyModeEnabled": "Режим приватності увімкнено",
+ "settings_privacyModeDisabled": "Режим приватності вимкнено",
+ "settings_actions": "Дії",
+ "settings_sendAdvertisement": "Оголосити себе",
+ "settings_sendAdvertisementSubtitle": "Транслювати присутність зараз",
+ "settings_advertisementSent": "Оголошення надіслано",
+ "settings_syncTime": "Синхронізація часу",
+ "settings_syncTimeSubtitle": "Встановити час пристрою відповідно до часу телефону.",
+ "settings_timeSynchronized": "Час синхронізовано",
+ "settings_refreshContacts": "Оновити контакти",
+ "settings_refreshContactsSubtitle": "Перезавантажити список контактів з пристрою",
+ "settings_rebootDevice": "Перезавантажити пристрій",
+ "settings_rebootDeviceSubtitle": "Перезавантажити пристрій MeshCore",
+ "settings_rebootDeviceConfirm": "Ви впевнені, що хочете перезавантажити пристрій? Вас буде відключено.",
+ "settings_debug": "Налагодження",
+ "settings_bleDebugLog": "Журнал налагодження BLE",
+ "settings_bleDebugLogSubtitle": "Команди BLE, відповіді та необроблені дані",
+ "settings_appDebugLog": "Журнал налагодження програми",
+ "settings_appDebugLogSubtitle": "Повідомлення налагодження програми",
+ "settings_about": "Про програму",
+ "settings_aboutVersion": "MeshCore Open v{version}",
+ "@settings_aboutVersion": {
+ "placeholders": {
+ "version": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_aboutLegalese": "Проєкт MeshCore Open Source 2026",
+ "settings_aboutDescription": "Клієнт Flutter з відкритим вихідним кодом для пристроїв мережі MeshCore LoRa.",
+ "settings_infoName": "Ім'я",
+ "settings_infoId": "ID",
+ "settings_infoStatus": "Статус",
+ "settings_infoBattery": "Батарея",
+ "settings_infoPublicKey": "Відкритий ключ",
+ "settings_infoContactsCount": "Кількість контактів",
+ "settings_infoChannelCount": "Кількість каналів",
+ "settings_presets": "Попередні налаштування",
+ "settings_frequency": "Частота (МГц)",
+ "settings_frequencyHelper": "300.0 - 2500.0",
+ "settings_frequencyInvalid": "Некоректна частота (300-2500 МГц)",
+ "settings_bandwidth": "Смуга пропускання",
+ "settings_spreadingFactor": "Коефіцієнт розширення",
+ "settings_codingRate": "Швидкість кодування",
+ "settings_txPower": "Потужність TX (дБм)",
+ "settings_txPowerHelper": "0 - 22",
+ "settings_txPowerInvalid": "Некоректна потужність TX (0-22 дБм)",
+ "settings_error": "Помилка: {message}",
+ "@settings_error": {
+ "placeholders": {
+ "message": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_title": "Налаштування програми",
+ "appSettings_appearance": "Вигляд",
+ "appSettings_theme": "Тема",
+ "appSettings_themeSystem": "Системна",
+ "appSettings_themeLight": "Світла",
+ "appSettings_themeDark": "Темна",
+ "appSettings_language": "Мова",
+ "appSettings_languageSystem": "Як у системі",
+ "appSettings_languageEn": "English",
+ "appSettings_languageFr": "Français",
+ "appSettings_languageEs": "Español",
+ "appSettings_languageDe": "Deutsch",
+ "appSettings_languagePl": "Polski",
+ "appSettings_languageSl": "Slovenščina",
+ "appSettings_languagePt": "Português",
+ "appSettings_languageIt": "Italiano",
+ "appSettings_languageZh": "中文",
+ "appSettings_languageSv": "Svenska",
+ "appSettings_languageNl": "Nederlands",
+ "appSettings_languageSk": "Slovenčina",
+ "appSettings_languageBg": "Български",
+ "appSettings_languageUk": "Українська",
+ "appSettings_notifications": "Сповіщення",
+ "appSettings_enableNotifications": "Увімкнути сповіщення",
+ "appSettings_enableNotificationsSubtitle": "Отримувати сповіщення про повідомлення та оголошення",
+ "appSettings_notificationPermissionDenied": "У доступі до сповіщень відмовлено",
+ "appSettings_notificationsEnabled": "Сповіщення увімкнено",
+ "appSettings_notificationsDisabled": "Сповіщення вимкнено",
+ "appSettings_messageNotifications": "Сповіщення про повідомлення",
+ "appSettings_messageNotificationsSubtitle": "Показувати сповіщення при отриманні нових повідомлень",
+ "appSettings_channelMessageNotifications": "Сповіщення каналів",
+ "appSettings_channelMessageNotificationsSubtitle": "Показувати сповіщення при отриманні повідомлень каналу",
+ "appSettings_advertisementNotifications": "Сповіщення про оголошення",
+ "appSettings_advertisementNotificationsSubtitle": "Показувати сповіщення при виявленні нових вузлів",
+ "appSettings_messaging": "Обмін повідомленнями",
+ "appSettings_clearPathOnMaxRetry": "Очищати шлях після макс. спроб",
+ "appSettings_clearPathOnMaxRetrySubtitle": "Скидати шлях до контакту після 5 невдалих спроб надсилання",
+ "appSettings_pathsWillBeCleared": "Шляхи будуть очищені після 5 невдалих спроб.",
+ "appSettings_pathsWillNotBeCleared": "Шляхи не будуть очищатися автоматично.",
+ "appSettings_autoRouteRotation": "Авторотація маршруту",
+ "appSettings_autoRouteRotationSubtitle": "Чергувати найкращі шляхи та режим «на всю мережу» (flood)",
+ "appSettings_autoRouteRotationEnabled": "Авторотація маршрутизації увімкнена",
+ "appSettings_autoRouteRotationDisabled": "Авторотація маршрутизації вимкнена",
+ "appSettings_battery": "Батарея",
+ "appSettings_batteryChemistry": "Хімія батареї",
+ "appSettings_batteryChemistryPerDevice": "Встановити для пристрою ({deviceName})",
+ "@appSettings_batteryChemistryPerDevice": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_batteryChemistryConnectFirst": "Підключіть пристрій, щоб вибрати",
+ "appSettings_batteryNmc": "18650 NMC (3.0-4.2В)",
+ "appSettings_batteryLifepo4": "LiFePO4 (2.6-3.65В)",
+ "appSettings_batteryLipo": "LiPo (3.0-4.2В)",
+ "appSettings_mapDisplay": "Відображення карти",
+ "appSettings_showRepeaters": "Показувати ретранслятори",
+ "appSettings_showRepeatersSubtitle": "Відображати вузли-ретранслятори на карті",
+ "appSettings_showChatNodes": "Показувати вузли чату",
+ "appSettings_showChatNodesSubtitle": "Відображати вузли чату на карті",
+ "appSettings_showOtherNodes": "Показувати інші вузли",
+ "appSettings_showOtherNodesSubtitle": "Відображати інші типи вузлів на карті",
+ "appSettings_timeFilter": "Фільтр часу",
+ "appSettings_timeFilterShowAll": "Показати всі вузли",
+ "appSettings_timeFilterShowLast": "Показати вузли за останні {hours} год",
+ "@appSettings_timeFilterShowLast": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_mapTimeFilter": "Фільтр часу карти",
+ "appSettings_showNodesDiscoveredWithin": "Показувати вузли, виявлені за:",
+ "appSettings_allTime": "Весь час",
+ "appSettings_lastHour": "Останню годину",
+ "appSettings_last6Hours": "Останні 6 годин",
+ "appSettings_last24Hours": "Останні 24 години",
+ "appSettings_lastWeek": "Минулий тиждень",
+ "appSettings_offlineMapCache": "Офлайн-кеш карти",
+ "appSettings_noAreaSelected": "Область не вибрано",
+ "appSettings_areaSelectedZoom": "Вибрана область (зум {minZoom}-{maxZoom})",
+ "@appSettings_areaSelectedZoom": {
+ "placeholders": {
+ "minZoom": {
+ "type": "int"
+ },
+ "maxZoom": {
+ "type": "int"
+ }
+ }
+ },
+ "appSettings_debugCard": "Налагодження",
+ "appSettings_appDebugLogging": "Логування налагодження програми",
+ "appSettings_appDebugLoggingSubtitle": "Записувати повідомлення налагодження програми в лог для усунення несправностей.",
+ "appSettings_appDebugLoggingEnabled": "Логування налагодження програми увімкнено",
+ "appSettings_appDebugLoggingDisabled": "Налагодження програми вимкнено.",
+ "contacts_title": "Контакти",
+ "contacts_noContacts": "Контактів не знайдено.",
+ "contacts_contactsWillAppear": "Контакти з'являться, коли пристрої надішлють оголошення.",
+ "contacts_searchContacts": "Пошук контактів...",
+ "contacts_noUnreadContacts": "Немає непрочитаних контактів",
+ "contacts_noContactsFound": "Контактів або груп не знайдено.",
+ "contacts_deleteContact": "Видалити контакт",
+ "contacts_removeConfirm": "Видалити {contactName} з контактів?",
+ "@contacts_removeConfirm": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_manageRepeater": "Керувати ретранслятором",
+ "contacts_roomLogin": "Вхід у кімнату",
+ "contacts_openChat": "Відкрити чат",
+ "contacts_editGroup": "Редагувати групу",
+ "contacts_deleteGroup": "Видалити групу",
+ "contacts_deleteGroupConfirm": "Видалити {groupName}?",
+ "@contacts_deleteGroupConfirm": {
+ "placeholders": {
+ "groupName": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_newGroup": "Нова група",
+ "contacts_groupName": "Назва групи",
+ "contacts_groupNameRequired": "Назва групи обов'язкова.",
+ "contacts_groupNameReserved": "Ця назва групи зарезервована",
+ "contacts_groupAlreadyExists": "Група «{name}» вже існує.",
+ "@contacts_groupAlreadyExists": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_filterContacts": "Фільтрувати контакти...",
+ "contacts_noContactsMatchFilter": "Жоден контакт не відповідає фільтру.",
+ "contacts_noMembers": "Немає учасників",
+ "contacts_lastSeenNow": "В мережі",
+ "contacts_lastSeenMinsAgo": "В мережі {minutes} хв. тому",
+ "@contacts_lastSeenMinsAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenHourAgo": "В мережі 1 годину тому",
+ "contacts_lastSeenHoursAgo": "В мережі {hours} год. тому",
+ "@contacts_lastSeenHoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "contacts_lastSeenDayAgo": "В мережі 1 день тому",
+ "contacts_lastSeenDaysAgo": "В мережі {days} дн. тому",
+ "@contacts_lastSeenDaysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_title": "Канали",
+ "channels_noChannelsConfigured": "Канали не налаштовані",
+ "channels_addPublicChannel": "Додати публічний канал",
+ "channels_searchChannels": "Пошук каналів...",
+ "channels_noChannelsFound": "Каналів не знайдено",
+ "channels_channelIndex": "Канал {index}",
+ "@channels_channelIndex": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_hashtagChannel": "Канал з хештегом",
+ "channels_public": "Публічний",
+ "channels_private": "Приватний",
+ "channels_publicChannel": "Публічний канал",
+ "channels_privateChannel": "Приватний канал",
+ "channels_editChannel": "Редагувати канал",
+ "channels_muteChannel": "Вимкнути сповіщення каналу",
+ "channels_unmuteChannel": "Увімкнути сповіщення каналу",
+ "channels_deleteChannel": "Видалити канал",
+ "channels_deleteChannelConfirm": "Видалити {name}? Це не можна скасувати.",
+ "@channels_deleteChannelConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_channelDeleted": "Канал «{name}» видалено",
+ "@channels_channelDeleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_addChannel": "Додати канал",
+ "channels_channelIndexLabel": "Індекс каналу",
+ "channels_channelName": "Назва каналу",
+ "channels_usePublicChannel": "Використовувати публічний канал",
+ "channels_standardPublicPsk": "Стандартний публічний PSK",
+ "channels_pskHex": "PSK (Hex)",
+ "channels_generateRandomPsk": "Згенерувати випадковий ключ PSK",
+ "channels_enterChannelName": "Будь ласка, введіть назву каналу",
+ "channels_pskMustBe32Hex": "PSK має складатися з 32 шістнадцяткових символів.",
+ "channels_channelAdded": "Канал «{name}» додано",
+ "@channels_channelAdded": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_editChannelTitle": "Редагувати канал {index}",
+ "@channels_editChannelTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channels_smazCompression": "Стиснення SMAZ",
+ "channels_channelUpdated": "Канал «{name}» оновлено",
+ "@channels_channelUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "channels_publicChannelAdded": "Публічний канал додано",
+ "channels_sortBy": "Сортувати за",
+ "channels_sortManual": "Вручну",
+ "channels_sortAZ": "А-Я",
+ "channels_sortLatestMessages": "Останні повідомлення",
+ "channels_sortUnread": "Непрочитані",
+ "chat_noMessages": "Поки немає повідомлень.",
+ "chat_sendMessageToStart": "Надішліть повідомлення, щоб почати",
+ "chat_originalMessageNotFound": "Оригінальне повідомлення не знайдено",
+ "chat_replyingTo": "Відповідь {name}",
+ "@chat_replyingTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_replyTo": "Відповісти {name}",
+ "@chat_replyTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_location": "Розташування",
+ "chat_sendMessageTo": "Надіслати повідомлення {contactName}",
+ "@chat_sendMessageTo": {
+ "placeholders": {
+ "contactName": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_typeMessage": "Введіть повідомлення...",
+ "chat_messageTooLong": "Повідомлення занадто довге (макс. {maxBytes} байт).",
+ "@chat_messageTooLong": {
+ "placeholders": {
+ "maxBytes": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_messageCopied": "Повідомлення скопійовано",
+ "chat_messageDeleted": "Повідомлення видалено",
+ "chat_retryingMessage": "Спроба відновлення.",
+ "chat_retryCount": "Повторна спроба {current}/{max}",
+ "@chat_retryCount": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendGif": "Надіслати GIF",
+ "chat_reply": "Відповісти",
+ "chat_addReaction": "Додати реакцію",
+ "chat_me": "Я",
+ "emojiCategorySmileys": "Емодзі",
+ "emojiCategoryGestures": "Жести",
+ "emojiCategoryHearts": "Серця",
+ "emojiCategoryObjects": "Об'єкти",
+ "gifPicker_title": "Вибрати GIF",
+ "gifPicker_searchHint": "Пошук GIF...",
+ "gifPicker_poweredBy": "На базі GIPHY",
+ "gifPicker_noGifsFound": "GIF не знайдено",
+ "gifPicker_failedLoad": "Не вдалося завантажити GIF-файли",
+ "gifPicker_failedSearch": "Пошук GIF не вдався",
+ "gifPicker_noInternet": "Немає інтернет-з'єднання",
+ "debugLog_appTitle": "Журнал налагодження програми",
+ "debugLog_bleTitle": "Журнал налагодження BLE",
+ "debugLog_copyLog": "Копіювати журнал",
+ "debugLog_clearLog": "Очистити журнал",
+ "debugLog_copied": "Журнал налагодження скопійовано",
+ "debugLog_bleCopied": "Журнал BLE скопійовано",
+ "debugLog_noEntries": "Поки що немає записів журналу налагодження.",
+ "debugLog_enableInSettings": "Увімкніть налагодження програми в налаштуваннях",
+ "debugLog_frames": "Кадри",
+ "debugLog_rawLogRx": "Необроблений лог - RX",
+ "debugLog_noBleActivity": "Поки що немає активності BLE.",
+ "debugFrame_length": "Довжина кадру: {count} байт",
+ "@debugFrame_length": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_command": "Команда: 0x{value}",
+ "@debugFrame_command": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textMessageHeader": "Повідомлення:",
+ "debugFrame_destinationPubKey": "- PubKey призначення: {pubKey}",
+ "@debugFrame_destinationPubKey": {
+ "placeholders": {
+ "pubKey": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_timestamp": "- Мітка часу: {timestamp}",
+ "@debugFrame_timestamp": {
+ "placeholders": {
+ "timestamp": {
+ "type": "int"
+ }
+ }
+ },
+ "debugFrame_flags": "- Прапорці: 0x{value}",
+ "@debugFrame_flags": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textType": "- Тип тексту: {type} ({label})",
+ "@debugFrame_textType": {
+ "placeholders": {
+ "type": {
+ "type": "int"
+ },
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_textTypeCli": "CLI",
+ "debugFrame_textTypePlain": "Звичайний",
+ "debugFrame_text": "- Текст: \"{text}\"",
+ "@debugFrame_text": {
+ "placeholders": {
+ "text": {
+ "type": "String"
+ }
+ }
+ },
+ "debugFrame_hexDump": "Дамп Hex:",
+ "chat_pathManagement": "Керування шляхами",
+ "chat_routingMode": "Режим маршрутизації",
+ "chat_autoUseSavedPath": "Авто (використовувати збережений шлях)",
+ "chat_forceFloodMode": "Примусово на всю мережу",
+ "chat_recentAckPaths": "Недавні шляхи ACK (натисніть, щоб використати):",
+ "chat_pathHistoryFull": "Історія шляхів заповнена. Видаліть записи, щоб додати нові.",
+ "chat_hopSingular": "Стрибок",
+ "chat_hopPlural": "стрибків",
+ "chat_hopsCount": "{count} {count, plural, =1{стрибок} few{стрибки} many{стрибків} other{стрибків}}",
+ "@chat_hopsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_successes": "Успішно",
+ "chat_removePath": "Видалити шлях",
+ "chat_noPathHistoryYet": "Історія шляхів недоступна.\nНадішліть повідомлення, щоб виявити шляхи.",
+ "chat_pathActions": "Дії зі шляхом:",
+ "chat_setCustomPath": "Встановити власний шлях",
+ "chat_setCustomPathSubtitle": "Вказати шлях маршрутизації вручну",
+ "chat_clearPath": "Очистити шлях",
+ "chat_clearPathSubtitle": "Примусово повторити пошук при наступному надсиланні",
+ "chat_pathCleared": "Шлях очищено. Наступне повідомлення оновить маршрут.",
+ "chat_floodModeSubtitle": "Використовувати перемикач маршрутизації в панелі програми",
+ "chat_floodModeEnabled": "Увімкнено режим «на всю мережу». Перемикайте через іконку маршрутизації на панелі інструментів.",
+ "chat_fullPath": "Повний шлях",
+ "chat_pathDetailsNotAvailable": "Деталі шляху ще недоступні. Спробуйте надіслати повідомлення для оновлення.",
+ "chat_pathSetHops": "Шлях встановлено: {hopCount} {hopCount, plural, =1{стрибок} few{стрибки} many{стрибків} other{стрибків}} - {status}",
+ "@chat_pathSetHops": {
+ "placeholders": {
+ "hopCount": {
+ "type": "int"
+ },
+ "status": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_pathSavedLocally": "Збережено локально. Підключіться для синхронізації.",
+ "chat_pathDeviceConfirmed": "Пристрій підтверджено.",
+ "chat_pathDeviceNotConfirmed": "Пристрій ще не підтверджено.",
+ "chat_type": "Ввід",
+ "chat_path": "Шлях",
+ "chat_publicKey": "Відкритий ключ",
+ "chat_compressOutgoingMessages": "Стискати вихідні повідомлення",
+ "chat_floodForced": "На всю мережу (примусово)",
+ "chat_directForced": "Прямий (примусово)",
+ "chat_hopsForced": "{count} стрибків (примусово)",
+ "@chat_hopsForced": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_floodAuto": "На всю мережу (авто)",
+ "chat_direct": "Прямий",
+ "chat_poiShared": "Точкою інтересу поділилися",
+ "chat_unread": "Непрочитано: {count}",
+ "@chat_unread": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_openLink": "Відкрити посилання?",
+ "chat_openLinkConfirmation": "Ви хочете відкрити це посилання у браузері?",
+ "chat_open": "Відкрити",
+ "chat_couldNotOpenLink": "Не вдалося відкрити посилання: {url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "Невірний формат посилання",
+ "map_title": "Карта вузлів",
+ "map_noNodesWithLocation": "Немає вузлів з даними про розташування",
+ "map_nodesNeedGps": "Вузли повинні надавати свої GPS координати,\nщоб з'явитися на карті.",
+ "map_nodesCount": "Вузли: {count}",
+ "@map_nodesCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_pinsCount": "Мітки: {count}",
+ "@map_pinsCount": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "map_chat": "Чат",
+ "map_repeater": "Ретранслятор",
+ "map_room": "Кімната",
+ "map_sensor": "Сенсор",
+ "map_pinDm": "Ключ (DM)",
+ "map_pinPrivate": "Замок (Приватний)",
+ "map_pinPublic": "Ключ (Публічний)",
+ "map_lastSeen": "Останній раз бачили",
+ "map_disconnectConfirm": "Ви впевнені, що хочете відключитися від цього пристрою?",
+ "map_from": "Від",
+ "map_source": "Джерело",
+ "map_flags": "Прапорці",
+ "map_shareMarkerHere": "Поділитися маркером тут",
+ "map_pinLabel": "Мітка піна",
+ "map_label": "Мітка",
+ "map_pointOfInterest": "Точка інтересу",
+ "map_sendToContact": "Надіслати контакту",
+ "map_sendToChannel": "Надіслати в канал",
+ "map_noChannelsAvailable": "Немає доступних каналів",
+ "map_publicLocationShare": "Поділитися в публічному місці",
+ "map_publicLocationShareConfirm": "Ви збираєтеся поділитися розташуванням у {channelLabel}. Цей канал публічний, і кожен, хто має ключ PSK, може це побачити.",
+ "@map_publicLocationShareConfirm": {
+ "placeholders": {
+ "channelLabel": {
+ "type": "String"
+ }
+ }
+ },
+ "map_connectToShareMarkers": "Підключіться до пристрою, щоб поділитися маркерами",
+ "map_filterNodes": "Фільтрувати вузли",
+ "map_nodeTypes": "Типи вузлів",
+ "map_chatNodes": "Вузли чату",
+ "map_repeaters": "Ретранслятори",
+ "map_otherNodes": "Інші вузли",
+ "map_keyPrefix": "Префікс ключа",
+ "map_filterByKeyPrefix": "Фільтрувати за префіксом ключа",
+ "map_publicKeyPrefix": "Префікс відкритого ключа",
+ "map_markers": "Маркери",
+ "map_showSharedMarkers": "Показувати спільні маркери",
+ "map_lastSeenTime": "Час останньої активності",
+ "map_sharedPin": "Спільний пін",
+ "map_joinRoom": "Приєднатися до кімнати",
+ "map_manageRepeater": "Керувати ретранслятором",
+ "mapCache_title": "Офлайн-кеш карти",
+ "mapCache_selectAreaFirst": "Спершу виберіть область для кешування",
+ "mapCache_noTilesToDownload": "Немає плиток для завантаження в цій області.",
+ "mapCache_downloadTilesTitle": "Завантажити плитки",
+ "mapCache_downloadTilesPrompt": "Завантажити {count} плиток для використання офлайн?",
+ "@mapCache_downloadTilesPrompt": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadAction": "Завантажити",
+ "mapCache_cachedTiles": "Закешовано {count} плиток",
+ "@mapCache_cachedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_cachedTilesWithFailed": "Плитки в кеші ({downloaded}) ({failed} помилок)",
+ "@mapCache_cachedTilesWithFailed": {
+ "placeholders": {
+ "downloaded": {
+ "type": "int"
+ },
+ "failed": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_clearOfflineCacheTitle": "Очистити офлайн-кеш",
+ "mapCache_clearOfflineCachePrompt": "Видалити всі закешовані плитки карти?",
+ "mapCache_offlineCacheCleared": "Офлайн-кеш очищено.",
+ "mapCache_noAreaSelected": "Область не вибрано",
+ "mapCache_cacheArea": "Область кешування",
+ "mapCache_useCurrentView": "Використати поточний вигляд",
+ "mapCache_zoomRange": "Діапазон масштабування",
+ "mapCache_estimatedTiles": "Оцінка плиток: {count}",
+ "@mapCache_estimatedTiles": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadedTiles": "Завантажено {completed} / {total}",
+ "@mapCache_downloadedTiles": {
+ "placeholders": {
+ "completed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_downloadTilesButton": "Завантажити плитки",
+ "mapCache_clearCacheButton": "Очистити кеш",
+ "mapCache_failedDownloads": "Невдалі завантаження: {count}",
+ "@mapCache_failedDownloads": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "mapCache_boundsLabel": "Пн {north}, Пд {south}, Сх {east}, Зх {west}",
+ "@mapCache_boundsLabel": {
+ "placeholders": {
+ "north": {
+ "type": "String"
+ },
+ "south": {
+ "type": "String"
+ },
+ "east": {
+ "type": "String"
+ },
+ "west": {
+ "type": "String"
+ }
+ }
+ },
+ "time_justNow": "Тільки що",
+ "time_minutesAgo": "{minutes} хв. тому",
+ "@time_minutesAgo": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hoursAgo": "{hours} год. тому",
+ "@time_hoursAgo": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "time_daysAgo": "{days} дн. тому",
+ "@time_daysAgo": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ }
+ }
+ },
+ "time_hour": "година",
+ "time_hours": "годин",
+ "time_day": "день",
+ "time_days": "днів",
+ "time_week": "тиждень",
+ "time_weeks": "тижнів",
+ "time_month": "місяць",
+ "time_months": "місяців",
+ "time_minutes": "хвилин",
+ "time_allTime": "Весь час",
+ "dialog_disconnect": "Відключити",
+ "dialog_disconnectConfirm": "Ви впевнені, що хочете відключитися від цього пристрою?",
+ "login_repeaterLogin": "Вхід у ретранслятор",
+ "login_roomLogin": "Вхід у кімнату",
+ "login_password": "Пароль",
+ "login_enterPassword": "Введіть пароль",
+ "login_savePassword": "Зберегти пароль",
+ "login_savePasswordSubtitle": "Пароль буде надійно збережено на цьому пристрої.",
+ "login_repeaterDescription": "Введіть пароль ретранслятора для доступу до налаштувань та статусу.",
+ "login_roomDescription": "Введіть пароль кімнати для доступу до налаштувань та статусу.",
+ "login_routing": "Маршрутизація",
+ "login_routingMode": "Режим маршрутизації",
+ "login_autoUseSavedPath": "Авто (використовувати збережений шлях)",
+ "login_forceFloodMode": "Примусово на всю мережу",
+ "login_managePaths": "Керувати шляхами",
+ "login_login": "Вхід",
+ "login_attempt": "Спроба {current}/{max}",
+ "@login_attempt": {
+ "placeholders": {
+ "current": {
+ "type": "int"
+ },
+ "max": {
+ "type": "int"
+ }
+ }
+ },
+ "login_failed": "Вхід не вдався: {error}",
+ "@login_failed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "login_failedMessage": "Вхід не вдався. Або пароль неправильний, або ретранслятор недосяжний.",
+ "common_reload": "Перезавантажити",
+ "common_clear": "Очистити",
+ "path_currentPath": "Поточний шлях: {path}",
+ "@path_currentPath": {
+ "placeholders": {
+ "path": {
+ "type": "String"
+ }
+ }
+ },
+ "path_usingHopsPath": "Використання шляху з {count} {count, plural, =1{стрибком} few{стрибками} many{стрибками} other{стрибками}}",
+ "@path_usingHopsPath": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "path_enterCustomPath": "Ввести власний шлях",
+ "path_currentPathLabel": "Поточний шлях",
+ "path_hexPrefixInstructions": "Введіть 2-символьні hex-префікси для кожного стрибка, розділені комами.",
+ "path_hexPrefixExample": "Приклад: A1,F2,3C (кожен вузол використовує перший байт свого відкритого ключа).",
+ "path_labelHexPrefixes": "Hex-префікси",
+ "path_helperMaxHops": "Макс. 64 стрибки. Кожен префікс - 2 шістнадцяткові символи (1 байт)",
+ "path_selectFromContacts": "Вибрати з контактів:",
+ "path_noRepeatersFound": "Ретрансляторів або серверів кімнат не знайдено.",
+ "path_customPathsRequire": "Власні шляхи вимагають проміжних вузлів, які можуть передавати повідомлення.",
+ "path_invalidHexPrefixes": "Некоректні hex-префікси: {prefixes}",
+ "@path_invalidHexPrefixes": {
+ "placeholders": {
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "path_tooLong": "Шлях занадто довгий. Максимум 64 стрибки.",
+ "path_setPath": "Встановити шлях",
+ "repeater_management": "Керування ретранслятором",
+ "repeater_managementTools": "Інструменти керування",
+ "repeater_status": "Статус",
+ "repeater_statusSubtitle": "Показати статус, статистику та сусідів ретранслятора",
+ "repeater_telemetry": "Телеметрія",
+ "repeater_telemetrySubtitle": "Показати телеметрію сенсорів та статистику системи",
+ "repeater_cli": "CLI",
+ "repeater_cliSubtitle": "Надіслати команди ретранслятору",
+ "repeater_settings": "Налаштування",
+ "repeater_settingsSubtitle": "Налаштувати параметри ретранслятора",
+ "repeater_statusTitle": "Статус ретранслятора",
+ "repeater_routingMode": "Режим маршрутизації",
+ "repeater_autoUseSavedPath": "Авто (використовувати збережений шлях)",
+ "repeater_forceFloodMode": "Примусово на всю мережу",
+ "repeater_pathManagement": "Керування шляхами",
+ "repeater_refresh": "Оновити",
+ "repeater_statusRequestTimeout": "Час очікування запиту статусу вичерпано.",
+ "repeater_errorLoadingStatus": "Помилка завантаження статусу: {error}",
+ "@repeater_errorLoadingStatus": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_systemInformation": "Системна інформація",
+ "repeater_battery": "Батарея",
+ "repeater_clockAtLogin": "Годинник (при вході)",
+ "repeater_uptime": "Час роботи",
+ "repeater_queueLength": "Довжина черги",
+ "repeater_debugFlags": "Прапорці налагодження",
+ "repeater_radioStatistics": "Статистика радіо",
+ "repeater_lastRssi": "Останній RSSI",
+ "repeater_lastSnr": "Останній SNR",
+ "repeater_noiseFloor": "Рівень шуму",
+ "repeater_txAirtime": "Ефірний час TX",
+ "repeater_rxAirtime": "Ефірний час RX",
+ "repeater_packetStatistics": "Статистика пакетів",
+ "repeater_sent": "Надіслано",
+ "repeater_received": "Отримано",
+ "repeater_duplicates": "Дублікати",
+ "repeater_daysHoursMinsSecs": "{days} дн. {hours} год {minutes} хв {seconds} с",
+ "@repeater_daysHoursMinsSecs": {
+ "placeholders": {
+ "days": {
+ "type": "int"
+ },
+ "hours": {
+ "type": "int"
+ },
+ "minutes": {
+ "type": "int"
+ },
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_packetTxTotal": "Всього: {total}, На всю мережу: {flood}, Прямі: {direct}",
+ "@repeater_packetTxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_packetRxTotal": "Всього: {total}, На всю мережу: {flood}, Прямі: {direct}",
+ "@repeater_packetRxTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ },
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesFloodDirect": "На всю мережу: {flood}, Прямі: {direct}",
+ "@repeater_duplicatesFloodDirect": {
+ "placeholders": {
+ "flood": {
+ "type": "String"
+ },
+ "direct": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_duplicatesTotal": "Всього: {total}",
+ "@repeater_duplicatesTotal": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_settingsTitle": "Налаштування ретранслятора",
+ "repeater_basicSettings": "Основні налаштування",
+ "repeater_repeaterName": "Ім'я ретранслятора",
+ "repeater_repeaterNameHelper": "Показати ім'я цього ретранслятора",
+ "repeater_adminPassword": "Пароль адміністратора",
+ "repeater_adminPasswordHelper": "Пароль повного доступу",
+ "repeater_guestPassword": "Гостьовий пароль",
+ "repeater_guestPasswordHelper": "Доступ лише для читання з паролем",
+ "repeater_radioSettings": "Налаштування радіо",
+ "repeater_frequencyMhz": "Частота (МГц)",
+ "repeater_frequencyHelper": "300-2500 МГц",
+ "repeater_txPower": "Потужність TX",
+ "repeater_txPowerHelper": "1-30 дБм",
+ "repeater_bandwidth": "Смуга пропускання",
+ "repeater_spreadingFactor": "Коефіцієнт розширення",
+ "repeater_codingRate": "Швидкість кодування",
+ "repeater_locationSettings": "Налаштування розташування",
+ "repeater_latitude": "Широта",
+ "repeater_latitudeHelper": "Десяткові градуси (наприклад, 37.7749)",
+ "repeater_longitude": "Довгота",
+ "repeater_longitudeHelper": "Десяткові градуси (наприклад, -122.4194)",
+ "repeater_features": "Функції",
+ "repeater_packetForwarding": "Пересилання пакетів",
+ "repeater_packetForwardingSubtitle": "Дозволити ретранслятору пересилати пакети",
+ "repeater_guestAccess": "Гостьовий доступ",
+ "repeater_guestAccessSubtitle": "Дозволити гостьовий доступ лише для читання",
+ "repeater_privacyMode": "Режим приватності",
+ "repeater_privacyModeSubtitle": "Приховати ім'я/розташування в оголошеннях",
+ "repeater_advertisementSettings": "Налаштування оголошень",
+ "repeater_localAdvertInterval": "Інтервал локальних оголошень (0 стрибків)",
+ "repeater_localAdvertIntervalMinutes": "{minutes} хвилин",
+ "@repeater_localAdvertIntervalMinutes": {
+ "placeholders": {
+ "minutes": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_floodAdvertInterval": "Інтервал оголошень на всю мережу (flood)",
+ "repeater_floodAdvertIntervalHours": "{hours} годин",
+ "@repeater_floodAdvertIntervalHours": {
+ "placeholders": {
+ "hours": {
+ "type": "int"
+ }
+ }
+ },
+ "repeater_encryptedAdvertInterval": "Інтервал зашифрованих оголошень",
+ "repeater_dangerZone": "Небезпечна зона",
+ "repeater_rebootRepeater": "Перезавантажити ретранслятор",
+ "repeater_rebootRepeaterSubtitle": "Скинути пристрій ретранслятора",
+ "repeater_rebootRepeaterConfirm": "Ви впевнені, що хочете перезавантажити цей ретранслятор?",
+ "repeater_regenerateIdentityKey": "Перегенерувати ключ ідентичності",
+ "repeater_regenerateIdentityKeySubtitle": "Згенерувати нову пару ключів (публічний/приватний)",
+ "repeater_regenerateIdentityKeyConfirm": "Це створить нову ідентичність для ретранслятора. Продовжити?",
+ "repeater_eraseFileSystem": "Очистити файлову систему",
+ "repeater_eraseFileSystemSubtitle": "Відформатувати файлову систему ретранслятора",
+ "repeater_eraseFileSystemConfirm": "УВАГА: Це видалить всі дані з ретранслятора. Це не можна скасувати!",
+ "repeater_eraseSerialOnly": "Очищення доступне лише через послідовну консоль.",
+ "repeater_commandSent": "Команда надіслана: {command}",
+ "@repeater_commandSent": {
+ "placeholders": {
+ "command": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorSendingCommand": "Помилка надсилання команди: {error}",
+ "@repeater_errorSendingCommand": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_confirm": "Підтвердити",
+ "repeater_settingsSaved": "Налаштування успішно збережено.",
+ "repeater_errorSavingSettings": "Помилка збереження налаштувань: {error}",
+ "@repeater_errorSavingSettings": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_refreshBasicSettings": "Оновити основні налаштування",
+ "repeater_refreshRadioSettings": "Оновити налаштування радіо",
+ "repeater_refreshTxPower": "Оновити потужність TX",
+ "repeater_refreshLocationSettings": "Оновити налаштування розташування",
+ "repeater_refreshPacketForwarding": "Оновити пересилання пакетів",
+ "repeater_refreshGuestAccess": "Оновити гостьовий доступ",
+ "repeater_refreshPrivacyMode": "Оновити режим приватності",
+ "repeater_refreshAdvertisementSettings": "Оновити налаштування оголошень",
+ "repeater_refreshed": "{label} оновлено",
+ "@repeater_refreshed": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_errorRefreshing": "Помилка оновлення {label}",
+ "@repeater_errorRefreshing": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliTitle": "Ретранслятор CLI",
+ "repeater_debugNextCommand": "Налагодити наступну команду",
+ "repeater_commandHelp": "Довідка",
+ "repeater_clearHistory": "Очистити історію",
+ "repeater_noCommandsSent": "Команди ще не надсилалися.",
+ "repeater_typeCommandOrUseQuick": "Введіть команду нижче або використовуйте швидкі команди",
+ "repeater_enterCommandHint": "Введіть команду...",
+ "repeater_previousCommand": "Попередня команда",
+ "repeater_nextCommand": "Наступна команда",
+ "repeater_enterCommandFirst": "Спершу введіть команду",
+ "repeater_cliCommandFrameTitle": "Фрейм команди CLI",
+ "repeater_cliCommandError": "Помилка: {error}",
+ "@repeater_cliCommandError": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_cliQuickGetName": "Отримати ім'я",
+ "repeater_cliQuickGetRadio": "Отримати Радіо",
+ "repeater_cliQuickGetTx": "Отримати TX",
+ "repeater_cliQuickNeighbors": "Сусіди",
+ "repeater_cliQuickVersion": "Версія",
+ "repeater_cliQuickAdvertise": "Оголосити",
+ "repeater_cliQuickClock": "Годинник",
+ "repeater_cliHelpAdvert": "Надсилає пакет оголошення",
+ "repeater_cliHelpReboot": "Перезавантажує пристрій. (Зверніть увагу, ви можете отримати «Тайм-аут», що є нормальним)",
+ "repeater_cliHelpClock": "Відображає поточний час за годинником кожного пристрою.",
+ "repeater_cliHelpPassword": "Встановлює новий пароль адміністратора для пристрою.",
+ "repeater_cliHelpVersion": "Відображає версію пристрою та дату збірки прошивки.",
+ "repeater_cliHelpClearStats": "Скидає різні лічильники статистики до нуля.",
+ "repeater_cliHelpSetAf": "Встановлює коефіцієнт ефірного часу.",
+ "repeater_cliHelpSetTx": "Встановлює потужність передачі LoRa в дБм (для застосування потрібне перезавантаження).",
+ "repeater_cliHelpSetRepeat": "Вмикає або вимикає роль ретранслятора для цього вузла.",
+ "repeater_cliHelpSetAllowReadOnly": "(Сервер кімнати) Якщо «увімкнено», порожній пароль дозволить вхід, але не дозволить публікувати в кімнаті. (тільки читання)",
+ "repeater_cliHelpSetFloodMax": "Встановлює максимальну кількість стрибків для вхідних пакетів flood (якщо >= max, пакет не пересилається).",
+ "repeater_cliHelpSetIntThresh": "Встановлює поріг інтерференції (в дБ). Значення за замовчуванням — 14. Встановлення на 0 вимикає виявлення інтерференції каналу.",
+ "repeater_cliHelpSetAgcResetInterval": "Встановлює інтервал скидання автоматичного контролера посилення (AGC). Встановіть 0 для вимкнення.",
+ "repeater_cliHelpSetMultiAcks": "Вмикає або вимикає функціональність подвійних ACK.",
+ "repeater_cliHelpSetAdvertInterval": "Встановлює інтервал таймера для надсилання локального пакету оголошення (без ретрансляції). Встановіть 0 для вимкнення.",
+ "repeater_cliHelpSetFloodAdvertInterval": "Встановлює інтервал таймера в годинах для надсилання пакету оголошення на всю мережу. Встановіть 0 для вимкнення.",
+ "repeater_cliHelpSetGuestPassword": "Встановлює/оновлює гостьовий пароль. (для ретрансляторів гостьові підключення можуть надсилати запит «Get Stats»)",
+ "repeater_cliHelpSetName": "Встановлює ім'я для оголошення.",
+ "repeater_cliHelpSetLat": "Встановлює широту для карти оголошень. (десяткові градуси)",
+ "repeater_cliHelpSetLon": "Встановлює довготу для карти оголошень. (десяткові градуси)",
+ "repeater_cliHelpSetRadio": "Повністю встановлює нові параметри радіо та зберігає їх у налаштуваннях. Потребує команди «перезавантаження» для застосування.",
+ "repeater_cliHelpSetRxDelay": "Базові (експериментальні) параметри для застосування невеликої затримки до отриманих пакетів залежно від сили сигналу/оцінки. Встановіть 0 для вимкнення.",
+ "repeater_cliHelpSetTxDelay": "Встановлює множник для часу роботи в режимі «на всю мережу» (flood) для пакету та системи випадкових слотів, щоб затримати його відправку (для зменшення ймовірності колізій).",
+ "repeater_cliHelpSetDirectTxDelay": "Те саме, що й txdelay, але для застосування випадкової затримки при пересиланні пакетів у прямому режимі.",
+ "repeater_cliHelpSetBridgeEnabled": "Увімкнути/Вимкнути міст.",
+ "repeater_cliHelpSetBridgeDelay": "Встановити затримку перед пересиланням пакетів.",
+ "repeater_cliHelpSetBridgeSource": "Виберіть, чи буде міст ретранслювати отримані пакети або передані пакети.",
+ "repeater_cliHelpSetBridgeBaud": "Встановити швидкість послідовного зв'язку для мостів Rs232.",
+ "repeater_cliHelpSetBridgeSecret": "Встановити секрет мосту для мостів espnow.",
+ "repeater_cliHelpSetAdcMultiplier": "Встановлює власний множник для коригування повідомлюваної напруги батареї (підтримується лише на деяких платах).",
+ "repeater_cliHelpTempRadio": "Встановлює тимчасові параметри радіо на задану кількість хвилин, потім повертається до початкових налаштувань. (не зберігає в налаштуваннях).",
+ "repeater_cliHelpSetPerm": "Змінює ACL (список контролю доступу). Видаляє відповідний запис (за префіксом публічного ключа), якщо «permissions» дорівнює нулю. Додає новий запис, якщо hex публічного ключа повний і його немає в ACL. Оновлює запис на основі префікса публічного ключа. Біти дозволів залежать від ролі прошивки, але нижні 2 біти: 0 (Гість), 1 (Тільки читання), 2 (Читання/Запис), 3 (Адміністратор).",
+ "repeater_cliHelpGetBridgeType": "Отримати тип мосту: немає, rs232, espnow",
+ "repeater_cliHelpLogStart": "Починає запис пакетів у файлову систему.",
+ "repeater_cliHelpLogStop": "Зупиняє запис пакетів у файлову систему.",
+ "repeater_cliHelpLogErase": "Видаляє журнали пакетів з файлової системи.",
+ "repeater_cliHelpNeighbors": "Показує список інших вузлів-ретрансляторів, почутих через оголошення без ретрансляції. Кожен рядок — id-hex-префікс:timestamp:snr-помножено-на-4",
+ "repeater_cliHelpNeighborRemove": "Видаляє перший відповідний запис (за префіксом публічного ключа (hex)) зі списку сусідів.",
+ "repeater_cliHelpRegion": "(тільки серійний) Перелічує всі визначені регіони та поточні дозволи на оголошення «на всю мережу» (flood).",
+ "repeater_cliHelpRegionLoad": "ПРИМІТКА: це спеціальний виклик кількох команд. Кожна наступна команда — це назва регіону (з відступом пробілами для позначення ієрархії батьків, мінімум один пробіл). Завершується надсиланням порожнього рядка/команди.",
+ "repeater_cliHelpRegionGet": "Шукає регіон із заданим префіксом назви (або «» для глобальної області). Відповідає: «-> ім'я-регіону (ім'я-батька) 'F'»",
+ "repeater_cliHelpRegionPut": "Додає або оновлює визначення регіону з заданою назвою.",
+ "repeater_cliHelpRegionRemove": "Видаляє визначення регіону з заданою назвою.",
+ "repeater_cliHelpRegionAllowf": "Встановлює дозвіл «Flood» для заданого регіону. ('' для глобальної/успадкованої області)",
+ "repeater_cliHelpRegionDenyf": "Видаляє дозвіл «Flood» для заданого регіону. (ПРИМІТКА: на даному етапі не рекомендується використовувати для глобальної/успадкованої області!! )",
+ "repeater_cliHelpRegionHome": "Відповідає поточним «домашнім» регіоном. (Примітка: поки ніде не застосовується, зарезервовано для майбутнього використання)",
+ "repeater_cliHelpRegionHomeSet": "Встановлює «домашній» регіон.",
+ "repeater_cliHelpRegionSave": "Зберігає список/карту регіонів у сховищі.",
+ "repeater_cliHelpGps": "Показує статус GPS. Коли GPS вимкнено, відповідає лише «вимкнено», якщо увімкнено — відповідає «увімкнено», статус, корекція, кількість супутників.",
+ "repeater_cliHelpGpsOnOff": "Увімкнути/вимкнути GPS.",
+ "repeater_cliHelpGpsSync": "Синхронізує час вузла з годинником GPS.",
+ "repeater_cliHelpGpsSetLoc": "Встановлює позицію вузла за координатами GPS і зберігає в налаштуваннях.",
+ "repeater_cliHelpGpsAdvert": "Надає конфігурацію оголошення розташування вузла:\n- none : не включати розташування в оголошення\n- share : ділитися розташуванням GPS (з SensorManager)\n- prefs : оголошувати розташування, збережене в налаштуваннях",
+ "repeater_cliHelpGpsAdvertSet": "Встановлює конфігурацію оголошення розташування.",
+ "repeater_commandsListTitle": "Список команд",
+ "repeater_commandsListNote": "ПРИМІТКА: для різних команд «set»... також існує команда «get»...",
+ "repeater_general": "Загальні",
+ "repeater_settingsCategory": "Налаштування",
+ "repeater_bridge": "Міст",
+ "repeater_logging": "Логування",
+ "repeater_neighborsRepeaterOnly": "Сусіди (Тільки ретранслятор)",
+ "repeater_regionManagementRepeaterOnly": "Керування регіонами (Тільки ретранслятор)",
+ "repeater_regionNote": "Команди регіонів були введені для керування визначеннями та дозволами регіонів.",
+ "repeater_gpsManagement": "Керування GPS",
+ "repeater_gpsNote": "Команда GPS була введена для керування питаннями, пов'язаними з локацією.",
+ "telemetry_receivedData": "Дані телеметрії отримано",
+ "telemetry_requestTimeout": "Час запиту телеметрії вичерпано.",
+ "telemetry_errorLoading": "Помилка завантаження телеметрії: {error}",
+ "@telemetry_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_noData": "Дані телеметрії недоступні.",
+ "telemetry_channelTitle": "Канал {channel}",
+ "@telemetry_channelTitle": {
+ "placeholders": {
+ "channel": {
+ "type": "int"
+ }
+ }
+ },
+ "telemetry_batteryLabel": "Батарея",
+ "telemetry_voltageLabel": "Напруга",
+ "telemetry_mcuTemperatureLabel": "Температура MCU",
+ "telemetry_temperatureLabel": "Температура",
+ "telemetry_currentLabel": "Поточний струм",
+ "telemetry_batteryValue": "{percent}% / {volts}В",
+ "@telemetry_batteryValue": {
+ "placeholders": {
+ "percent": {
+ "type": "int"
+ },
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_voltageValue": "{volts}В",
+ "@telemetry_voltageValue": {
+ "placeholders": {
+ "volts": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_currentValue": "{amps}А",
+ "@telemetry_currentValue": {
+ "placeholders": {
+ "amps": {
+ "type": "String"
+ }
+ }
+ },
+ "telemetry_temperatureValue": "{celsius}°C / {fahrenheit}°F",
+ "@telemetry_temperatureValue": {
+ "placeholders": {
+ "celsius": {
+ "type": "String"
+ },
+ "fahrenheit": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_title": "Шлях пакету",
+ "channelPath_viewMap": "Показати карту",
+ "channelPath_otherObservedPaths": "Інші спостережувані шляхи",
+ "channelPath_repeaterHops": "Стрибки ретранслятора",
+ "channelPath_noHopDetails": "Деталі відправки не надані для цього пакету.",
+ "channelPath_messageDetails": "Деталі повідомлення",
+ "channelPath_senderLabel": "Відправник",
+ "channelPath_timeLabel": "Час",
+ "channelPath_repeatsLabel": "Повторення",
+ "channelPath_pathLabel": "Шлях {index}",
+ "channelPath_observedLabel": "Спостережено",
+ "channelPath_observedPathTitle": "Спостережуваний шлях {index} • {hops}",
+ "@channelPath_observedPathTitle": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ },
+ "hops": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noLocationData": "Немає даних про розташування",
+ "channelPath_timeWithDate": "{day}/{month} {time}",
+ "@channelPath_timeWithDate": {
+ "placeholders": {
+ "day": {
+ "type": "int"
+ },
+ "month": {
+ "type": "int"
+ },
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_timeOnly": "{time}",
+ "@channelPath_timeOnly": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_unknownPath": "Невідомий",
+ "channelPath_floodPath": "На всю мережу",
+ "channelPath_directPath": "Прямий",
+ "channelPath_observedZeroOf": "0 з {total} стрибків",
+ "@channelPath_observedZeroOf": {
+ "placeholders": {
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_observedSomeOf": "{observed} з {total} стрибків",
+ "@channelPath_observedSomeOf": {
+ "placeholders": {
+ "observed": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_mapTitle": "Карта шляху",
+ "channelPath_noRepeaterLocations": "Позиції ретрансляторів недоступні для цього шляху.",
+ "channelPath_primaryPath": "Шлях {index} (Основний)",
+ "@channelPath_primaryPath": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "@channelPath_pathLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "channelPath_pathLabelTitle": "Шлях",
+ "channelPath_observedPathHeader": "Спостережуваний шлях",
+ "channelPath_selectedPathLabel": "{label} • {prefixes}",
+ "@channelPath_selectedPathLabel": {
+ "placeholders": {
+ "label": {
+ "type": "String"
+ },
+ "prefixes": {
+ "type": "String"
+ }
+ }
+ },
+ "channelPath_noHopDetailsAvailable": "Деталі стрибків недоступні для цього пакету.",
+ "channelPath_unknownRepeater": "Невідомий ретранслятор",
+ "listFilter_tooltip": "Фільтр та сортування",
+ "listFilter_sortBy": "Сортувати за",
+ "listFilter_latestMessages": "Останні повідомлення",
+ "listFilter_heardRecently": "Нещодавно чули",
+ "listFilter_az": "А-Я",
+ "listFilter_filters": "Фільтри",
+ "listFilter_all": "Все",
+ "listFilter_users": "Користувачі",
+ "listFilter_repeaters": "Ретранслятори",
+ "listFilter_roomServers": "Сервери кімнат",
+ "listFilter_unreadOnly": "Тільки непрочитані повідомлення",
+ "listFilter_newGroup": "Нова група",
+ "@neighbors_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "repeater_neighbors": "Сусіди",
+ "repeater_neighborsSubtitle": "Показати сусідів нульового стрибка.",
+ "neighbors_receivedData": "Дані сусідів отримано",
+ "neighbors_requestTimedOut": "Час запиту сусідів вичерпано.",
+ "neighbors_errorLoading": "Помилка завантаження сусідів: {error}",
+ "neighbors_repeatersNeighbors": "Ретранслятори-сусіди",
+ "neighbors_noData": "Дані про сусідів недоступні.",
+ "channels_createPrivateChannelDesc": "Захищено секретним ключем.",
+ "channels_joinPrivateChannel": "Приєднатися до приватного каналу",
+ "channels_createPrivateChannel": "Створити приватний канал",
+ "channels_joinPrivateChannelDesc": "Ввести секретний ключ вручну.",
+ "channels_joinPublicChannel": "Приєднатися до публічного каналу",
+ "channels_joinPublicChannelDesc": "Будь-хто може приєднатися до цього каналу.",
+ "channels_joinHashtagChannel": "Приєднатися до каналу з хештегом",
+ "channels_joinHashtagChannelDesc": "Будь-хто може приєднатися до каналів #hashtag.",
+ "channels_scanQrCode": "Сканувати QR-код",
+ "channels_scanQrCodeComingSoon": "Скоро буде",
+ "channels_enterHashtag": "Введіть хештег",
+ "channels_hashtagHint": "напр. #команда",
+ "@neighbors_unknownContact": {
+ "placeholders": {
+ "pubkey": {
+ "type": "String"
+ }
+ }
+ },
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_unknownContact": "Невідомий відкритий ключ {pubkey}",
+ "neighbors_heardAgo": "Почуто: {time} тому",
+ "settings_locationGPSEnable": "Увімкнути GPS",
+ "settings_locationGPSEnableSubtitle": "Вмикає автоматичне оновлення місцезнаходження через GPS.",
+ "settings_locationIntervalSec": "Інтервал для GPS (Секунди)",
+ "settings_locationIntervalInvalid": "Інтервал має бути не менше 60 секунд і менше 86400 секунд.",
+ "contacts_manageRoom": "Керувати сервером кімнати",
+ "room_management": "Адміністрування сервера кімнати",
+ "@community_joinConfirmation": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_created": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_joined": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_qrInstructions": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_alreadyMemberMessage": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_deleteConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_deleted": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_forCommunity": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "common_ok": "ОК",
+ "community_title": "Спільнота",
+ "community_create": "Створити спільноту",
+ "community_createDesc": "Створити нову спільноту та поділитися через QR-код.",
+ "community_join": "Приєднатися",
+ "community_joinTitle": "Приєднатися до спільноти",
+ "community_joinConfirmation": "Ви бажаєте приєднатися до спільноти «{name}»?",
+ "community_scanQr": "Сканувати QR спільноти",
+ "community_scanInstructions": "Наведіть камеру на QR-код спільноти.",
+ "community_showQr": "Показати QR-код",
+ "community_publicChannel": "Публічна спільнота",
+ "community_hashtagChannel": "Хештег спільноти",
+ "community_name": "Назва спільноти",
+ "community_enterName": "Введіть назву спільноти",
+ "community_created": "Спільноту «{name}» створено",
+ "community_joined": "Приєднався до спільноти «{name}»",
+ "community_qrTitle": "Поділитися спільнотою",
+ "community_qrInstructions": "Відскануйте цей QR-код, щоб приєднатися до {name}",
+ "community_hashtagPrivacyHint": "Канали хештегів спільноти доступні лише членам спільноти",
+ "community_invalidQrCode": "Недійсний QR-код спільноти",
+ "community_alreadyMember": "Вже учасник",
+ "community_alreadyMemberMessage": "Ви вже є учасником «{name}».",
+ "community_addPublicChannel": "Додати публічний канал спільноти",
+ "community_addPublicChannelHint": "Автоматично додати публічний канал для цієї спільноти",
+ "community_noCommunities": "Поки не приєднано до жодної групи.",
+ "community_scanOrCreate": "Відскануйте QR-код або створіть спільноту, щоб почати",
+ "community_manageCommunities": "Керувати спільнотами",
+ "community_delete": "Покинути спільноту",
+ "community_deleteConfirm": "Покинути «{name}»?",
+ "community_deleteChannelsWarning": "Це також видалить {count} {count, plural, =1{канал} few{канали} many{каналів} other{каналів}} та їх повідомлення.",
+ "@community_deleteChannelsWarning": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "community_deleted": "Спільноту «{name}» покинуто",
+ "community_addHashtagChannel": "Додати хештег спільноти",
+ "community_addHashtagChannelDesc": "Додати канал хештегу для цієї спільноти",
+ "community_selectCommunity": "Вибрати спільноту",
+ "community_regularHashtag": "Звичайний хештег",
+ "community_regularHashtagDesc": "Публічний хештег (будь-хто може приєднатися)",
+ "community_communityHashtag": "Хештег спільноти",
+ "community_communityHashtagDesc": "Ексклюзивно для членів спільноти",
+ "community_forCommunity": "Для {name}",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerateSecret": "Перегенерувати секрет",
+ "community_regenerateSecretConfirm": "Перегенерувати секретний ключ для «{name}»? Всі учасники повинні будуть відсканувати новий QR-код, щоб продовжити спілкування.",
+ "community_regenerate": "Перегенерувати",
+ "community_secretRegenerated": "Секретний пароль для «{name}» перегенеровано",
+ "community_scanToUpdateSecret": "Відскануйте новий QR-код, щоб оновити пароль для «{name}»",
+ "community_updateSecret": "Оновити секрет",
+ "community_secretUpdated": "Зміну секрету для «{name}» оновлено",
+ "@contacts_pathTraceTo": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "pathTrace_you": "Ви",
+ "pathTrace_failed": "Відстеження шляху не вдалося.",
+ "pathTrace_notAvailable": "Трасування шляху недоступне.",
+ "pathTrace_refreshTooltip": "Оновити Path Trace",
+ "contacts_pathTrace": "Трасування шляхів",
+ "contacts_ping": "Пінгувати",
+ "contacts_repeaterPathTrace": "Трасування шляху до повторювача",
+ "contacts_repeaterPing": "Пінгувати повторювач",
+ "contacts_roomPathTrace": "Трасування шляху до серверу кімнати",
+ "contacts_roomPing": "Пінг сервера кімнати",
+ "contacts_chatTraceRoute": "Трасування шляху",
+ "contacts_pathTraceTo": "Відстежити маршрут до {name}",
+ "contacts_invalidAdvertFormat": "Недійсні контактні дані",
+ "contacts_contactImported": "Контакт було імпортовано.",
+ "contacts_contactImportFailed": "Контакт не вдалося імпортувати",
+ "contacts_zeroHopAdvert": "Реклама без перехоплення",
+ "contacts_floodAdvert": "Залив реклами",
+ "contacts_copyAdvertToClipboard": "Копіювати оголошення в буфер обміну",
+ "contacts_clipboardEmpty": "Буфер обміну порожній",
+ "appSettings_languageRu": "Російська",
+ "appSettings_enableMessageTracing": "Увімкнути відстеження повідомлень",
+ "appSettings_enableMessageTracingSubtitle": "Показувати детальні метадані про маршрутизацію та час для повідомлень",
+ "contacts_ShareContact": "Копіювати контакт у буфер обміну",
+ "contacts_zeroHopContactAdvertFailed": "Не вдалося надіслати контакт.",
+ "contacts_contactAdvertCopied": "Рекламу скопійовано до буфера обміну.",
+ "contacts_contactAdvertCopyFailed": "Копіювання оголошення в буфер обміну завершилося невдало",
+ "contacts_zeroHopContactAdvertSent": "Відправлено контакт за оголошенням",
+ "contacts_addContactFromClipboard": "Додати контакт з буфера обміну",
+ "contacts_ShareContactZeroHop": "Поділитися контактом за оголошенням",
+ "notification_activityTitle": "Активність MeshCore",
+ "notification_messagesCount": "{count} {count, plural, =1{повідомлення} few{повідомлення} many{повідомлень} other{повідомлень}}",
+ "notification_channelMessagesCount": "{count} {count, plural, =1{повідомлення каналу} few{повідомлення каналу} many{повідомлень каналу} other{повідомлень каналу}}",
+ "notification_newNodesCount": "{count} {count, plural, =1{новий вузол} few{нових вузли} many{нових вузлів} other{нових вузлів}}",
+ "notification_newTypeDiscovered": "Виявлено новий {contactType}",
+ "notification_receivedNewMessage": "Отримано нове повідомлення",
+ "settings_gpxExportRepeaters": "Експортувати ретранслятори / сервер кімнати до GPX",
+ "settings_gpxExportRepeatersSubtitle": "Експортує ретранслятори / сервер кімнати з місцезнаходженням у файл GPX.",
+ "settings_gpxExportSuccess": "Успішно експортовано файл GPX.",
+ "settings_gpxExportNoContacts": "Немає контактів для експорту.",
+ "settings_gpxExportNotAvailable": "Не підтримується на вашому пристрої/операційній системі",
+ "settings_gpxExportError": "Сталася помилка під час експорту.",
+ "settings_gpxExportAllSubtitle": "Експортує всі контакти з місцем розташування у файл GPX.",
+ "settings_gpxExportAll": "Експортувати всі контакти до GPX",
+ "settings_gpxExportContactsSubtitle": "Експортує супутників з місцезнаходженням у файл GPX.",
+ "settings_gpxExportContacts": "Експортувати супутників до GPX",
+ "settings_gpxExportRepeatersRoom": "Місцезнаходження повторювача та сервера кімнати",
+ "settings_gpxExportChat": "Місця супутників",
+ "settings_gpxExportShareText": "Дані карти експортовані з meshcore-open",
+ "settings_gpxExportAllContacts": "Усі місця контактів",
+ "settings_gpxExportShareSubject": "експорт даних карти meshcore-open у форматі GPX",
+ "pathTrace_someHopsNoLocation": "Одне або більше хмелів відсутнє місце розташування!",
+ "map_tapToAdd": "Натисніть на вузли, щоб додати їх до шляху",
+ "map_runTrace": "Виконати трасування шляху",
+ "pathTrace_clearTooltip": "Очистити шлях",
+ "map_removeLast": "Видалити останній",
+ "map_pathTraceCancelled": "Відмінується трасування шляху",
+ "scanner_enableBluetooth": "Увімкніть Bluetooth",
+ "scanner_bluetoothOffMessage": "Будь ласка, увімкніть Bluetooth, щоб сканувати пристрої.",
+ "scanner_chromeRequired": "Потрібен браузер Chrome",
+ "scanner_chromeRequiredMessage": "Для підтримки Bluetooth у цьому веб-додатку потрібен Google Chrome або браузер на базі Chromium.",
+ "scanner_bluetoothOff": "Bluetooth вимкнено",
+ "snrIndicator_lastSeen": "Останній раз бачили",
+ "snrIndicator_nearByRepeaters": "Ближні ретранслятори",
+ "chat_ShowAllPaths": "Показати всі шляхи",
+ "settings_clientRepeatFreqWarning": "Повтор без підключення до мережі вимагає частоти 433, 869 або 918 МГц.",
+ "settings_clientRepeatSubtitle": "Дозвольте цьому пристрою повторювати пакети даних для інших пристроїв.",
+ "settings_clientRepeat": "Автономна система",
+ "settings_aboutOpenMeteoAttribution": "Дані про висоту LOS: Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "одиниці",
+ "appSettings_unitsMetric": "Метричний (м / км)",
+ "appSettings_unitsImperial": "Імперська (ft / mi)",
+ "map_lineOfSight": "Пряма видимість",
+ "map_losScreenTitle": "Пряма видимість",
+ "losSelectStartEnd": "Виберіть початковий і кінцевий вузли для LOS.",
+ "losRunFailed": "Помилка перевірки прямої видимості: {error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "Очистити всі пункти",
+ "losRunToViewElevationProfile": "Запустіть LOS, щоб переглянути профіль висоти",
+ "losMenuTitle": "Меню LOS",
+ "losMenuSubtitle": "Торкніться вузлів або утримуйте карту, щоб отримати власні точки",
+ "losShowDisplayNodes": "Показати вузли відображення",
+ "losCustomPoints": "Користувальницькі точки",
+ "losCustomPointLabel": "Спеціальний {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "Точка А",
+ "losPointB": "Точка Б",
+ "losAntennaA": "Антена A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "Антена B: {value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "Запустіть LOS",
+ "losNoElevationData": "Немає даних про висоту",
+ "losProfileClear": "{distance} {distanceUnit}, чистий LOS, мінімальний зазор {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit}, заблоковано {obstruction} {heightUnit}",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "LOS: перевірка...",
+ "losStatusNoData": "LOS: немає даних",
+ "losStatusSummary": "LOS: {clear}/{total} очищено, {blocked} заблоковано, {unknown} невідомо",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "Дані про висоту недоступні для одного чи кількох зразків.",
+ "losErrorInvalidInput": "Недійсні дані про точки/висоту для розрахунку LOS.",
+ "losRenameCustomPoint": "Перейменуйте спеціальну точку",
+ "losPointName": "Назва точки",
+ "losShowPanelTooltip": "Показати панель LOS",
+ "losHidePanelTooltip": "Приховати панель LOS",
+ "losElevationAttribution": "Дані про висоту: Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "Радіогоризонт",
+ "losLegendLosBeam": "Лінія прямої видимості",
+ "losLegendTerrain": "Рельєф",
+ "losFrequencyLabel": "Частота",
+ "losFrequencyInfoTooltip": "Переглянути деталі розрахунку",
+ "losFrequencyDialogTitle": "Розрахунок радіогоризонту",
+ "losFrequencyDialogDescription": "Починаючи з k={baselineK} на {baselineFreq} МГц, обчислення коригує k-фактор для поточного діапазону {frequencyMHz} МГц, який визначає викривлену межу радіогоризонту.",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_removeFromFavorites": "Видалити зі списку улюблених",
+ "listFilter_addToFavorites": "Додати до улюблених",
+ "listFilter_favorites": "Улюблені",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchRoomServers": "Пошук {number}{str} серверів кімнат...",
+ "contacts_searchUsers": "Пошук {number}{str} користувачів...",
+ "contacts_searchFavorites": "Пошук {number}{str} улюблених...",
+ "contacts_searchContactsNoNumber": "Пошук контактів...",
+ "contacts_searchRepeaters": "Пошук {number}{str} ретрансляторів...",
+ "contacts_unread": "Непрочитане",
+ "settings_contactSettingsSubtitle": "Налаштування для додавання контактів",
+ "settings_contactSettings": "Налаштування контактів",
+ "contactsSettings_autoAddUsersSubtitle": "Дозволити супутникові автоматично додавати виявлених користувачів",
+ "contactsSettings_autoAddRepeatersTitle": "Автоматично додавати повторювачі",
+ "contactsSettings_autoAddRepeatersSubtitle": "Дозволити супутнику автоматично додавати виявлені ретранслятори",
+ "contactsSettings_autoAddRoomServersTitle": "Автоматично додавати сервери кімнат",
+ "contactsSettings_otherTitle": "Інші налаштування, пов'язані з контактами",
+ "contactsSettings_autoAddTitle": "Автоматичне виявлення",
+ "contactsSettings_autoAddUsersTitle": "Автоматично додавати користувачів",
+ "contactsSettings_title": "Налаштування контактів",
+ "contactsSettings_autoAddRoomServersSubtitle": "Дозволити супровіднику автоматично додавати виявлені сервери кімнат.",
+ "contactsSettings_autoAddSensorsTitle": "Автоматично додавати датчики",
+ "discoveredContacts_searchHint": "Знайти виявлені контакти",
+ "discoveredContacts_contactAdded": "Контакт додано",
+ "contactsSettings_autoAddSensorsSubtitle": "Дозволити супровіднику автоматично додавати виявлені сенсори",
+ "contactsSettings_overwriteOldestTitle": "Перезаписати найстаріше",
+ "discoveredContacts_Title": "Виявлені контакти",
+ "discoveredContacts_noMatching": "Відповідних контактів не знайдено",
+ "discoveredContacts_deleteContact": "Видалити контакт",
+ "discoveredContacts_copyContact": "Копіювати контакт у буфер обміну",
+ "discoveredContacts_addContact": "Додати контакт",
+ "contactsSettings_overwriteOldestSubtitle": "Коли список контактів заповнений, найстарший контакт без позначки улюбленого буде замінений.",
+ "common_deleteAll": "Видалити все",
+ "discoveredContacts_deleteContactAll": "Видалити всі виявлені контакти",
+ "discoveredContacts_deleteContactAllContent": "Ви впевнені, що хочете видалити всі виявлені контакти?",
+ "map_showGuessedLocations": "Показати місцезнаходження передбачених вузлів",
+ "map_guessedLocation": "Визначено місцезнаходження",
+ "usbScreenSubtitle": "Виберіть виявлене серійне пристрій і підключіть його безпосередньо до вашого вузла MeshCore.",
+ "usbScreenTitle": "Підключити через USB",
+ "connectionChoiceBluetoothLabel": "Bluetooth",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenStatus": "Виберіть пристрій USB",
+ "usbScreenNote": "USB-серіальний інтерфейс активний на підтримуваних пристроях на базі Android та на десктопних платформах.",
+ "usbScreenEmptyState": "Не знайдено жодних пристроїв USB. Підключіть один і перезавантажте.",
+ "usbErrorPermissionDenied": "Було відмовлено у наданні дозволу на використання USB.",
+ "usbErrorDeviceMissing": "Вибране USB-пристрій більше недоступне.",
+ "usbErrorInvalidPort": "Виберіть дійсний USB-пристрій.",
+ "usbErrorBusy": "Ще один запит на підключення через USB вже обробляється.",
+ "usbErrorNotConnected": "Немає підключених пристроїв USB.",
+ "usbErrorOpenFailed": "Не вдалося відкрити вибране USB-пристрій.",
+ "usbErrorConnectFailed": "Не вдалося підключитися до вибраного USB-пристрою.",
+ "usbErrorUnsupported": "Підтримка USB-серіального інтерфейсу не реалізована на цій платформі.",
+ "usbErrorAlreadyActive": "USB-з'єднання вже встановлено.",
+ "usbErrorNoDeviceSelected": "Не було вибрано жодного пристрою USB.",
+ "usbErrorPortClosed": "З'єднання USB не встановлено.",
+ "usbFallbackDeviceName": "Пристрій для передачі даних по веб-серіалах",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "Пошук пристроїв USB...",
+ "usbStatus_notConnected": "Виберіть пристрій USB",
+ "usbConnectionFailed": "Не вдалося встановити з'єднання через USB: {error}",
+ "usbStatus_connecting": "Підключення до USB-пристрою...",
+ "usbErrorConnectTimedOut": "З'єднання не вдалося встановити. Переконайтеся, що пристрій має встановлене програмне забезпечення USB Companion.",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpHostHint": "192.168.40.10",
+ "tcpHostLabel": "IP-адреса",
+ "tcpScreenTitle": "З'єднатися через протокол TCP",
+ "tcpPortLabel": "Порт",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "Введіть кінцеву точку та підключіться",
+ "tcpStatus_connectingTo": "Підключення до {endpoint}...",
+ "tcpErrorHostRequired": "Необхідно вказати IP-адресу.",
+ "tcpErrorPortInvalid": "Порт повинен бути в межах від 1 до 65535.",
+ "tcpErrorUnsupported": "Транспорт TCP не підтримується на цій платформі.",
+ "tcpErrorTimedOut": "З'єднання TCP завершилося через закінчення часу очікування.",
+ "tcpConnectionFailed": "Не вдалося встановити з'єднання TCP: {error}",
+ "map_showDiscoveryContacts": "Показати контакти Відкриття",
+ "map_setAsMyLocation": "Встановити моє місцезнаходження",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacySubtitle": "Керуйте інформацією, яку буде спільно використовуватися",
+ "settings_privacy": "Налаштування приватності",
+ "settings_telemetryBaseMode": "Режим базової телеметрії",
+ "settings_telemetryLocationMode": "Режим місця телеметрії",
+ "settings_advertLocation": "Розміщення реклами",
+ "settings_advertLocationSubtitle": "Включити місце розташування в оголошення",
+ "settings_privacySettingsDescription": "Виберіть, яку інформацію ваш пристрій буде передавати іншим.",
+ "settings_allowAll": "Дозволити все",
+ "settings_denyAll": "Відхилити все",
+ "settings_allowByContact": "Дозволити за контактними прапорцями",
+ "settings_telemetryEnvironmentMode": "Режим середовища телеметрії",
+ "contact_info": "Контактна інформація",
+ "contact_teleBaseSubtitle": "Дозволити спільний доступ до рівня заряду батареї та базової телеметрії",
+ "contact_teleLoc": "Розташування телеметрії",
+ "contact_teleBase": "Базовий телебачення",
+ "contact_teleLocSubtitle": "Дозволити спільне використання даних про місцеположення",
+ "contact_settings": "Налаштування контактів",
+ "contact_telemetry": "Телеметрія",
+ "contact_clearChat": "Очистити чат",
+ "contact_lastSeen": "Останній раз бачили",
+ "contact_teleEnv": "Середовище телеметрії",
+ "contact_teleEnvSubtitle": "Дозволити спільний доступ до даних датчиків середовища",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_initialRouteWeight": "Початкова вартість маршруту",
+ "appSettings_initialRouteWeightSubtitle": "Початкова вага для нових відкритих шляхів",
+ "appSettings_maxRouteWeight": "Максимальна вага маршруту",
+ "appSettings_maxRouteWeightSubtitle": "Максимальна вага, яку може накопичити маршрут завдяки успішним доставкам.",
+ "appSettings_routeWeightSuccessIncrement": "Збільшення ваги успіху",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "Вага, додана до маршруту після успішної доставки",
+ "appSettings_routeWeightFailureDecrement": "Зменшення ваги помилки",
+ "appSettings_routeWeightFailureDecrementSubtitle": "Вага, яка була знята з маршруту після невдалої доставки",
+ "appSettings_maxMessageRetries": "Максимальна кількість повторних спроб надсилання повідомлення",
+ "appSettings_maxMessageRetriesSubtitle": "Кількість спроб повторного відправлення повідомлення перед тим, як позначити його як невдале",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_telemetryModeUpdated": "Режим телеметрії оновлено",
+ "settings_multiAck": "Багатократне підтвердження: {value}",
+ "map_showOverlaps": "Перекриття ключа повторювача",
+ "map_runTraceWithReturnPath": "Повернутися назад тим же шляхом",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "Будь ласка, зачекайте трохи, перш ніж відправляти знову.",
+ "appSettings_languageHu": "Угорський",
+ "appSettings_jumpToOldestUnreadSubtitle": "При відкритті чату з не прочитаними повідомленнями, прокрутіть до першого не прочитаного повідомлення, а не до останнього.",
+ "appSettings_jumpToOldestUnread": "Перейти до найстарішого непрочитаного повідомлення",
+ "appSettings_languageJa": "Японська",
+ "appSettings_languageKo": "Кореєська",
+ "radioStats_tooltip": "Статистика радіо та мережі",
+ "radioStats_screenTitle": "Дані про радіостанції",
+ "radioStats_notConnected": "Підключіться до пристрою, щоб переглядати статистику радіопередач.",
+ "radioStats_firmwareTooOld": "Статистика радіо приймача вимагає супутнього програмного забезпечення версії 8 або новішої.",
+ "radioStats_waiting": "Очікую на отримання даних…",
+ "radioStats_noiseFloor": "Рівень шуму: {noiseDbm} дБм",
+ "radioStats_lastRssi": "Останній показник RSSI: {rssiDbm} дБм",
+ "radioStats_lastSnr": "Останній показник SNR: {snr} дБ",
+ "radioStats_txAir": "Час трансляції на телеканалі TX (загальний): {seconds} секунд",
+ "radioStats_rxAir": "Загальний час використання RX: {seconds} секунд",
+ "radioStats_chartCaption": "Рівень шуму (дБм) на основі останніх вимірювань.",
+ "radioStats_stripNoise": "Рівень шуму: {noiseDbm} дБм",
+ "radioStats_stripWaiting": "Отримано статистику радіо…",
+ "radioStats_settingsTile": "Дані про радіостанції",
+ "radioStats_settingsSubtitle": "Рівень шуму, RSSI, SNR та час, протягом якого пристрій використовує радіоканал.",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerTitle": "Перекладіть перед відправкою",
+ "translation_title": "Переклад",
+ "translation_enableTitle": "Увімкнути переклад",
+ "translation_enableSubtitle": "Перекладати отримані повідомлення та дозволяти попередній переклад перед відправкою.",
+ "translation_composerSubtitle": "Контролює стан ікон перекладу, який використовується за замовчуванням.",
+ "translation_targetLanguage": "Цільова мова",
+ "translation_useAppLanguage": "Використовуйте мову додатку",
+ "translation_downloadedModelLabel": "Завантажений шаблон",
+ "translation_presetModelLabel": "Заздалегідь налаштований модель від Hugging Face",
+ "translation_manualUrlLabel": "Посилання на веб-сторінку з інструкцією",
+ "translation_downloadModel": "Завантажити модель",
+ "translation_downloading": "Завантаження...",
+ "translation_working": "Працюю...",
+ "translation_stop": "Припинити",
+ "translation_mergingChunks": "Об'єднання завантажених фрагментів у кінцевий файл...",
+ "translation_downloadedModels": "Завантажені моделі",
+ "translation_deleteModel": "Видалити модель",
+ "translation_modelDownloaded": "Модель перекладу завантажена.",
+ "translation_downloadStopped": "Завантаження призупинено.",
+ "translation_downloadFailed": "Не вдалося завантажити: {error}",
+ "translation_enterUrlFirst": "Спочатку введіть URL моделі.",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerEnabledHint": "Повідомлення будуть перекладені перед відправленням.",
+ "translation_messageTranslation": "Переклад повідомлення",
+ "translation_composerDisabledHint": "Надсилайте повідомлення, використовуючи оригінальний текстовий формат.",
+ "translation_translateBeforeSending": "Перекладіть перед відправкою",
+ "translation_translateTo": "Перекласти на {language}",
+ "translation_translationOptions": "Варіанти перекладу",
+ "translation_systemLanguage": "Мова системи",
+ "scanner_linuxPairingPinTitle": "PIN‑код спарювання Bluetooth",
+ "scanner_linuxPairingShowPin": "Показати PIN",
+ "scanner_linuxPairingPinPrompt": "Введіть PIN для {deviceName} (залиште порожнім, якщо його немає).",
+ "scanner_linuxPairingHidePin": "Приховати PIN",
+ "repeater_cliQuickClockSync": "Синхронізація годинника",
+ "repeater_cliQuickDiscovery": "Відкрити сусідів",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLoginSubtitle": "Автоматично надсилати повідомлення \"синхронізація годин\" після успішного входу.",
+ "repeater_clockSyncAfterLogin": "Синхронізація годин після входу",
+ "repeater_guestTools": "Інструменти для гостей",
+ "repeater_guest": "Інформація про ретранслятор",
+ "room_guest": "Інформація про сервер кімнати",
+ "chat_sendMessage": "Надіслати повідомлення"
+}
diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb
index c070414..9dc2325 100644
--- a/lib/l10n/app_zh.arb
+++ b/lib/l10n/app_zh.arb
@@ -1,10 +1,19 @@
{
+ "channels_channelDeleteFailed": "无法删除频道 \"{name}\"",
+ "@channels_channelDeleteFailed": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
"@@locale": "zh",
"appTitle": "MeshCore Open",
"nav_contacts": "联系人",
"nav_channels": "频道",
"nav_map": "地图",
"common_cancel": "取消",
+ "common_ok": "确定",
"common_connect": "连接",
"common_unknownDevice": "未知设备",
"common_save": "保存",
@@ -15,14 +24,14 @@
"common_settings": "设置",
"common_disconnect": "断开",
"common_connected": "已连接",
- "common_disconnected": "断开",
+ "common_disconnected": "已断开",
"common_create": "创建",
"common_continue": "继续",
"common_share": "分享",
"common_copy": "复制",
"common_retry": "重试",
"common_hide": "隐藏",
- "common_remove": "删除",
+ "common_remove": "移除",
"common_enable": "启用",
"common_disable": "禁用",
"common_reboot": "重启",
@@ -44,12 +53,12 @@
}
}
},
- "scanner_title": "MeshCore Open",
- "scanner_scanning": "扫描设备…",
- "scanner_connecting": "连接中...",
- "scanner_disconnecting": "断开中...",
+ "scanner_title": "连接设备",
+ "scanner_scanning": "正在搜索设备...",
+ "scanner_connecting": "正在连接...",
+ "scanner_disconnecting": "断开连接...",
"scanner_notConnected": "未连接",
- "scanner_connectedTo": "已连接至 {deviceName}",
+ "scanner_connectedTo": "已连接到 {deviceName}",
"@scanner_connectedTo": {
"placeholders": {
"deviceName": {
@@ -57,8 +66,8 @@
}
}
},
- "scanner_searchingDevices": "搜索 MeshCore 设备...",
- "scanner_tapToScan": "点击扫描以查找MeshCore设备",
+ "scanner_searchingDevices": "正在搜索 MeshCore 设备...",
+ "scanner_tapToScan": "点击“扫描”按钮以查找 MeshCore 设备。",
"scanner_connectionFailed": "连接失败:{error}",
"@scanner_connectionFailed": {
"placeholders": {
@@ -79,37 +88,41 @@
"settings_nodeName": "节点名称",
"settings_nodeNameNotSet": "未设置",
"settings_nodeNameHint": "请输入节点名称",
- "settings_nodeNameUpdated": "姓名已更新",
- "settings_radioSettings": "无线设置",
- "settings_radioSettingsSubtitle": "频率,功率,扩展因子",
- "settings_radioSettingsUpdated": "射频设置已更新",
+ "settings_nodeNameUpdated": "节点名称已更新",
+ "settings_radioSettings": "无线电设置",
+ "settings_radioSettingsSubtitle": "频率、功率、扩频因子",
+ "settings_radioSettingsUpdated": "无线电设置已更新",
"settings_location": "位置",
- "settings_locationSubtitle": "GPS坐标",
- "settings_locationUpdated": "位置已更新",
- "settings_locationBothRequired": "请输入纬度和经度。",
- "settings_locationInvalid": "无效的纬度或经度。",
+ "settings_locationSubtitle": "GPS 坐标",
+ "settings_locationUpdated": "位置和 GPS 设置已更新",
+ "settings_locationBothRequired": "请输入经度和纬度",
+ "settings_locationInvalid": "无效的经度和纬度",
+ "settings_locationGPSEnable": "启用 GPS",
+ "settings_locationGPSEnableSubtitle": "启用 GPS 以自动更新位置。",
+ "settings_locationIntervalSec": "GPS 间隔(秒)",
+ "settings_locationIntervalInvalid": "间隔时间必须至少为 60 秒,但不超过 86400 秒。",
"settings_latitude": "纬度",
"settings_longitude": "经度",
"settings_privacyMode": "隐私模式",
- "settings_privacyModeSubtitle": "隐藏在广告中的姓名/位置",
- "settings_privacyModeToggle": "开启隐私模式以隐藏您的姓名和位置在广告中的显示。",
+ "settings_privacyModeSubtitle": "在广告中隐藏姓名/位置",
+ "settings_privacyModeToggle": "切换隐私模式以在广告中隐藏姓名和位置,保护个人信息。",
"settings_privacyModeEnabled": "隐私模式已启用",
- "settings_privacyModeDisabled": "隐私模式已禁用",
+ "settings_privacyModeDisabled": "隐私模式已关闭",
"settings_actions": "操作",
- "settings_sendAdvertisement": "发送广告",
- "settings_sendAdvertisementSubtitle": "现在已广播",
- "settings_advertisementSent": "广告已发送",
+ "settings_sendAdvertisement": "发送广播",
+ "settings_sendAdvertisementSubtitle": "立即发送广播",
+ "settings_advertisementSent": "已发送广播",
"settings_syncTime": "同步时间",
- "settings_syncTimeSubtitle": "将设备时钟设置为手机时间",
- "settings_timeSynchronized": "时间同步",
+ "settings_syncTimeSubtitle": "将设备时钟设置为与手机时间一致",
+ "settings_timeSynchronized": "时间已同步",
"settings_refreshContacts": "刷新联系人",
"settings_refreshContactsSubtitle": "从设备重新加载联系人列表",
"settings_rebootDevice": "重启设备",
"settings_rebootDeviceSubtitle": "重启 MeshCore 设备",
- "settings_rebootDeviceConfirm": "您确定要重启设备吗?您将会断开连接。",
+ "settings_rebootDeviceConfirm": "确定要重启设备吗?这将断开与设备的连接。",
"settings_debug": "调试",
- "settings_bleDebugLog": "蓝牙调试日志",
- "settings_bleDebugLogSubtitle": "蓝牙命令、响应和原始数据",
+ "settings_bleDebugLog": "BLE 调试日志",
+ "settings_bleDebugLogSubtitle": "BLE 命令、响应和原始数据",
"settings_appDebugLog": "应用调试日志",
"settings_appDebugLogSubtitle": "应用调试消息",
"settings_about": "关于",
@@ -121,10 +134,10 @@
}
}
},
- "settings_aboutLegalese": "2024 MeshCore 开放源代码项目",
- "settings_aboutDescription": "一个开源的 Flutter 客户端,用于 MeshCore LoRa 网状网络设备。",
- "settings_infoName": "姓名",
- "settings_infoId": "ID",
+ "settings_aboutLegalese": "2026 MeshCore 开源项目",
+ "settings_aboutDescription": "一个开源的 Flutter 客户端,用于 MeshCore LoRa 无线网络设备。",
+ "settings_infoName": "名称",
+ "settings_infoId": "MAC ID",
"settings_infoStatus": "状态",
"settings_infoBattery": "电池",
"settings_infoPublicKey": "公钥",
@@ -136,15 +149,15 @@
"settings_preset433Mhz": "433 MHz",
"settings_frequency": "频率 (MHz)",
"settings_frequencyHelper": "300.0 - 2500.0",
- "settings_frequencyInvalid": "无效频率 (300-2500 MHz)",
+ "settings_frequencyInvalid": "无效频率范围(300-2500 MHz)",
"settings_bandwidth": "带宽",
- "settings_spreadingFactor": "扩散因子",
+ "settings_spreadingFactor": "扩频因子",
"settings_codingRate": "编码速率",
- "settings_txPower": "TX Power (dBm)",
+ "settings_txPower": "TX 功率 (dBm)",
"settings_txPowerHelper": "0 - 22",
- "settings_txPowerInvalid": "无效的 TX 电功率 (0-22 dBm)",
+ "settings_txPowerInvalid": "无效的发射功率(0-22 dBm)",
"settings_longRange": "远距离",
- "settings_fastSpeed": "快速速度",
+ "settings_fastSpeed": "高速",
"settings_error": "错误:{message}",
"@settings_error": {
"placeholders": {
@@ -156,48 +169,52 @@
"appSettings_title": "应用设置",
"appSettings_appearance": "外观",
"appSettings_theme": "主题",
- "appSettings_themeSystem": "系统默认",
- "appSettings_themeLight": "光",
+ "appSettings_themeSystem": "跟随系统",
+ "appSettings_themeLight": "浅色",
"appSettings_themeDark": "深色",
"appSettings_language": "语言",
- "appSettings_languageSystem": "系统默认",
- "appSettings_languageEn": "English",
- "appSettings_languageFr": "Français",
- "appSettings_languageEs": "Español",
- "appSettings_languageDe": "Deutsch",
- "appSettings_languagePl": "Polski",
- "appSettings_languageSl": "Slovenščina",
- "appSettings_languagePt": "Português",
- "appSettings_languageIt": "Italiano",
+ "appSettings_languageSystem": "跟随系统",
+ "appSettings_languageEn": "英语",
+ "appSettings_languageFr": "法语",
+ "appSettings_languageEs": "西班牙语",
+ "appSettings_languageDe": "德语",
+ "appSettings_languagePl": "波兰语",
+ "appSettings_languageSl": "斯洛文尼亚语",
+ "appSettings_languagePt": "葡萄牙语",
+ "appSettings_languageIt": "意大利语",
"appSettings_languageZh": "中文",
- "appSettings_languageSv": "Svenska",
- "appSettings_languageNl": "Nederlands",
- "appSettings_languageSk": "Slovenčina",
- "appSettings_languageBg": "Български",
+ "appSettings_languageSv": "瑞典语",
+ "appSettings_languageNl": "荷兰语",
+ "appSettings_languageSk": "斯洛伐克语",
+ "appSettings_languageBg": "保加利亚语",
+ "appSettings_languageRu": "俄语",
+ "appSettings_languageUk": "乌克兰语",
+ "appSettings_enableMessageTracing": "启用消息追踪",
+ "appSettings_enableMessageTracingSubtitle": "显示消息的详细路由和时间元数据",
"appSettings_notifications": "通知",
"appSettings_enableNotifications": "启用通知",
- "appSettings_enableNotificationsSubtitle": "接收消息和广告的通知",
- "appSettings_notificationPermissionDenied": "通知权限被拒绝",
+ "appSettings_enableNotificationsSubtitle": "接收消息和广播的通知",
+ "appSettings_notificationPermissionDenied": "权限被拒绝",
"appSettings_notificationsEnabled": "通知已启用",
"appSettings_notificationsDisabled": "通知已关闭",
"appSettings_messageNotifications": "消息通知",
- "appSettings_messageNotificationsSubtitle": "显示收到新消息时的通知",
+ "appSettings_messageNotificationsSubtitle": "收到新消息时显示通知",
"appSettings_channelMessageNotifications": "频道消息通知",
- "appSettings_channelMessageNotificationsSubtitle": "显示接收频道消息时的通知",
- "appSettings_advertisementNotifications": "广告通知",
- "appSettings_advertisementNotificationsSubtitle": "显示当新节点被发现时通知",
+ "appSettings_channelMessageNotificationsSubtitle": "收到频道消息时显示通知",
+ "appSettings_advertisementNotifications": "广播通知",
+ "appSettings_advertisementNotificationsSubtitle": "发现新节点时显示通知",
"appSettings_messaging": "消息",
- "appSettings_clearPathOnMaxRetry": "清除最大重试路径",
- "appSettings_clearPathOnMaxRetrySubtitle": "重置联系人路径,在5次发送失败尝试后",
- "appSettings_pathsWillBeCleared": "路径将在5次失败重试后清除",
- "appSettings_pathsWillNotBeCleared": "路径不会自动清理",
- "appSettings_autoRouteRotation": "自动路径旋转",
- "appSettings_autoRouteRotationSubtitle": "在最佳路径和洪水模式之间切换",
+ "appSettings_clearPathOnMaxRetry": "达到最大重试次数时清除路径",
+ "appSettings_clearPathOnMaxRetrySubtitle": "在5次发送失败后重置联系路径。",
+ "appSettings_pathsWillBeCleared": "5次失败后将重新路由",
+ "appSettings_pathsWillNotBeCleared": "路径不会自动清除",
+ "appSettings_autoRouteRotation": "自动路径轮换",
+ "appSettings_autoRouteRotationSubtitle": "在最佳路径和泛洪模式之间切换",
"appSettings_autoRouteRotationEnabled": "自动路径轮换已启用",
"appSettings_autoRouteRotationDisabled": "自动路径轮换已禁用",
"appSettings_battery": "电池",
- "appSettings_batteryChemistry": "电池化学",
- "appSettings_batteryChemistryPerDevice": "设置每个设备 ({deviceName})",
+ "appSettings_batteryChemistry": "电池类型",
+ "appSettings_batteryChemistryPerDevice": "为每个设备设置 ({deviceName})",
"@appSettings_batteryChemistryPerDevice": {
"placeholders": {
"deviceName": {
@@ -205,20 +222,20 @@
}
}
},
- "appSettings_batteryChemistryConnectFirst": "连接设备以选择",
- "appSettings_batteryNmc": "18650 NMC (3.0-4.2V)",
+ "appSettings_batteryChemistryConnectFirst": "请先连接设备",
+ "appSettings_batteryNmc": "18650 NMC 电池 (3.0-4.2V)",
"appSettings_batteryLifepo4": "磷酸铁锂 (2.6-3.65V)",
- "appSettings_batteryLipo": "LiPo (3.0-4.2V)",
+ "appSettings_batteryLipo": "锂聚合物电池 (3.0-4.2V)",
"appSettings_mapDisplay": "地图显示",
- "appSettings_showRepeaters": "显示循环器",
- "appSettings_showRepeatersSubtitle": "在地图上显示重复节点",
+ "appSettings_showRepeaters": "显示转发节点",
+ "appSettings_showRepeatersSubtitle": "在地图上显示转发节点",
"appSettings_showChatNodes": "显示聊天节点",
"appSettings_showChatNodesSubtitle": "在地图上显示聊天节点",
"appSettings_showOtherNodes": "显示其他节点",
- "appSettings_showOtherNodesSubtitle": "显示其他节点类型在地图上",
- "appSettings_timeFilter": "时间筛选",
+ "appSettings_showOtherNodesSubtitle": "在地图上显示其他节点类型",
+ "appSettings_timeFilter": "时间过滤器",
"appSettings_timeFilterShowAll": "显示所有节点",
- "appSettings_timeFilterShowLast": "显示来自过去 {hours} 小时的节点",
+ "appSettings_timeFilterShowLast": "显示过去 {hours} 小时内的节点",
"@appSettings_timeFilterShowLast": {
"placeholders": {
"hours": {
@@ -227,15 +244,15 @@
}
},
"appSettings_mapTimeFilter": "地图时间筛选",
- "appSettings_showNodesDiscoveredWithin": "显示发现的节点在:",
+ "appSettings_showNodesDiscoveredWithin": "显示在此时间段内发现的节点:",
"appSettings_allTime": "所有时间",
- "appSettings_lastHour": "最后小时",
- "appSettings_last6Hours": "最后6小时",
- "appSettings_last24Hours": "最后24小时",
+ "appSettings_lastHour": "过去一小时",
+ "appSettings_last6Hours": "过去6小时",
+ "appSettings_last24Hours": "过去24小时",
"appSettings_lastWeek": "上周",
"appSettings_offlineMapCache": "离线地图缓存",
"appSettings_noAreaSelected": "未选择任何区域",
- "appSettings_areaSelectedZoom": "选中的区域(缩放至 {minZoom} - {maxZoom})",
+ "appSettings_areaSelectedZoom": "已选择区域(缩放 {minZoom} - {maxZoom})",
"@appSettings_areaSelectedZoom": {
"placeholders": {
"minZoom": {
@@ -248,17 +265,17 @@
},
"appSettings_debugCard": "调试",
"appSettings_appDebugLogging": "应用调试日志",
- "appSettings_appDebugLoggingSubtitle": "记录应用调试消息以供故障排除",
- "appSettings_appDebugLoggingEnabled": "应用调试日志已启用",
+ "appSettings_appDebugLoggingSubtitle": "记录应用调试消息以进行故障排除。",
+ "appSettings_appDebugLoggingEnabled": "调试日志已启用",
"appSettings_appDebugLoggingDisabled": "应用调试日志已禁用",
"contacts_title": "联系人",
- "contacts_noContacts": "还没有联系人",
- "contacts_contactsWillAppear": "设备会广播时,联系人会显示",
+ "contacts_noContacts": "暂无联系人",
+ "contacts_contactsWillAppear": "当设备发送广播时,联系人将显示。",
"contacts_searchContacts": "搜索联系人...",
- "contacts_noUnreadContacts": "未读联系人",
- "contacts_noContactsFound": "未找到任何联系人或群组",
+ "contacts_noUnreadContacts": "没有未读内容",
+ "contacts_noContactsFound": "未找到任何联系人或群聊",
"contacts_deleteContact": "删除联系人",
- "contacts_removeConfirm": "从联系人中删除 {contactName} 吗?",
+ "contacts_removeConfirm": "从联系人中移除 {contactName}?",
"@contacts_removeConfirm": {
"placeholders": {
"contactName": {
@@ -266,12 +283,13 @@
}
}
},
- "contacts_manageRepeater": "管理重复项",
- "contacts_roomLogin": "房间登录",
+ "contacts_manageRepeater": "管理转发节点",
+ "contacts_manageRoom": "管理房间服务器",
+ "contacts_roomLogin": "服务器登录",
"contacts_openChat": "打开聊天",
- "contacts_editGroup": "编辑组",
- "contacts_deleteGroup": "删除分组",
- "contacts_deleteGroupConfirm": "删除\"{groupName}\"?",
+ "contacts_editGroup": "编辑群聊",
+ "contacts_deleteGroup": "删除群聊",
+ "contacts_deleteGroupConfirm": "删除群聊 \"{groupName}\"?",
"@contacts_deleteGroupConfirm": {
"placeholders": {
"groupName": {
@@ -279,10 +297,11 @@
}
}
},
- "contacts_newGroup": "新组",
- "contacts_groupName": "组名",
- "contacts_groupNameRequired": "组名不能为空",
- "contacts_groupAlreadyExists": "组“{name}”已存在",
+ "contacts_newGroup": "新建群聊",
+ "contacts_groupName": "群聊名称",
+ "contacts_groupNameRequired": "请输入群聊名称",
+ "contacts_groupNameReserved": "该群组名称已被保留",
+ "contacts_groupAlreadyExists": "名为 \"{name}\" 的群聊已存在",
"@contacts_groupAlreadyExists": {
"placeholders": {
"name": {
@@ -291,10 +310,10 @@
}
},
"contacts_filterContacts": "筛选联系人...",
- "contacts_noContactsMatchFilter": "未找到匹配您的筛选条件的结果",
- "contacts_noMembers": "没有会员",
- "contacts_lastSeenNow": "最后一次登录时间现在",
- "contacts_lastSeenMinsAgo": "最后一次出现 {minutes} 分前",
+ "contacts_noContactsMatchFilter": "没有符合条件的联系人",
+ "contacts_noMembers": "暂无成员",
+ "contacts_lastSeenNow": "刚刚",
+ "contacts_lastSeenMinsAgo": "最后在线 {minutes} 分钟前",
"@contacts_lastSeenMinsAgo": {
"placeholders": {
"minutes": {
@@ -302,8 +321,8 @@
}
}
},
- "contacts_lastSeenHourAgo": "最后一次出现前1小时",
- "contacts_lastSeenHoursAgo": "最后一次出现 {hours} 小时前",
+ "contacts_lastSeenHourAgo": "最后在线 1小时前",
+ "contacts_lastSeenHoursAgo": "最后在线 {hours} 小时前",
"@contacts_lastSeenHoursAgo": {
"placeholders": {
"hours": {
@@ -311,8 +330,8 @@
}
}
},
- "contacts_lastSeenDayAgo": "最后一次登录前一天",
- "contacts_lastSeenDaysAgo": "最后一次出现 {days} 天前",
+ "contacts_lastSeenDayAgo": "最后在线 1天前",
+ "contacts_lastSeenDaysAgo": "最后在线 {days} 天前",
"@contacts_lastSeenDaysAgo": {
"placeholders": {
"days": {
@@ -322,7 +341,7 @@
},
"channels_title": "频道",
"channels_noChannelsConfigured": "未配置任何频道",
- "channels_addPublicChannel": "添加公开频道",
+ "channels_addPublicChannel": "添加公共频道",
"channels_searchChannels": "搜索频道...",
"channels_noChannelsFound": "未找到任何频道",
"channels_channelIndex": "频道 {index}",
@@ -333,14 +352,16 @@
}
}
},
- "channels_hashtagChannel": "话题频道",
- "channels_public": "公开",
+ "channels_hashtagChannel": "标签频道",
+ "channels_public": "公共",
"channels_private": "私有",
- "channels_publicChannel": "公开频道",
- "channels_privateChannel": "私聊频道",
+ "channels_publicChannel": "公共频道",
+ "channels_privateChannel": "私有频道",
"channels_editChannel": "编辑频道",
+ "channels_muteChannel": "静音频道",
+ "channels_unmuteChannel": "取消静音频道",
"channels_deleteChannel": "删除频道",
- "channels_deleteChannelConfirm": "删除\"{name}\"?此操作无法撤销。",
+ "channels_deleteChannelConfirm": "删除频道 \"{name}\"?此操作不可撤销。",
"@channels_deleteChannelConfirm": {
"placeholders": {
"name": {
@@ -348,7 +369,7 @@
}
}
},
- "channels_channelDeleted": "频道“{name}”已删除",
+ "channels_channelDeleted": "已删除频道 \"{name}\"",
"@channels_channelDeleted": {
"placeholders": {
"name": {
@@ -360,12 +381,12 @@
"channels_channelIndexLabel": "频道索引",
"channels_channelName": "频道名称",
"channels_usePublicChannel": "使用公共频道",
- "channels_standardPublicPsk": "标准公钥共享密钥",
+ "channels_standardPublicPsk": "标准公共 PSK",
"channels_pskHex": "PSK (十六进制)",
- "channels_generateRandomPsk": "生成随机PSK",
+ "channels_generateRandomPsk": "生成随机 PSK",
"channels_enterChannelName": "请输入频道名称",
- "channels_pskMustBe32Hex": "PSK 必须是 32 个十六进制字符",
- "channels_channelAdded": "频道“{name}”已添加",
+ "channels_pskMustBe32Hex": "PSK 必须为 32 个十六进制字符",
+ "channels_channelAdded": "已添加频道 \"{name}\"",
"@channels_channelAdded": {
"placeholders": {
"name": {
@@ -382,7 +403,7 @@
}
},
"channels_smazCompression": "SMAZ 压缩",
- "channels_channelUpdated": "频道“{name}”已更新",
+ "channels_channelUpdated": "频道 \"{name}\" 已更新",
"@channels_channelUpdated": {
"placeholders": {
"name": {
@@ -390,16 +411,28 @@
}
}
},
- "channels_publicChannelAdded": "公共频道已添加",
- "channels_sortBy": "按类型排序",
+ "channels_publicChannelAdded": "已添加公共频道",
+ "channels_sortBy": "排序方式",
"channels_sortManual": "手动",
"channels_sortAZ": "A-Z",
"channels_sortLatestMessages": "最新消息",
"channels_sortUnread": "未读",
- "chat_noMessages": "目前还没有消息",
- "chat_sendMessageToStart": "发送消息开始",
+ "channels_createPrivateChannel": "创建私有频道",
+ "channels_createPrivateChannelDesc": "使用密钥保护。",
+ "channels_joinPrivateChannel": "加入私有频道",
+ "channels_joinPrivateChannelDesc": "手动输入密钥。",
+ "channels_joinPublicChannel": "加入公共频道",
+ "channels_joinPublicChannelDesc": "任何人都可以加入。",
+ "channels_joinHashtagChannel": "加入标签频道",
+ "channels_joinHashtagChannelDesc": "任何人都可以加入标签频道。",
+ "channels_scanQrCode": "扫描二维码",
+ "channels_scanQrCodeComingSoon": "即将推出",
+ "channels_enterHashtag": "输入标签",
+ "channels_hashtagHint": "例如:#团队",
+ "chat_noMessages": "暂无消息",
+ "chat_sendMessageToStart": "发送消息开始对话",
"chat_originalMessageNotFound": "找不到原始消息",
- "chat_replyingTo": "回复 {name}",
+ "chat_replyingTo": "正在回复 {name}",
"@chat_replyingTo": {
"placeholders": {
"name": {
@@ -416,7 +449,7 @@
}
},
"chat_location": "位置",
- "chat_sendMessageTo": "向{contactName}发送消息",
+ "chat_sendMessageTo": "发送消息给 {contactName}",
"@chat_sendMessageTo": {
"placeholders": {
"contactName": {
@@ -425,7 +458,7 @@
}
},
"chat_typeMessage": "输入消息...",
- "chat_messageTooLong": "消息太长了(最大 {maxBytes} 字节)。",
+ "chat_messageTooLong": "消息过长(最多 {maxBytes} 字节)",
"@chat_messageTooLong": {
"placeholders": {
"maxBytes": {
@@ -435,7 +468,7 @@
},
"chat_messageCopied": "消息已复制",
"chat_messageDeleted": "消息已删除",
- "chat_retryingMessage": "重试",
+ "chat_retryingMessage": "正在重试消息",
"chat_retryCount": "重试 {current}/{max}",
"@chat_retryCount": {
"placeholders": {
@@ -447,32 +480,32 @@
}
}
},
- "chat_sendGif": "发送GIF",
+ "chat_sendGif": "发送 GIF",
"chat_reply": "回复",
- "chat_addReaction": "添加反应",
+ "chat_addReaction": "添加表情",
"chat_me": "我",
- "emojiCategorySmileys": "表情符号",
+ "emojiCategorySmileys": "表情",
"emojiCategoryGestures": "手势",
- "emojiCategoryHearts": "心",
- "emojiCategoryObjects": "对象",
- "gifPicker_title": "选择一个 GIF",
- "gifPicker_searchHint": "搜索GIF...",
- "gifPicker_poweredBy": "由 GIPHY 提供支持",
- "gifPicker_noGifsFound": "未找到 GIF 动画",
- "gifPicker_failedLoad": "GIF 加载失败",
- "gifPicker_failedSearch": "搜索GIF失败",
+ "emojiCategoryHearts": "爱心",
+ "emojiCategoryObjects": "物品",
+ "gifPicker_title": "选择 GIF",
+ "gifPicker_searchHint": "搜索 GIF...",
+ "gifPicker_poweredBy": "由 GIPHY 提供",
+ "gifPicker_noGifsFound": "未找到 GIF",
+ "gifPicker_failedLoad": "加载 GIF 失败",
+ "gifPicker_failedSearch": "搜索 GIF 失败",
"gifPicker_noInternet": "无网络连接",
"debugLog_appTitle": "应用调试日志",
- "debugLog_bleTitle": "蓝牙调试日志",
+ "debugLog_bleTitle": "BLE 调试日志",
"debugLog_copyLog": "复制日志",
"debugLog_clearLog": "清除日志",
"debugLog_copied": "调试日志已复制",
- "debugLog_bleCopied": "蓝牙日志复制",
- "debugLog_noEntries": "尚未生成调试日志",
- "debugLog_enableInSettings": "启用应用调试日志记录设置",
+ "debugLog_bleCopied": "BLE 日志已复制",
+ "debugLog_noEntries": "暂无调试日志",
+ "debugLog_enableInSettings": "请在设置中启用应用调试日志。",
"debugLog_frames": "帧",
- "debugLog_rawLogRx": "原始日志-RX",
- "debugLog_noBleActivity": "目前还没有蓝牙活动。",
+ "debugLog_rawLogRx": "原始日志 RX",
+ "debugLog_noBleActivity": "暂无 BLE 活动",
"debugFrame_length": "帧长度:{count} 字节",
"@debugFrame_length": {
"placeholders": {
@@ -489,8 +522,8 @@
}
}
},
- "debugFrame_textMessageHeader": "短信框",
- "debugFrame_destinationPubKey": "- 目的地公钥:{pubKey}",
+ "debugFrame_textMessageHeader": "文本消息:",
+ "debugFrame_destinationPubKey": "- 目标公钥:{pubKey}",
"@debugFrame_destinationPubKey": {
"placeholders": {
"pubKey": {
@@ -525,9 +558,9 @@
}
}
},
- "debugFrame_textTypeCli": "CLI",
- "debugFrame_textTypePlain": "简洁",
- "debugFrame_text": "- 文本:\"{text}\"",
+ "debugFrame_textTypeCli": "命令行",
+ "debugFrame_textTypePlain": "纯文本",
+ "debugFrame_text": "- 文本:“{text}”",
"@debugFrame_text": {
"placeholders": {
"text": {
@@ -535,16 +568,16 @@
}
}
},
- "debugFrame_hexDump": "十六进制数据",
+ "debugFrame_hexDump": "十六进制数据:",
"chat_pathManagement": "路径管理",
"chat_routingMode": "路由模式",
- "chat_autoUseSavedPath": "自动(使用已保存路径)",
- "chat_forceFloodMode": "强制洪水模式",
- "chat_recentAckPaths": "最近的 ACK 路径 (点击以使用):",
- "chat_pathHistoryFull": "路径历史已满。删除条目以添加新条目。",
- "chat_hopSingular": "跳转",
- "chat_hopPlural": "跳跃",
- "chat_hopsCount": "{count} {count, plural, =1{跳跃} other{跳跃}}",
+ "chat_autoUseSavedPath": "自动(使用保存的路径)",
+ "chat_forceFloodMode": "强制泛洪模式",
+ "chat_recentAckPaths": "最近使用的 ACK 路径(点击使用):",
+ "chat_pathHistoryFull": "路径历史已满,请删除后再添加。",
+ "chat_hopSingular": "跳",
+ "chat_hopPlural": "跳",
+ "chat_hopsCount": "{count} 跳",
"@chat_hopsCount": {
"placeholders": {
"count": {
@@ -553,19 +586,19 @@
}
},
"chat_successes": "成功",
- "chat_removePath": "删除路径",
- "chat_noPathHistoryYet": "还没有历史记录。\n发送消息以发现路径。",
+ "chat_removePath": "移除路径",
+ "chat_noPathHistoryYet": "暂无路径历史。\n发送消息以探索路径。",
"chat_pathActions": "路径操作:",
"chat_setCustomPath": "设置自定义路径",
"chat_setCustomPathSubtitle": "手动指定路由路径",
"chat_clearPath": "清除路径",
- "chat_clearPathSubtitle": "强制下次发送时重新发现",
- "chat_pathCleared": "路径已清除。下一条消息将重新发现路线。",
- "chat_floodModeSubtitle": "使用应用栏中的路由切换开关",
- "chat_floodModeEnabled": "防洪模式已启用。通过应用程序栏中的路由图标进行反转。",
+ "chat_clearPathSubtitle": "清除当前路径,下次发送将重新尝试。",
+ "chat_pathCleared": "路径已清除。下一条消息将重新路由。",
+ "chat_floodModeSubtitle": "在应用栏中切换路由模式。",
+ "chat_floodModeEnabled": "泛洪模式已启用。可通过应用栏的路由图标切换。",
"chat_fullPath": "完整路径",
- "chat_pathDetailsNotAvailable": "路径详情尚未获取。请尝试发送消息以刷新。",
- "chat_pathSetHops": "路径设置:{hopCount} {hopCount, plural, =1{hop} other{hops}} - {status}",
+ "chat_pathDetailsNotAvailable": "路径信息暂不可用,请尝试发送消息刷新。",
+ "chat_pathSetHops": "路径设置:{hopCount} 跳 - {status}",
"@chat_pathSetHops": {
"placeholders": {
"hopCount": {
@@ -576,16 +609,16 @@
}
}
},
- "chat_pathSavedLocally": "已本地保存。连接以同步。",
+ "chat_pathSavedLocally": "已本地保存,连接设备后可同步。",
"chat_pathDeviceConfirmed": "设备已确认。",
"chat_pathDeviceNotConfirmed": "设备尚未确认。",
- "chat_type": "输入",
+ "chat_type": "类型",
"chat_path": "路径",
"chat_publicKey": "公钥",
"chat_compressOutgoingMessages": "压缩发送的消息",
- "chat_floodForced": "强制溢出",
- "chat_directForced": "强制直接",
- "chat_hopsForced": "{count} 次跳跃 (强制)",
+ "chat_floodForced": "泛洪(强制)",
+ "chat_directForced": "直连(强制)",
+ "chat_hopsForced": "{count} 跳(强制)",
"@chat_hopsForced": {
"placeholders": {
"count": {
@@ -593,9 +626,9 @@
}
}
},
- "chat_floodAuto": "自动防洪",
- "chat_direct": "直接",
- "chat_poiShared": "共享位置信息",
+ "chat_floodAuto": "自动泛洪",
+ "chat_direct": "直连",
+ "chat_poiShared": "共享位置",
"chat_unread": "未读:{count}",
"@chat_unread": {
"placeholders": {
@@ -604,9 +637,21 @@
}
}
},
+ "chat_openLink": "打开链接?",
+ "chat_openLinkConfirmation": "是否使用浏览器打开此链接?",
+ "chat_open": "打开",
+ "chat_couldNotOpenLink": "无法打开链接:{url}",
+ "@chat_couldNotOpenLink": {
+ "placeholders": {
+ "url": {
+ "type": "String"
+ }
+ }
+ },
+ "chat_invalidLink": "无效的链接格式",
"map_title": "节点地图",
- "map_noNodesWithLocation": "没有具有位置数据的节点",
- "map_nodesNeedGps": "节点需要共享它们的 GPS 坐标\n才能在地图上显示",
+ "map_noNodesWithLocation": "没有包含位置信息的节点",
+ "map_nodesNeedGps": "节点需要共享 GPS 坐标才能在地图上显示",
"map_nodesCount": "节点:{count}",
"@map_nodesCount": {
"placeholders": {
@@ -615,7 +660,7 @@
}
}
},
- "map_pinsCount": "针:{count}",
+ "map_pinsCount": "标记:{count}",
"@map_pinsCount": {
"placeholders": {
"count": {
@@ -624,26 +669,26 @@
}
},
"map_chat": "聊天",
- "map_repeater": "重复器",
+ "map_repeater": "转发节点",
"map_room": "房间",
"map_sensor": "传感器",
- "map_pinDm": "私信 (DM)",
- "map_pinPrivate": "私密模式",
- "map_pinPublic": "公开(公版)",
- "map_lastSeen": "最后一次登录",
- "map_disconnectConfirm": "您确定要断开与此设备的连接吗?",
- "map_from": "从",
+ "map_pinDm": "标记(私信)",
+ "map_pinPrivate": "私有",
+ "map_pinPublic": "公共",
+ "map_lastSeen": "最后在线",
+ "map_disconnectConfirm": "确定要断开与此设备的连接吗?",
+ "map_from": "来自",
"map_source": "来源",
- "map_flags": "旗帜",
- "map_shareMarkerHere": "分享标记在此",
- "map_pinLabel": "固定标签",
+ "map_flags": "标志",
+ "map_shareMarkerHere": "在此分享标记",
+ "map_pinLabel": "标签",
"map_label": "标签",
"map_pointOfInterest": "兴趣点",
"map_sendToContact": "发送给联系人",
"map_sendToChannel": "发送到频道",
"map_noChannelsAvailable": "没有可用的频道",
"map_publicLocationShare": "公共位置共享",
- "map_publicLocationShareConfirm": "您即将分享一个位置在 {channelLabel}。此频道公开,任何拥有 PSK 的人都可以看到它。",
+ "map_publicLocationShareConfirm": "您即将在 {channelLabel} 上分享一个位置。此频道是公开的,任何拥有 PSK 的人都可以看到。",
"@map_publicLocationShareConfirm": {
"placeholders": {
"channelLabel": {
@@ -652,25 +697,25 @@
}
},
"map_connectToShareMarkers": "连接设备以共享标记",
- "map_filterNodes": "筛选节点",
+ "map_filterNodes": "过滤节点",
"map_nodeTypes": "节点类型",
"map_chatNodes": "聊天节点",
- "map_repeaters": "重复器",
+ "map_repeaters": "转发节点",
"map_otherNodes": "其他节点",
- "map_keyPrefix": "键前缀",
- "map_filterByKeyPrefix": "按关键词前缀筛选",
- "map_publicKeyPrefix": "公钥前缀",
+ "map_keyPrefix": "关键字前缀",
+ "map_filterByKeyPrefix": "按关键字前缀筛选",
+ "map_publicKeyPrefix": "关键字前缀",
"map_markers": "标记",
"map_showSharedMarkers": "显示共享标记",
- "map_lastSeenTime": "最后一次查看时间",
- "map_sharedPin": "共享 PIN",
+ "map_lastSeenTime": "最后在线时间",
+ "map_sharedPin": "共享标记",
"map_joinRoom": "加入房间",
- "map_manageRepeater": "管理重复项",
+ "map_manageRepeater": "管理转发节点",
"mapCache_title": "离线地图缓存",
- "mapCache_selectAreaFirst": "选择一个区域进行缓存",
- "mapCache_noTilesToDownload": "该区域没有可下载的瓦片。",
+ "mapCache_selectAreaFirst": "请先选择要缓存的区域",
+ "mapCache_noTilesToDownload": "此区域没有可下载的瓦片",
"mapCache_downloadTilesTitle": "下载瓦片",
- "mapCache_downloadTilesPrompt": "下载 {count} 个瓦片用于离线使用?",
+ "mapCache_downloadTilesPrompt": "这需要下载 {count} 个瓦片",
"@mapCache_downloadTilesPrompt": {
"placeholders": {
"count": {
@@ -687,7 +732,7 @@
}
}
},
- "mapCache_cachedTilesWithFailed": "已缓存 {downloaded} 个瓦片 ({failed} 失败)",
+ "mapCache_cachedTilesWithFailed": "已缓存 {downloaded} 个瓦片({failed} 个失败)",
"@mapCache_cachedTilesWithFailed": {
"placeholders": {
"downloaded": {
@@ -699,13 +744,13 @@
}
},
"mapCache_clearOfflineCacheTitle": "清除离线缓存",
- "mapCache_clearOfflineCachePrompt": "删除所有缓存地图瓦片?",
+ "mapCache_clearOfflineCachePrompt": "清除所有缓存的地图瓦片",
"mapCache_offlineCacheCleared": "离线缓存已清除",
- "mapCache_noAreaSelected": "未选择任何区域",
+ "mapCache_noAreaSelected": "未选择区域",
"mapCache_cacheArea": "缓存区域",
"mapCache_useCurrentView": "使用当前视图",
"mapCache_zoomRange": "缩放范围",
- "mapCache_estimatedTiles": "预计瓦片数量:{count}",
+ "mapCache_estimatedTiles": "估计瓦片数:{count}",
"@mapCache_estimatedTiles": {
"placeholders": {
"count": {
@@ -713,7 +758,7 @@
}
}
},
- "mapCache_downloadedTiles": "已下载 {completed} / {total}",
+ "mapCache_downloadedTiles": "已下载 {completed}/{total}",
"@mapCache_downloadedTiles": {
"placeholders": {
"completed": {
@@ -768,7 +813,7 @@
}
}
},
- "time_daysAgo": "{days} 天前",
+ "time_daysAgo": "{days}天前",
"@time_daysAgo": {
"placeholders": {
"days": {
@@ -778,28 +823,28 @@
},
"time_hour": "小时",
"time_hours": "小时",
- "time_day": "今天",
+ "time_day": "天",
"time_days": "天",
- "time_week": "本周",
- "time_weeks": "几周",
- "time_month": "月份",
- "time_months": "月份",
+ "time_week": "周",
+ "time_weeks": "周",
+ "time_month": "月",
+ "time_months": "月",
"time_minutes": "分钟",
"time_allTime": "所有时间",
"dialog_disconnect": "断开",
- "dialog_disconnectConfirm": "您确定要断开与此设备的连接吗?",
- "login_repeaterLogin": "重复登录",
- "login_roomLogin": "房间登录",
+ "dialog_disconnectConfirm": "确定要断开与此设备的连接吗?",
+ "login_repeaterLogin": "转发节点登录",
+ "login_roomLogin": "房间服务器登录",
"login_password": "密码",
"login_enterPassword": "请输入密码",
"login_savePassword": "保存密码",
- "login_savePasswordSubtitle": "密码将安全地存储在这个设备上",
- "login_repeaterDescription": "输入重复密码以访问设置和状态。",
- "login_roomDescription": "输入房间密码以访问设置和状态。",
+ "login_savePasswordSubtitle": "密码将安全地存储在此设备上",
+ "login_repeaterDescription": "输入转发节点密码以访问设置和状态。",
+ "login_roomDescription": "输入房间服务器密码以访问设置和状态。",
"login_routing": "路由",
"login_routingMode": "路由模式",
- "login_autoUseSavedPath": "自动(使用已保存路径)",
- "login_forceFloodMode": "强制洪水模式",
+ "login_autoUseSavedPath": "自动(使用保存的路径)",
+ "login_forceFloodMode": "强制泛洪模式",
"login_managePaths": "管理路径",
"login_login": "登录",
"login_attempt": "尝试 {current}/{max}",
@@ -821,7 +866,7 @@
}
}
},
- "login_failedMessage": "登录失败。密码不正确或中继器不可达。",
+ "login_failedMessage": "登录失败。可能是密码错误或无法连接到服务器。",
"common_reload": "重新加载",
"common_clear": "清除",
"path_currentPath": "当前路径:{path}",
@@ -832,7 +877,7 @@
}
}
},
- "path_usingHopsPath": "使用 {count} {count, plural, =1{hop} other{hops}} 路径",
+ "path_usingHopsPath": "使用 {count} 跳路径",
"@path_usingHopsPath": {
"placeholders": {
"count": {
@@ -842,13 +887,13 @@
},
"path_enterCustomPath": "输入自定义路径",
"path_currentPathLabel": "当前路径",
- "path_hexPrefixInstructions": "输入2个字符的十六进制前缀,每个前缀之间用逗号分隔。",
- "path_hexPrefixExample": "A1,F2,3C (每个节点使用其公钥的第一字节)",
- "path_labelHexPrefixes": "十六进制前缀",
- "path_helperMaxHops": "最大 64 步跳。每个前缀是 2 个十六进制字符(1 字节)",
- "path_selectFromContacts": "或从联系人中选择:",
- "path_noRepeatersFound": "未找到任何重复器或房间服务器。",
- "path_customPathsRequire": "自定义路径需要中间跳转,这些跳转可以传递消息。",
+ "path_hexPrefixInstructions": "请输入每个中继节点的2字符十六进制前缀,用逗号分隔。",
+ "path_hexPrefixExample": "例如:A1, F2, 3C(每个节点使用其公钥的第一字节)",
+ "path_labelHexPrefixes": "路径(十六进制前缀)",
+ "path_helperMaxHops": "最多 64 跳。每个前缀由 2 个十六进制字符(1 字节)组成。",
+ "path_selectFromContacts": "或从联系人列表中选择:",
+ "path_noRepeatersFound": "未找到任何转发节点或房间服务器。",
+ "path_customPathsRequire": "自定义路径需要中间节点转发消息。",
"path_invalidHexPrefixes": "无效的十六进制前缀:{prefixes}",
"@path_invalidHexPrefixes": {
"placeholders": {
@@ -857,26 +902,29 @@
}
}
},
- "path_tooLong": "路径太长。允许的最大跳跃次数为 64 次。",
+ "path_tooLong": "路径过长,最多允许 64 跳。",
"path_setPath": "设置路径",
- "repeater_management": "重复器管理",
+ "repeater_management": "转发节点管理",
+ "room_management": "房间服务器管理",
"repeater_managementTools": "管理工具",
"repeater_status": "状态",
- "repeater_statusSubtitle": "查看重复器状态、统计信息和邻居",
+ "repeater_statusSubtitle": "查看转发节点状态、统计和邻居",
"repeater_telemetry": "遥测",
- "repeater_telemetrySubtitle": "查看传感器和系统状态的Telemetry数据",
- "repeater_cli": "CLI",
- "repeater_cliSubtitle": "发送命令到重复器",
+ "repeater_telemetrySubtitle": "查看传感器和系统状态数据",
+ "repeater_cli": "命令行",
+ "repeater_cliSubtitle": "向转发节点发送命令",
+ "repeater_neighbors": "邻居",
+ "repeater_neighborsSubtitle": "查看邻居节点(零跳)",
"repeater_settings": "设置",
- "repeater_settingsSubtitle": "配置重复器参数",
- "repeater_statusTitle": "重复器状态",
+ "repeater_settingsSubtitle": "配置转发节点参数",
+ "repeater_statusTitle": "转发节点状态",
"repeater_routingMode": "路由模式",
- "repeater_autoUseSavedPath": "自动(使用已保存路径)",
- "repeater_forceFloodMode": "强制洪水模式",
+ "repeater_autoUseSavedPath": "自动(使用保存的路径)",
+ "repeater_forceFloodMode": "强制泛洪模式",
"repeater_pathManagement": "路径管理",
"repeater_refresh": "刷新",
- "repeater_statusRequestTimeout": "状态请求超时。",
- "repeater_errorLoadingStatus": "错误加载状态:{error}",
+ "repeater_statusRequestTimeout": "状态请求超时",
+ "repeater_errorLoadingStatus": "加载状态时出错:{error}",
"@repeater_errorLoadingStatus": {
"placeholders": {
"error": {
@@ -886,19 +934,19 @@
},
"repeater_systemInformation": "系统信息",
"repeater_battery": "电池",
- "repeater_clockAtLogin": "时间 (登录时)",
- "repeater_uptime": "可用时间",
- "repeater_queueLength": "排队长度",
+ "repeater_clockAtLogin": "登录时的时钟",
+ "repeater_uptime": "运行时间",
+ "repeater_queueLength": "队列长度",
"repeater_debugFlags": "调试标志",
"repeater_radioStatistics": "无线电统计",
- "repeater_lastRssi": "上次RSSI",
- "repeater_lastSnr": "最后 SNR",
- "repeater_noiseFloor": "噪声地板",
- "repeater_txAirtime": "TX Airtime",
- "repeater_rxAirtime": "RX Airtime",
+ "repeater_lastRssi": "上次 RSSI",
+ "repeater_lastSnr": "上次 SNR",
+ "repeater_noiseFloor": "底噪",
+ "repeater_txAirtime": "发送空中时间",
+ "repeater_rxAirtime": "接收空中时间",
"repeater_packetStatistics": "数据包统计",
- "repeater_sent": "已发送",
- "repeater_received": "已收到",
+ "repeater_sent": "发送",
+ "repeater_received": "接收",
"repeater_duplicates": "重复",
"repeater_daysHoursMinsSecs": "{days}天 {hours}小时 {minutes}分 {seconds}秒",
"@repeater_daysHoursMinsSecs": {
@@ -917,7 +965,7 @@
}
}
},
- "repeater_packetTxTotal": "总计:{total}, 洪流:{flood}, 直连:{direct}",
+ "repeater_packetTxTotal": "总计:{total},泛洪:{flood},直连:{direct}",
"@repeater_packetTxTotal": {
"placeholders": {
"total": {
@@ -931,7 +979,7 @@
}
}
},
- "repeater_packetRxTotal": "总计:{total}, 洪流:{flood}, 直连:{direct}",
+ "repeater_packetRxTotal": "总计:{total},泛洪:{flood},直连:{direct}",
"@repeater_packetRxTotal": {
"placeholders": {
"total": {
@@ -945,7 +993,7 @@
}
}
},
- "repeater_duplicatesFloodDirect": "洪水:{flood}, 直通:{direct}",
+ "repeater_duplicatesFloodDirect": "泛洪:{flood},直连:{direct}",
"@repeater_duplicatesFloodDirect": {
"placeholders": {
"flood": {
@@ -964,36 +1012,36 @@
}
}
},
- "repeater_settingsTitle": "重复设置",
+ "repeater_settingsTitle": "转发节点设置",
"repeater_basicSettings": "基本设置",
- "repeater_repeaterName": "重复器名称",
- "repeater_repeaterNameHelper": "显示此重复器的名称",
+ "repeater_repeaterName": "转发节点名称",
+ "repeater_repeaterNameHelper": "此转发节点的显示名称",
"repeater_adminPassword": "管理员密码",
"repeater_adminPasswordHelper": "完整访问密码",
"repeater_guestPassword": "访客密码",
"repeater_guestPasswordHelper": "只读访问密码",
- "repeater_radioSettings": "射频设置",
+ "repeater_radioSettings": "无线电设置",
"repeater_frequencyMhz": "频率 (MHz)",
"repeater_frequencyHelper": "300-2500 MHz",
- "repeater_txPower": "TX Power",
+ "repeater_txPower": "TX 功率",
"repeater_txPowerHelper": "1-30 dBm",
"repeater_bandwidth": "带宽",
- "repeater_spreadingFactor": "扩散因子",
+ "repeater_spreadingFactor": "扩频因子",
"repeater_codingRate": "编码速率",
"repeater_locationSettings": "位置设置",
"repeater_latitude": "纬度",
- "repeater_latitudeHelper": "十进度的数字(例如:37.7749)",
+ "repeater_latitudeHelper": "十进制,例如 37.7749",
"repeater_longitude": "经度",
- "repeater_longitudeHelper": "十进度的数字(例如:-122.4194)",
+ "repeater_longitudeHelper": "十进制,例如 -122.4194",
"repeater_features": "功能",
"repeater_packetForwarding": "数据包转发",
- "repeater_packetForwardingSubtitle": "启用重复器以转发数据包",
+ "repeater_packetForwardingSubtitle": "启用转发节点转发数据包",
"repeater_guestAccess": "访客访问",
- "repeater_guestAccessSubtitle": "允许访客仅读访问",
+ "repeater_guestAccessSubtitle": "允许访客只读权限",
"repeater_privacyMode": "隐私模式",
- "repeater_privacyModeSubtitle": "隐藏在广告中的姓名/位置",
- "repeater_advertisementSettings": "广告设置",
- "repeater_localAdvertInterval": "本地广告间隔",
+ "repeater_privacyModeSubtitle": "在广播中隐藏姓名/位置",
+ "repeater_advertisementSettings": "广播设置",
+ "repeater_localAdvertInterval": "本地广播间隔",
"repeater_localAdvertIntervalMinutes": "{minutes} 分钟",
"@repeater_localAdvertIntervalMinutes": {
"placeholders": {
@@ -1002,7 +1050,7 @@
}
}
},
- "repeater_floodAdvertInterval": "洪水广告间隔",
+ "repeater_floodAdvertInterval": "泛洪广播间隔",
"repeater_floodAdvertIntervalHours": "{hours} 小时",
"@repeater_floodAdvertIntervalHours": {
"placeholders": {
@@ -1011,18 +1059,18 @@
}
}
},
- "repeater_encryptedAdvertInterval": "加密广告间隔",
- "repeater_dangerZone": "危险区域",
- "repeater_rebootRepeater": "重启重复器",
- "repeater_rebootRepeaterSubtitle": "重启重复器设备",
- "repeater_rebootRepeaterConfirm": "您确定要重启这个中继器吗?",
+ "repeater_encryptedAdvertInterval": "加密广播间隔",
+ "repeater_dangerZone": "危险设置",
+ "repeater_rebootRepeater": "重启转发节点",
+ "repeater_rebootRepeaterSubtitle": "重启转发节点设备",
+ "repeater_rebootRepeaterConfirm": "确定要重启此转发节点吗?",
"repeater_regenerateIdentityKey": "重新生成身份密钥",
"repeater_regenerateIdentityKeySubtitle": "生成新的公钥/私钥对",
- "repeater_regenerateIdentityKeyConfirm": "这将生成一个重复器的新身份。继续吗?",
- "repeater_eraseFileSystem": "删除文件系统",
- "repeater_eraseFileSystemSubtitle": "格式化重复文件系统",
- "repeater_eraseFileSystemConfirm": "警告:这将擦除重复器上的所有数据。 这无法撤销!",
- "repeater_eraseSerialOnly": "通过串行控制台才能删除。",
+ "repeater_regenerateIdentityKeyConfirm": "这将为转发节点生成新身份,继续吗?",
+ "repeater_eraseFileSystem": "擦除文件系统",
+ "repeater_eraseFileSystemSubtitle": "格式化转发节点文件系统",
+ "repeater_eraseFileSystemConfirm": "警告:此操作将清除转发节点上的所有数据,且无法恢复!",
+ "repeater_eraseSerialOnly": "擦除功能仅可通过串行控制台使用。",
"repeater_commandSent": "命令已发送:{command}",
"@repeater_commandSent": {
"placeholders": {
@@ -1040,8 +1088,8 @@
}
},
"repeater_confirm": "确认",
- "repeater_settingsSaved": "设置已保存成功",
- "repeater_errorSavingSettings": "保存设置出错:{error}",
+ "repeater_settingsSaved": "设置保存成功",
+ "repeater_errorSavingSettings": "保存设置时出错:{error}",
"@repeater_errorSavingSettings": {
"placeholders": {
"error": {
@@ -1051,12 +1099,12 @@
},
"repeater_refreshBasicSettings": "刷新基本设置",
"repeater_refreshRadioSettings": "刷新无线电设置",
- "repeater_refreshTxPower": "刷新 TX 电量",
+ "repeater_refreshTxPower": "刷新 TX 功率",
"repeater_refreshLocationSettings": "刷新位置设置",
"repeater_refreshPacketForwarding": "刷新包转发",
- "repeater_refreshGuestAccess": "刷新访客访问",
+ "repeater_refreshGuestAccess": "刷新访客权限",
"repeater_refreshPrivacyMode": "刷新隐私模式",
- "repeater_refreshAdvertisementSettings": "刷新广告设置",
+ "repeater_refreshAdvertisementSettings": "刷新广播设置",
"repeater_refreshed": "{label} 已刷新",
"@repeater_refreshed": {
"placeholders": {
@@ -1073,17 +1121,17 @@
}
}
},
- "repeater_cliTitle": "重复器命令行工具",
- "repeater_debugNextCommand": "调试下一步命令",
+ "repeater_cliTitle": "转发节点命令行",
+ "repeater_debugNextCommand": "调试下一条命令",
"repeater_commandHelp": "帮助",
"repeater_clearHistory": "清除历史",
- "repeater_noCommandsSent": "尚未发送任何命令",
+ "repeater_noCommandsSent": "尚未发送命令",
"repeater_typeCommandOrUseQuick": "输入命令或使用快捷命令",
"repeater_enterCommandHint": "输入命令...",
- "repeater_previousCommand": "上一个命令",
- "repeater_nextCommand": "下一步命令",
- "repeater_enterCommandFirst": "请输入一个命令",
- "repeater_cliCommandFrameTitle": "CLI 命令窗口",
+ "repeater_previousCommand": "上一条命令",
+ "repeater_nextCommand": "下一条命令",
+ "repeater_enterCommandFirst": "请先输入命令",
+ "repeater_cliCommandFrameTitle": "CLI 命令帧",
"repeater_cliCommandError": "错误:{error}",
"@repeater_cliCommandError": {
"placeholders": {
@@ -1092,81 +1140,81 @@
}
}
},
- "repeater_cliQuickGetName": "获取姓名",
- "repeater_cliQuickGetRadio": "获取收音机",
+ "repeater_cliQuickGetName": "获取名称",
+ "repeater_cliQuickGetRadio": "获取无线电设置",
"repeater_cliQuickGetTx": "获取 TX",
"repeater_cliQuickNeighbors": "邻居",
"repeater_cliQuickVersion": "版本",
- "repeater_cliQuickAdvertise": "发布",
+ "repeater_cliQuickAdvertise": "发送广播",
"repeater_cliQuickClock": "时钟",
- "repeater_cliHelpAdvert": "发送广告包",
- "repeater_cliHelpReboot": "重启设备。(请注意,可能会出现“超时”现象,这是正常现象)",
- "repeater_cliHelpClock": "显示每个设备的当前时间。",
- "repeater_cliHelpPassword": "设置设备的新管理员密码。",
- "repeater_cliHelpVersion": "显示设备版本和固件构建日期。",
- "repeater_cliHelpClearStats": "重置各种统计数值为零。",
- "repeater_cliHelpSetAf": "设置空闲时间因子。",
- "repeater_cliHelpSetTx": "设置 LoRa 传输功率 (重置生效)",
- "repeater_cliHelpSetRepeat": "启用或禁用此节点的重复器角色。",
- "repeater_cliHelpSetAllowReadOnly": "(房间服务器) 如果“开”了,则空密码登录将被允许,但不能向房间发布内容。(仅限读取)",
- "repeater_cliHelpSetFloodMax": "设置最大换路包数量(如果 >= 最大,则不转发包)。",
- "repeater_cliHelpSetIntThresh": "设置干扰阈值(以 dB 为单位)。默认值为 14。将设置为 0 以禁用通道干扰检测。",
- "repeater_cliHelpSetAgcResetInterval": "设置间隔以重置自动增益控制器。将设置为 0 以禁用。",
- "repeater_cliHelpSetMultiAcks": "启用或禁用“双 ACKs”功能。",
- "repeater_cliHelpSetAdvertInterval": "设置定时器间隔时间为分钟,以发送本地(零跳)广告包。将设置为0以禁用。",
- "repeater_cliHelpSetFloodAdvertInterval": "设置定时器间隔时间为小时,以发送洪水广告包。将设置为 0 以禁用。",
- "repeater_cliHelpSetGuestPassword": "设置/更新客人密码。(对于重复器,客人在登录时可以发送“获取统计”请求)",
- "repeater_cliHelpSetName": "设置广告名称。",
- "repeater_cliHelpSetLat": "设置广告地图纬度(十进制度)",
- "repeater_cliHelpSetLon": "设置广告地图经度 (十进位)",
- "repeater_cliHelpSetRadio": "设置全新的无线电参数,并保存到偏好设置。需要执行“重启”命令才能应用。",
- "repeater_cliHelpSetRxDelay": "设置(实验性)的基础(必须大于 1 才能生效)是用于对接收到的数据包应用轻微延迟,基于信号强度/得分。将设置为 0 以禁用。",
- "repeater_cliHelpSetTxDelay": "设置一个与时间-在空气中(time-on-air)的系数,用于洪水模式的数据包,并结合随机插槽系统,以延迟其转发。(以降低碰撞的可能性)",
- "repeater_cliHelpSetDirectTxDelay": "与txdelay相同,但用于为直接模式包的转发应用随机延迟。",
- "repeater_cliHelpSetBridgeEnabled": "启用/禁用桥梁",
- "repeater_cliHelpSetBridgeDelay": "设置在重新发送数据包之前延迟时间。",
- "repeater_cliHelpSetBridgeSource": "选择桥梁是否会重传接收到的数据包或发送的数据包。",
- "repeater_cliHelpSetBridgeBaud": "设置rs232桥接的串口链路波特率。",
- "repeater_cliHelpSetBridgeSecret": "设置 espnow 桥的秘密。",
- "repeater_cliHelpSetAdcMultiplier": "设置自定义因子以调整报告的电池电压(仅限部分板卡支持)。",
- "repeater_cliHelpTempRadio": "设置临时无线电参数,持续指定的分钟数,之后恢复为原始无线电参数。(不保存到偏好设置)。",
- "repeater_cliHelpSetPerm": "修改ACL。如果“权限”为零,则删除匹配的条目(通过pubkey前缀)。如果pubkey-hex的完整长度且当前不在ACL中,则添加新条目。通过匹配pubkey前缀更新条目。权限位因固件角色而异,但低2位为:0(Guest)、1(只读)、2(读写)、3(Admin)",
- "repeater_cliHelpGetBridgeType": "获取桥接类型:无,RS232,ESPNow",
- "repeater_cliHelpLogStart": "开始将数据包记录到文件系统。",
- "repeater_cliHelpLogStop": "停止将数据包记录到文件系统。",
- "repeater_cliHelpLogErase": "删除文件系统中的包日志。",
- "repeater_cliHelpNeighbors": "显示通过零跳广告收听的其他重复节点列表。 每行是 id-prefix-hex:时间戳:snr-times-4",
- "repeater_cliHelpNeighborRemove": "移除邻居列表中第一个匹配的条目(通过十六进制 pubkey 前缀)。",
- "repeater_cliHelpRegion": "(仅显示区域) 列出所有已定义的区域和当前的防洪权限。",
- "repeater_cliHelpRegionLoad": "注意:这是一个特殊的多命令调用。 随后的每个命令都是一个区域名称(用空格缩进以指示父级层次结构,至少需要一个空格)。 以发送一个空行/命令结束。",
- "repeater_cliHelpRegionGet": "搜索具有给定名称前缀的区域(或“”用于全局范围)。回复为“-> region-name (parent-name) ‘F’”",
- "repeater_cliHelpRegionPut": "添加或更新区域定义,使用指定名称。",
- "repeater_cliHelpRegionRemove": "删除指定名称的区域定义。(必须没有子区域)",
- "repeater_cliHelpRegionAllowf": "设置指定区域的“洪水”权限。(“”代表全局/遗留范围)",
- "repeater_cliHelpRegionDenyf": "移除指定区域的‘F’lood权限。 (注意:目前阶段不建议在此范围内使用,尤其是全局/旧版范围!!)",
- "repeater_cliHelpRegionHome": "回复当前“主页”区域。 (注意尚未应用,保留用于未来)",
- "repeater_cliHelpRegionHomeSet": "设置‘主页’区域。",
- "repeater_cliHelpRegionSave": "保存区域列表/地图到存储。",
- "repeater_cliHelpGps": "显示GPS状态。当GPS关闭时,回复仅为“关闭”,如果已开启,则回复为“开启”、“状态”、“定位”和卫星数量。",
- "repeater_cliHelpGpsOnOff": "切换 GPS 开启状态。",
- "repeater_cliHelpGpsSync": "同步节点时间与 GPS 钟。",
- "repeater_cliHelpGpsSetLoc": "设置节点位置至 GPS 坐标并保存偏好设置。",
- "repeater_cliHelpGpsAdvert": "提供节点广告配置位置:\n- none:不包含位置在广告中\n- share:分享 GPS 位置(来自 SensorManager)\n- prefs:在偏好设置中投放位置",
- "repeater_cliHelpGpsAdvertSet": "设置广告位置配置。",
+ "repeater_cliHelpAdvert": "发送广播包",
+ "repeater_cliHelpReboot": "重启设备。(注意:可能会收到超时错误,属于正常现象)",
+ "repeater_cliHelpClock": "显示设备当前时间",
+ "repeater_cliHelpPassword": "设置新的管理员密码",
+ "repeater_cliHelpVersion": "显示设备版本和固件构建日期",
+ "repeater_cliHelpClearStats": "重置各种统计数据",
+ "repeater_cliHelpSetAf": "设置时间因子",
+ "repeater_cliHelpSetTx": "设置 LoRa 发射功率 (dBm)(重启生效)",
+ "repeater_cliHelpSetRepeat": "启用或禁用此节点的转发功能",
+ "repeater_cliHelpSetAllowReadOnly": "(房间服务器)设为“开”则允许空密码登录,但只能读(不能发送)",
+ "repeater_cliHelpSetFloodMax": "设置最大传入数据包跳数(≥该值则不转发)",
+ "repeater_cliHelpSetIntThresh": "设置干扰阈值 (dB),默认14,设为0禁用",
+ "repeater_cliHelpSetAgcResetInterval": "设置 AGC 重置间隔(秒),设为0禁用",
+ "repeater_cliHelpSetMultiAcks": "启用或禁用“多重确认”功能",
+ "repeater_cliHelpSetAdvertInterval": "设置本地广播间隔(分钟),设为0禁用",
+ "repeater_cliHelpSetFloodAdvertInterval": "设置泛洪广播间隔(小时),设为0禁用",
+ "repeater_cliHelpSetGuestPassword": "设置/更新访客密码",
+ "repeater_cliHelpSetName": "设置广播名称",
+ "repeater_cliHelpSetLat": "设置广播纬度(十进制)",
+ "repeater_cliHelpSetLon": "设置广播经度(十进制)",
+ "repeater_cliHelpSetRadio": "完全重设无线电参数并保存,需重启生效",
+ "repeater_cliHelpSetRxDelay": "(实验性)设置接收延迟基数,设为0禁用",
+ "repeater_cliHelpSetTxDelay": "通过因子和随机时隙延迟泛洪数据包转发,降低冲突",
+ "repeater_cliHelpSetDirectTxDelay": "同 txdelay,用于直连模式数据包",
+ "repeater_cliHelpSetBridgeEnabled": "启用/禁用桥接",
+ "repeater_cliHelpSetBridgeDelay": "设置桥接转发延迟",
+ "repeater_cliHelpSetBridgeSource": "选择桥接器转发接收或发送的数据包",
+ "repeater_cliHelpSetBridgeBaud": "设置 RS232 桥接串口波特率",
+ "repeater_cliHelpSetBridgeSecret": "设置 ESPNOW 桥接密钥",
+ "repeater_cliHelpSetAdcMultiplier": "设置电池电压校正系数(特定板支持)",
+ "repeater_cliHelpTempRadio": "临时设置无线电参数指定分钟,之后恢复(不保存)",
+ "repeater_cliHelpSetPerm": "修改 ACL,权限位:0访客、1只读、2读写、3管理员",
+ "repeater_cliHelpGetBridgeType": "支持桥接模式:RS232、ESPNOW",
+ "repeater_cliHelpLogStart": "开始记录数据包到文件系统",
+ "repeater_cliHelpLogStop": "停止记录数据包",
+ "repeater_cliHelpLogErase": "删除所有记录的数据包",
+ "repeater_cliHelpNeighbors": "显示零跳广播收到的其他转发节点列表",
+ "repeater_cliHelpNeighborRemove": "从邻居列表删除第一个匹配项(通过公钥前缀)",
+ "repeater_cliHelpRegion": "(仅串口)列出所有定义区域及当前泛洪权限",
+ "repeater_cliHelpRegionLoad": "特殊多命令调用,以空行结束",
+ "repeater_cliHelpRegionGet": "搜索指定前缀的区域",
+ "repeater_cliHelpRegionPut": "添加或更新区域定义",
+ "repeater_cliHelpRegionRemove": "删除指定区域定义",
+ "repeater_cliHelpRegionAllowf": "为区域设置“泛洪”权限",
+ "repeater_cliHelpRegionDenyf": "移除区域的“泛洪”权限",
+ "repeater_cliHelpRegionHome": "返回当前“主区域”(预留)",
+ "repeater_cliHelpRegionHomeSet": "设置“主”区域",
+ "repeater_cliHelpRegionSave": "保存区域列表到存储",
+ "repeater_cliHelpGps": "显示 GPS 状态",
+ "repeater_cliHelpGpsOnOff": "切换 GPS 电源",
+ "repeater_cliHelpGpsSync": "将节点时间与 GPS 同步",
+ "repeater_cliHelpGpsSetLoc": "将节点坐标设为 GPS 坐标并保存",
+ "repeater_cliHelpGpsAdvert": "设置位置广播配置:none/share/prefs",
+ "repeater_cliHelpGpsAdvertSet": "设置广播位置配置",
"repeater_commandsListTitle": "命令列表",
- "repeater_commandsListNote": "注意:对于各种“设置...”命令,也存在“获取...”命令。",
+ "repeater_commandsListNote": "注意:多数 set 命令也有对应的 get 命令",
"repeater_general": "通用",
"repeater_settingsCategory": "设置",
- "repeater_bridge": "桥",
- "repeater_logging": "记录",
- "repeater_neighborsRepeaterOnly": "邻居(仅限重复器)",
- "repeater_regionManagementRepeaterOnly": "区域管理(仅限重复器)",
- "repeater_regionNote": "区域命令已推出,用于管理区域定义和权限。",
- "repeater_gpsManagement": "GPS管理",
- "repeater_gpsNote": "GPS 命令已引入用于管理与位置相关的主题。",
- "telemetry_receivedData": "接收遥测数据",
- "telemetry_requestTimeout": "遥测请求超时。",
- "telemetry_errorLoading": "错误加载遥测数据:{error}",
+ "repeater_bridge": "桥接",
+ "repeater_logging": "日志",
+ "repeater_neighborsRepeaterOnly": "邻居(仅转发节点)",
+ "repeater_regionManagementRepeaterOnly": "区域管理(仅转发节点)",
+ "repeater_regionNote": "区域命令用于管理区域定义和权限",
+ "repeater_gpsManagement": "GPS 管理",
+ "repeater_gpsNote": "GPS 命令用于位置相关任务",
+ "telemetry_receivedData": "接收到的遥测数据",
+ "telemetry_requestTimeout": "遥测请求超时",
+ "telemetry_errorLoading": "加载遥测数据时出错:{error}",
"@telemetry_errorLoading": {
"placeholders": {
"error": {
@@ -1174,7 +1222,7 @@
}
}
},
- "telemetry_noData": "没有可用的 telemetry 数据。",
+ "telemetry_noData": "暂无遥测数据",
"telemetry_channelTitle": "频道 {channel}",
"@telemetry_channelTitle": {
"placeholders": {
@@ -1187,7 +1235,7 @@
"telemetry_voltageLabel": "电压",
"telemetry_mcuTemperatureLabel": "MCU 温度",
"telemetry_temperatureLabel": "温度",
- "telemetry_currentLabel": "当前",
+ "telemetry_currentLabel": "电流",
"telemetry_batteryValue": "{percent}% / {volts}V",
"@telemetry_batteryValue": {
"placeholders": {
@@ -1226,18 +1274,46 @@
}
}
},
+ "neighbors_receivedData": "已接收邻居信息",
+ "neighbors_requestTimedOut": "邻居请求超时",
+ "neighbors_errorLoading": "加载邻居时出错:{error}",
+ "@neighbors_errorLoading": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_repeatersNeighbors": "转发节点的邻居",
+ "neighbors_noData": "暂无邻居信息",
+ "neighbors_unknownContact": "未知 {pubkey}",
+ "@neighbors_unknownContact": {
+ "placeholders": {
+ "pubkey": {
+ "type": "String"
+ }
+ }
+ },
+ "neighbors_heardAgo": "听到:{time}前",
+ "@neighbors_heardAgo": {
+ "placeholders": {
+ "time": {
+ "type": "String"
+ }
+ }
+ },
"channelPath_title": "数据包路径",
"channelPath_viewMap": "查看地图",
"channelPath_otherObservedPaths": "其他观察到的路径",
- "channelPath_repeaterHops": "重复跳跃",
- "channelPath_noHopDetails": "此包的详细信息未提供。",
+ "channelPath_repeaterHops": "转发节点跳数",
+ "channelPath_noHopDetails": "此数据包未提供详细信息",
"channelPath_messageDetails": "消息详情",
- "channelPath_senderLabel": "发件人",
+ "channelPath_senderLabel": "发送者",
"channelPath_timeLabel": "时间",
"channelPath_repeatsLabel": "重复",
"channelPath_pathLabel": "路径 {index}",
- "channelPath_observedLabel": "已观察",
- "channelPath_observedPathTitle": "观察路径 {index} • {hops}",
+ "channelPath_observedLabel": "观察到的",
+ "channelPath_observedPathTitle": "观察到的路径 {index} • {hops}",
"@channelPath_observedPathTitle": {
"placeholders": {
"index": {
@@ -1248,7 +1324,7 @@
}
}
},
- "channelPath_noLocationData": "没有位置数据",
+ "channelPath_noLocationData": "无位置信息",
"channelPath_timeWithDate": "{day}/{month} {time}",
"@channelPath_timeWithDate": {
"placeholders": {
@@ -1272,9 +1348,9 @@
}
},
"channelPath_unknownPath": "未知",
- "channelPath_floodPath": "洪水",
- "channelPath_directPath": "直接",
- "channelPath_observedZeroOf": "0 of {total} 跳跃",
+ "channelPath_floodPath": "泛洪",
+ "channelPath_directPath": "直连",
+ "channelPath_observedZeroOf": "0 / {total} 跳",
"@channelPath_observedZeroOf": {
"placeholders": {
"total": {
@@ -1282,7 +1358,7 @@
}
}
},
- "channelPath_observedSomeOf": "已观察到 {observed} 步中的 {total} 步",
+ "channelPath_observedSomeOf": "{observed} / {total} 跳",
"@channelPath_observedSomeOf": {
"placeholders": {
"observed": {
@@ -1294,8 +1370,8 @@
}
},
"channelPath_mapTitle": "路径地图",
- "channelPath_noRepeaterLocations": "此路径没有可用的重复器位置。",
- "channelPath_primaryPath": "路径 {index} (主)",
+ "channelPath_noRepeaterLocations": "此路径上没有可用的转发节点位置信息",
+ "channelPath_primaryPath": "路径 {index}(主要)",
"@channelPath_primaryPath": {
"placeholders": {
"index": {
@@ -1311,7 +1387,7 @@
}
},
"channelPath_pathLabelTitle": "路径",
- "channelPath_observedPathHeader": "已观察路径",
+ "channelPath_observedPathHeader": "观察到的路径",
"channelPath_selectedPathLabel": "{label} • {prefixes}",
"@channelPath_selectedPathLabel": {
"placeholders": {
@@ -1323,68 +1399,14 @@
}
}
},
- "channelPath_noHopDetailsAvailable": "此包的跳跃详情不可用。",
- "channelPath_unknownRepeater": "未知重复器",
- "listFilter_tooltip": "筛选和排序",
- "listFilter_sortBy": "按类型排序",
- "listFilter_latestMessages": "最新消息",
- "listFilter_heardRecently": "最近听说",
- "listFilter_az": "A-Z",
- "listFilter_filters": "筛选",
- "listFilter_all": "全部",
- "listFilter_users": "用户",
- "listFilter_repeaters": "重复器",
- "listFilter_roomServers": "房间服务器",
- "listFilter_unreadOnly": "未读消息",
- "listFilter_newGroup": "新组",
- "@neighbors_errorLoading": {
- "placeholders": {
- "error": {
- "type": "String"
- }
- }
- },
- "repeater_neighboursSubtitle": "查看零跳邻居。",
- "repeater_neighbours": "邻居",
- "neighbors_receivedData": "收到邻居数据",
- "neighbors_requestTimedOut": "邻居请求超时处理。",
- "neighbors_errorLoading": "加载邻居时出错:{error}",
- "neighbors_repeatersNeighbours": "重复器邻居",
- "neighbors_noData": "没有可用的邻居数据。",
- "channels_joinPrivateChannel": "加入私密频道",
- "channels_createPrivateChannelDesc": "使用密钥保护。",
- "channels_joinPrivateChannelDesc": "手动输入密钥。",
- "channels_createPrivateChannel": "创建私聊频道",
- "channels_joinPublicChannel": "加入公共频道",
- "channels_joinPublicChannelDesc": "任何人都可以加入这个频道。",
- "channels_joinHashtagChannel": "加入标签频道",
- "channels_joinHashtagChannelDesc": "任何人都可以加入话题频道。",
- "channels_scanQrCode": "扫描二维码",
- "channels_scanQrCodeComingSoon": "即将到来",
- "channels_enterHashtag": "输入标签",
- "channels_hashtagHint": "例如 #团队",
- "@neighbors_unknownContact": {
- "placeholders": {
- "pubkey": {
- "type": "String"
- }
- }
- },
- "@neighbors_heardAgo": {
- "placeholders": {
- "time": {
- "type": "String"
- }
- }
- },
- "neighbors_heardAgo": "听到的时间:{time}前",
- "neighbors_unknownContact": "未知{pubkey}",
- "settings_locationGPSEnable": "启用GPS",
- "settings_locationGPSEnableSubtitle": "启用GPS自动更新位置。",
- "settings_locationIntervalSec": "GPS 间隔(秒)",
- "settings_locationIntervalInvalid": "时间间隔必须至少为60秒,且小于86400秒。",
- "contacts_manageRoom": "管理房间服务器",
- "room_management": "房间服务器管理",
+ "channelPath_noHopDetailsAvailable": "此数据包暂无详细信息",
+ "channelPath_unknownRepeater": "未知转发节点",
+ "community_title": "社区",
+ "community_create": "创建社区",
+ "community_createDesc": "创建新社区并通过二维码分享。",
+ "community_join": "加入",
+ "community_joinTitle": "加入社区",
+ "community_joinConfirmation": "是否加入社区 \"{name}\"?",
"@community_joinConfirmation": {
"placeholders": {
"name": {
@@ -1392,6 +1414,14 @@
}
}
},
+ "community_scanQr": "扫描社区二维码",
+ "community_scanInstructions": "将摄像头对准社区的二维码",
+ "community_showQr": "显示二维码",
+ "community_publicChannel": "社区公共频道",
+ "community_hashtagChannel": "社区标签频道",
+ "community_name": "社区名称",
+ "community_enterName": "请输入社区名称",
+ "community_created": "社区 \"{name}\" 已创建",
"@community_created": {
"placeholders": {
"name": {
@@ -1399,6 +1429,7 @@
}
}
},
+ "community_joined": "已加入社区 \"{name}\"",
"@community_joined": {
"placeholders": {
"name": {
@@ -1406,6 +1437,8 @@
}
}
},
+ "community_qrTitle": "分享社区",
+ "community_qrInstructions": "扫描此二维码加入 \"{name}\"",
"@community_qrInstructions": {
"placeholders": {
"name": {
@@ -1413,6 +1446,10 @@
}
}
},
+ "community_hashtagPrivacyHint": "仅社区成员可加入社区标签频道。",
+ "community_invalidQrCode": "无效的社区二维码",
+ "community_alreadyMember": "已是成员",
+ "community_alreadyMemberMessage": "您已是 \"{name}\" 的成员。",
"@community_alreadyMemberMessage": {
"placeholders": {
"name": {
@@ -1420,6 +1457,13 @@
}
}
},
+ "community_addPublicChannel": "添加公共频道",
+ "community_addPublicChannelHint": "自动添加此社区的公共频道",
+ "community_noCommunities": "尚未加入任何社区。",
+ "community_scanOrCreate": "扫描二维码或创建社区以开始。",
+ "community_manageCommunities": "管理社区",
+ "community_delete": "退出社区",
+ "community_deleteConfirm": "是否退出 \"{name}\"?",
"@community_deleteConfirm": {
"placeholders": {
"name": {
@@ -1427,6 +1471,15 @@
}
}
},
+ "community_deleteChannelsWarning": "这将同时删除 {count} 个频道及其所有消息。",
+ "@community_deleteChannelsWarning": {
+ "placeholders": {
+ "count": {
+ "type": "int"
+ }
+ }
+ },
+ "community_deleted": "已退出社区 \"{name}\"",
"@community_deleted": {
"placeholders": {
"name": {
@@ -1434,6 +1487,49 @@
}
}
},
+ "community_regenerateSecret": "重新生成密钥",
+ "community_regenerateSecretConfirm": "是否为 \"{name}\" 重新生成密钥?所有成员需扫描新的二维码才能继续通信。",
+ "@community_regenerateSecretConfirm": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_regenerate": "重新生成",
+ "community_secretRegenerated": "已为 \"{name}\" 重新生成密钥",
+ "@community_secretRegenerated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_updateSecret": "更新密钥",
+ "community_secretUpdated": "“{name}”的密钥已更新",
+ "@community_secretUpdated": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_scanToUpdateSecret": "扫描新二维码以更新 \"{name}\" 的密钥",
+ "@community_scanToUpdateSecret": {
+ "placeholders": {
+ "name": {
+ "type": "String"
+ }
+ }
+ },
+ "community_addHashtagChannel": "添加标签频道",
+ "community_addHashtagChannelDesc": "为此社区创建标签频道",
+ "community_selectCommunity": "选择社区",
+ "community_regularHashtag": "普通标签",
+ "community_regularHashtagDesc": "公共标签频道(任何人都可参与)",
+ "community_communityHashtag": "社区标签",
+ "community_communityHashtagDesc": "仅限社区成员",
+ "community_forCommunity": "为 {name}",
"@community_forCommunity": {
"placeholders": {
"name": {
@@ -1441,48 +1537,546 @@
}
}
},
- "community_create": "创建社区",
- "community_title": "社区",
- "community_createDesc": "创建新的社区并可通过二维码分享。",
- "common_ok": "好的",
- "community_join": "加入",
- "community_joinTitle": "加入社区",
- "community_joinConfirmation": "您想加入社区 \"{name}\" 吗?",
- "community_scanQr": "扫描社区二维码",
- "community_scanInstructions": "将相机对准社区二维码",
- "community_showQr": "显示二维码",
- "community_publicChannel": "社区公开",
- "community_hashtagChannel": "社区标签",
- "community_name": "社区名称",
- "community_enterName": "请输入社区名称",
- "community_created": "社区“{name}”已创建",
- "community_joined": "加入社区 \"{name}\"",
- "community_qrTitle": "分享社区",
- "community_qrInstructions": "扫描此二维码加入{name}",
- "community_hashtagPrivacyHint": "社区标签频道仅社区成员可加入",
- "community_invalidQrCode": "无效的社区二维码",
- "community_alreadyMember": "已经是会员了",
- "community_alreadyMemberMessage": "您已经是 \"{name}\" 的会员。",
- "community_addPublicChannel": "添加社区公共频道",
- "community_addPublicChannelHint": "自动添加该社区的公共频道",
- "community_noCommunities": "尚未加入任何社区",
- "community_scanOrCreate": "扫描二维码或创建社区开始",
- "community_manageCommunities": "管理社群",
- "community_delete": "退出社区",
- "community_deleteConfirm": "退出 \"{name}\"?",
- "community_deleteChannelsWarning": "这也将删除 {count} 个频道及其消息。",
- "@community_deleteChannelsWarning": {
+ "listFilter_tooltip": "筛选与排序",
+ "listFilter_sortBy": "排序方式",
+ "listFilter_latestMessages": "最新消息",
+ "listFilter_heardRecently": "最近听到",
+ "listFilter_az": "A-Z",
+ "listFilter_filters": "筛选",
+ "listFilter_all": "全部",
+ "listFilter_users": "用户",
+ "listFilter_repeaters": "转发节点",
+ "listFilter_roomServers": "房间服务器",
+ "listFilter_unreadOnly": "仅显示未读",
+ "listFilter_newGroup": "新建群聊",
+ "pathTrace_you": "我自己",
+ "pathTrace_failed": "路径追踪失败。",
+ "pathTrace_notAvailable": "无法获取路径信息。",
+ "pathTrace_refreshTooltip": "刷新路径追踪",
+ "contacts_pathTrace": "路径追踪",
+ "contacts_ping": "Ping",
+ "contacts_repeaterPathTrace": "Trace 转发节点",
+ "contacts_repeaterPing": "Ping 转发节点",
+ "contacts_roomPathTrace": "Trace 房间服务器",
+ "contacts_roomPing": "Ping 房间服务器",
+ "contacts_chatTraceRoute": "路由追踪",
+ "contacts_pathTraceTo": "追踪至 {name} 的路径",
+ "@contacts_pathTraceTo": {
"placeholders": {
- "count": {"type": "int"}
+ "name": {
+ "type": "String"
+ }
}
},
- "community_deleted": "已退出社区 \"{name}\"",
- "community_addHashtagChannel": "添加社区标签",
- "community_addHashtagChannelDesc": "添加一个话题频道给此社区",
- "community_selectCommunity": "选择社区",
- "community_regularHashtag": "常规话题标签",
- "community_regularHashtagDesc": "公共话题(任何人都可以加入)",
- "community_communityHashtag": "社区标签",
- "community_communityHashtagDesc": "仅限社区成员使用",
- "community_forCommunity": "对于 {name}"
+ "contacts_clipboardEmpty": "剪贴板为空",
+ "contacts_invalidAdvertFormat": "无效的联系人信息格式",
+ "contacts_contactImported": "联系人已导入",
+ "contacts_contactImportFailed": "导入联系人失败。",
+ "contacts_zeroHopAdvert": "发送零跳广播",
+ "contacts_floodAdvert": "发送泛洪广播",
+ "contacts_copyAdvertToClipboard": "复制广播到剪贴板",
+ "contacts_addContactFromClipboard": "从剪贴板添加联系人",
+ "contacts_ShareContact": "复制联系人信息到剪贴板",
+ "contacts_ShareContactZeroHop": "通过广播分享联系人",
+ "contacts_zeroHopContactAdvertSent": "零跳广播已发送",
+ "contacts_zeroHopContactAdvertFailed": "发送联系人广播失败。",
+ "contacts_contactAdvertCopied": "广播已复制到剪贴板。",
+ "contacts_contactAdvertCopyFailed": "复制广播到剪贴板失败。",
+ "notification_activityTitle": "MeshCore 活动",
+ "notification_messagesCount": "{count} 条消息",
+ "notification_channelMessagesCount": "{count} 条频道消息",
+ "notification_newNodesCount": "{count} 个新节点",
+ "notification_newTypeDiscovered": "发现新 {contactType}",
+ "notification_receivedNewMessage": "收到新消息",
+ "settings_gpxExportRepeaters": "导出转发节点/房间服务器到 GPX",
+ "settings_gpxExportRepeatersSubtitle": "导出带位置的转发节点/房间服务器到 GPX 文件",
+ "settings_gpxExportContactsSubtitle": "导出带位置的伙伴到 GPX 文件",
+ "settings_gpxExportNotAvailable": "您的设备/操作系统不支持",
+ "settings_gpxExportSuccess": "GPX 文件导出成功",
+ "settings_gpxExportError": "导出时出错",
+ "settings_gpxExportRepeatersRoom": "转发节点与房间服务器位置",
+ "settings_gpxExportChat": "伙伴位置",
+ "settings_gpxExportAll": "导出所有联系人到 GPX",
+ "settings_gpxExportContacts": "导出伙伴到 GPX",
+ "settings_gpxExportAllSubtitle": "导出所有带位置的联系人到 GPX 文件",
+ "settings_gpxExportAllContacts": "所有联系人位置",
+ "settings_gpxExportNoContacts": "没有可导出的联系人",
+ "settings_gpxExportShareText": "来自 MeshCore Open 的地图数据导出",
+ "settings_gpxExportShareSubject": "MeshCore Open GPX 地图数据导出",
+ "pathTrace_someHopsNoLocation": "某些跳缺少位置信息!",
+ "map_tapToAdd": "点击节点以添加到路径",
+ "pathTrace_clearTooltip": "清除路径",
+ "map_pathTraceCancelled": "路径追踪已取消",
+ "map_removeLast": "移除最后一个",
+ "map_runTrace": "运行路径追踪",
+ "scanner_bluetoothOffMessage": "请开启蓝牙以搜索设备",
+ "scanner_chromeRequired": "需要 Chrome 浏览器",
+ "scanner_chromeRequiredMessage": "此 Web 应用程序需要 Google Chrome 或基于 Chromium 的浏览器以支持蓝牙。",
+ "scanner_bluetoothOff": "蓝牙已关闭",
+ "scanner_enableBluetooth": "启用蓝牙",
+ "snrIndicator_lastSeen": "最近访问",
+ "snrIndicator_nearByRepeaters": "附近的重复器",
+ "chat_ShowAllPaths": "显示所有路径",
+ "settings_clientRepeat": "离网重复",
+ "settings_clientRepeatSubtitle": "允许此设备重复发送网状数据包给其他设备",
+ "settings_clientRepeatFreqWarning": "离网重复通信需要使用 433、869 或 918 兆赫兹的频率。",
+ "settings_aboutOpenMeteoAttribution": "LOS 高程数据:Open-Meteo (CC BY 4.0)",
+ "appSettings_unitsTitle": "单位",
+ "appSettings_unitsMetric": "公制(米/公里)",
+ "appSettings_unitsImperial": "英制 (ft / mi)",
+ "map_lineOfSight": "视线",
+ "map_losScreenTitle": "视线",
+ "losSelectStartEnd": "选择 LOS 的起始节点和结束节点。",
+ "losRunFailed": "视线检查失败:{error}",
+ "@losRunFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "losClearAllPoints": "清除所有点",
+ "losRunToViewElevationProfile": "运行 LOS 查看高程剖面",
+ "losMenuTitle": "服务水平菜单",
+ "losMenuSubtitle": "点击节点或长按地图以获取自定义点",
+ "losShowDisplayNodes": "显示显示节点",
+ "losCustomPoints": "自定义积分",
+ "losCustomPointLabel": "自定义 {index}",
+ "@losCustomPointLabel": {
+ "placeholders": {
+ "index": {
+ "type": "int"
+ }
+ }
+ },
+ "losPointA": "A点",
+ "losPointB": "B点",
+ "losAntennaA": "天线 A: {value} {unit}",
+ "@losAntennaA": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losAntennaB": "天线 B:{value} {unit}",
+ "@losAntennaB": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ },
+ "unit": {
+ "type": "String"
+ }
+ }
+ },
+ "losRun": "运行视距",
+ "losNoElevationData": "无海拔数据",
+ "losProfileClear": "{distance} {distanceUnit},清除 LOS,最小间隙 {clearance} {heightUnit}",
+ "@losProfileClear": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "clearance": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losProfileBlocked": "{distance} {distanceUnit},被 {obstruction} {heightUnit} 阻止",
+ "@losProfileBlocked": {
+ "placeholders": {
+ "distance": {
+ "type": "String"
+ },
+ "distanceUnit": {
+ "type": "String"
+ },
+ "obstruction": {
+ "type": "String"
+ },
+ "heightUnit": {
+ "type": "String"
+ }
+ }
+ },
+ "losStatusChecking": "洛斯:正在检查...",
+ "losStatusNoData": "LOS:无数据",
+ "losStatusSummary": "LOS:{clear}/{total} 清除,{blocked} 阻塞,{unknown} 未知",
+ "@losStatusSummary": {
+ "placeholders": {
+ "clear": {
+ "type": "int"
+ },
+ "total": {
+ "type": "int"
+ },
+ "blocked": {
+ "type": "int"
+ },
+ "unknown": {
+ "type": "int"
+ }
+ }
+ },
+ "losErrorElevationUnavailable": "一个或多个样本的海拔数据不可用。",
+ "losErrorInvalidInput": "用于 LOS 计算的点/高程数据无效。",
+ "losRenameCustomPoint": "重命名自定义点",
+ "losPointName": "点名称",
+ "losShowPanelTooltip": "显示 LOS 面板",
+ "losHidePanelTooltip": "隐藏 LOS 面板",
+ "losElevationAttribution": "高程数据:Open-Meteo (CC BY 4.0)",
+ "losLegendRadioHorizon": "无线电地平线",
+ "losLegendLosBeam": "视距波束",
+ "losLegendTerrain": "地形",
+ "losFrequencyLabel": "频率",
+ "losFrequencyInfoTooltip": "查看计算详情",
+ "losFrequencyDialogTitle": "无线电地平线计算",
+ "losFrequencyDialogDescription": "从 {baselineFreq} MHz 处的 k={baselineK} 开始,计算调整当前 {frequencyMHz} MHz 频段的 k 因子,该因子定义了弯曲的无线电范围上限。",
+ "@losFrequencyDialogDescription": {
+ "description": "Explain how the calculation uses the baseline frequency and derived k-factor.",
+ "placeholders": {
+ "baselineK": {
+ "type": "double"
+ },
+ "baselineFreq": {
+ "type": "double"
+ },
+ "frequencyMHz": {
+ "type": "double"
+ },
+ "kFactor": {
+ "type": "double"
+ }
+ }
+ },
+ "listFilter_favorites": "收藏",
+ "listFilter_addToFavorites": "添加到收藏",
+ "listFilter_removeFromFavorites": "从收藏中移除",
+ "@contacts_searchFavorites": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchUsers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRepeaters": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "@contacts_searchRoomServers": {
+ "placeholders": {
+ "number": {
+ "type": "int"
+ },
+ "str": {
+ "type": "String"
+ }
+ }
+ },
+ "contacts_searchUsers": "搜索 {number}{str} 位用户...",
+ "contacts_unread": "未读",
+ "contacts_searchRepeaters": "搜索 {number}{str} 重复器...",
+ "contacts_searchContactsNoNumber": "搜索联系人...",
+ "contacts_searchRoomServers": "搜索 {number}{str} 房间服务器...",
+ "contacts_searchFavorites": "搜索 {number}{str} 收藏...",
+ "settings_contactSettings": "联系人设置",
+ "contactsSettings_title": "联系人设置",
+ "contactsSettings_autoAddUsersTitle": "自动添加用户",
+ "contactsSettings_otherTitle": "其他联系人相关设置",
+ "contactsSettings_autoAddUsersSubtitle": "允许伴侣自动添加发现的用户",
+ "contactsSettings_autoAddRepeatersSubtitle": "允许伴侣自动添加发现的重复器",
+ "contactsSettings_autoAddSensorsTitle": "自动添加传感器",
+ "contactsSettings_autoAddRoomServersSubtitle": "允许伴侣自动添加发现的房间服务器",
+ "contactsSettings_autoAddRepeatersTitle": "自动添加重复器",
+ "contactsSettings_autoAddTitle": "自动发现",
+ "settings_contactSettingsSubtitle": "添加联系人的设置",
+ "contactsSettings_overwriteOldestTitle": "覆盖最旧的",
+ "contactsSettings_autoAddSensorsSubtitle": "允许伴侣自动添加发现的传感器",
+ "discoveredContacts_searchHint": "搜索已发现的联系人",
+ "contactsSettings_autoAddRoomServersTitle": "自动添加房间服务器",
+ "discoveredContacts_contactAdded": "联系人已添加",
+ "discoveredContacts_deleteContact": "删除联系人",
+ "discoveredContacts_addContact": "添加联系人",
+ "discoveredContacts_noMatching": "没有匹配的联系人",
+ "discoveredContacts_Title": "已发现的联系人",
+ "discoveredContacts_copyContact": "复制联系人到剪贴板",
+ "contactsSettings_overwriteOldestSubtitle": "当联系人列表已满时,将替换最老的非收藏联系人。",
+ "common_deleteAll": "删除全部",
+ "discoveredContacts_deleteContactAllContent": "您确定要删除所有发现的联系人吗?",
+ "discoveredContacts_deleteContactAll": "删除所有发现的联系人",
+ "map_showGuessedLocations": "显示猜测的节点位置",
+ "map_guessedLocation": "猜测的位置",
+ "connectionChoiceUsbLabel": "USB",
+ "usbScreenTitle": "通过USB连接",
+ "usbScreenSubtitle": "选择已检测到的串行设备,并直接连接到您的 MeshCore 节点。",
+ "connectionChoiceBluetoothLabel": "蓝牙",
+ "usbScreenStatus": "选择一个 USB 设备",
+ "usbScreenNote": "USB 串行接口在支持的 Android 设备和桌面平台上处于活动状态。",
+ "usbScreenEmptyState": "未找到任何 USB 设备。请插入一个,然后刷新。",
+ "usbErrorPermissionDenied": "拒绝了USB权限。",
+ "usbErrorDeviceMissing": "所选的USB设备已不再可用。",
+ "usbErrorInvalidPort": "选择一个有效的USB设备。",
+ "usbErrorBusy": "还有一个 USB 连接请求正在进行中。",
+ "usbErrorNotConnected": "没有连接任何USB设备。",
+ "usbErrorOpenFailed": "未能打开所选的USB设备。",
+ "usbErrorConnectFailed": "未能连接到所选的USB设备。",
+ "usbErrorUnsupported": "此平台不支持USB串行通信。",
+ "usbErrorAlreadyActive": "USB 连接已建立。",
+ "usbErrorNoDeviceSelected": "未选择任何 USB 设备。",
+ "usbErrorPortClosed": "USB 连接未建立。",
+ "usbFallbackDeviceName": "Web 串流设备",
+ "@usbConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "usbStatus_searching": "正在搜索 USB 设备...",
+ "usbStatus_connecting": "连接USB设备...",
+ "usbStatus_notConnected": "选择一个 USB 设备",
+ "usbConnectionFailed": "USB 连接失败:{error}",
+ "usbErrorConnectTimedOut": "连接超时。请确保设备已安装 USB 伴侣固件。",
+ "@tcpStatus_connectingTo": {
+ "placeholders": {
+ "endpoint": {
+ "type": "String"
+ }
+ }
+ },
+ "@tcpConnectionFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "tcpHostLabel": "IP地址",
+ "tcpHostHint": "192.168.40.10",
+ "tcpScreenTitle": "通过 TCP 连接",
+ "connectionChoiceTcpLabel": "TCP",
+ "tcpPortLabel": "端口",
+ "tcpPortHint": "5000",
+ "tcpStatus_notConnected": "输入目标地址,然后连接",
+ "tcpStatus_connectingTo": "连接到 {endpoint}...",
+ "tcpErrorHostRequired": "需要提供IP地址。",
+ "tcpErrorPortInvalid": "端口号必须在 1 到 65535 之间。",
+ "tcpErrorUnsupported": "此平台不支持 TCP 传输。",
+ "tcpErrorTimedOut": "TCP 连接超时。",
+ "tcpConnectionFailed": "TCP 连接失败:{error}",
+ "map_showDiscoveryContacts": "显示发现联系人",
+ "map_setAsMyLocation": "设置为我的位置",
+ "@path_routeWeight": {
+ "placeholders": {
+ "weight": {
+ "type": "String"
+ },
+ "max": {
+ "type": "String"
+ }
+ }
+ },
+ "settings_privacySubtitle": "控制要共享的信息。",
+ "settings_privacySettingsDescription": "选择您的设备与他人共享的信息。",
+ "settings_telemetryBaseMode": "遥测基础模式",
+ "settings_telemetryLocationMode": "遥测位置模式",
+ "settings_advertLocation": "广告位置",
+ "settings_advertLocationSubtitle": "在广告中包含位置",
+ "settings_allowByContact": "按联系人标志允许",
+ "settings_denyAll": "拒绝所有",
+ "settings_privacy": "隐私设置",
+ "settings_allowAll": "允许全部",
+ "contact_info": "联系信息",
+ "contact_teleBase": "遥测基站",
+ "contact_teleBaseSubtitle": "允许共享电池电量和基本遥测数据",
+ "settings_telemetryEnvironmentMode": "遥测环境模式",
+ "contact_teleLoc": "遥测位置",
+ "contact_teleEnv": "遥测环境",
+ "contact_teleEnvSubtitle": "允许共享环境传感器数据",
+ "contact_clearChat": "清除聊天记录",
+ "contact_lastSeen": "最近出现",
+ "contact_settings": "联系人设置",
+ "contact_teleLocSubtitle": "允许共享位置数据",
+ "contact_telemetry": "遥测数据",
+ "@settings_multiAck": {
+ "placeholders": {
+ "value": {
+ "type": "String"
+ }
+ }
+ },
+ "appSettings_maxRouteWeight": "最大路径重量",
+ "appSettings_initialRouteWeightSubtitle": "新发现路径的初始重量",
+ "appSettings_initialRouteWeight": "初始路线权重",
+ "appSettings_maxRouteWeightSubtitle": "一条路径可以累积的最大重量,取决于成功交付的数量。",
+ "appSettings_routeWeightSuccessIncrement": "成功权重增加",
+ "appSettings_routeWeightSuccessIncrementSubtitle": "在成功交付后,将重量添加到路径中",
+ "appSettings_routeWeightFailureDecrement": "失败权重降低",
+ "appSettings_routeWeightFailureDecrementSubtitle": "从一条路径上移除的货物,由于无法成功交付而移除。",
+ "appSettings_maxMessageRetries": "最大消息重试次数",
+ "appSettings_maxMessageRetriesSubtitle": "在将消息标记为失败之前,允许尝试的次数",
+ "path_routeWeight": "{weight}/{max}",
+ "settings_multiAck": "多重ACK:{value}",
+ "settings_telemetryModeUpdated": "遥测模式已更新",
+ "map_showOverlaps": "重复键重叠",
+ "map_runTraceWithReturnPath": "沿着相同的路径返回",
+ "@radioStats_noiseFloor": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastRssi": {
+ "placeholders": {
+ "rssiDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_lastSnr": {
+ "placeholders": {
+ "snr": {
+ "type": "String"
+ }
+ }
+ },
+ "@radioStats_txAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_rxAir": {
+ "placeholders": {
+ "seconds": {
+ "type": "int"
+ }
+ }
+ },
+ "@radioStats_stripNoise": {
+ "placeholders": {
+ "noiseDbm": {
+ "type": "int"
+ }
+ }
+ },
+ "chat_sendCooldown": "请稍等片刻后再尝试发送。",
+ "appSettings_jumpToOldestUnreadSubtitle": "在打开包含未读消息的聊天时,请滚动到第一个未读消息,而不是最新的消息。",
+ "appSettings_jumpToOldestUnread": "跳转到最旧、未读的文章",
+ "appSettings_languageHu": "匈牙利",
+ "appSettings_languageJa": "日语",
+ "appSettings_languageKo": "韩语",
+ "radioStats_tooltip": "无线电和网状结构统计数据",
+ "radioStats_screenTitle": "广播统计数据",
+ "radioStats_notConnected": "连接到设备以查看收音机统计信息。",
+ "radioStats_firmwareTooOld": "使用无线电统计功能需要配合使用 v8 或更高版本的固件。",
+ "radioStats_waiting": "正在等待数据…",
+ "radioStats_noiseFloor": "噪声水平:{noiseDbm} dBm",
+ "radioStats_lastRssi": "上次 RSSI 值:{rssiDbm} dBm",
+ "radioStats_lastSnr": "上次 SNR:{snr} dB",
+ "radioStats_txAir": "TX 频道播出时间(总时长):{seconds} 秒",
+ "radioStats_rxAir": "RX 使用时长(总时长):{seconds} 秒",
+ "radioStats_chartCaption": "近期的噪声水平(dBm)。",
+ "radioStats_stripNoise": "噪声水平:{noiseDbm} dBm",
+ "radioStats_stripWaiting": "正在获取收音机数据…",
+ "radioStats_settingsTile": "广播统计数据",
+ "radioStats_settingsSubtitle": "噪声水平、RSSI、信噪比和空中时间",
+ "@translation_downloadFailed": {
+ "placeholders": {
+ "error": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_title": "翻译",
+ "translation_enableSubtitle": "翻译收到的消息,并允许在发送前进行翻译。",
+ "translation_composerTitle": "在发送之前进行翻译",
+ "translation_enableTitle": "启用翻译功能",
+ "translation_composerSubtitle": "控制作曲家翻译图标的默认状态。",
+ "translation_targetLanguage": "目标语言",
+ "translation_useAppLanguage": "使用应用程序语言",
+ "translation_downloadedModelLabel": "下载的模型",
+ "translation_presetModelLabel": "预设的 Hugging Face 模型",
+ "translation_downloadModel": "下载模型",
+ "translation_manualUrlLabel": "手动模型网址",
+ "translation_downloading": "正在下载...",
+ "translation_working": "工作中...",
+ "translation_stop": "停止",
+ "translation_mergingChunks": "将下载的片段合并成最终文件...",
+ "translation_downloadedModels": "下载的模型",
+ "translation_deleteModel": "删除模型",
+ "translation_modelDownloaded": "翻译模型已下载。",
+ "translation_downloadStopped": "下载已停止。",
+ "translation_downloadFailed": "下载失败:{error}",
+ "translation_enterUrlFirst": "首先,请输入模型的 URL。",
+ "@scanner_linuxPairingPinPrompt": {
+ "placeholders": {
+ "deviceName": {
+ "type": "String"
+ }
+ }
+ },
+ "scanner_linuxPairingPinTitle": "蓝牙配对 PIN",
+ "scanner_linuxPairingPinPrompt": "输入 {deviceName} 的 PIN 码(如果为空,则留空)。",
+ "scanner_linuxPairingHidePin": "隐藏 PIN",
+ "scanner_linuxPairingShowPin": "显示PIN码",
+ "@translation_translateTo": {
+ "placeholders": {
+ "language": {
+ "type": "String"
+ }
+ }
+ },
+ "translation_composerDisabledHint": "使用原始的打字方式发送消息。",
+ "translation_messageTranslation": "消息翻译",
+ "translation_composerEnabledHint": "消息将在发送前进行翻译。",
+ "translation_translateBeforeSending": "在发送前进行翻译",
+ "translation_translateTo": "翻译成 {language}",
+ "translation_translationOptions": "翻译选项",
+ "translation_systemLanguage": "系统语言",
+ "repeater_cliQuickDiscovery": "发现邻居",
+ "repeater_cliQuickClockSync": "同步时钟",
+ "@repeater_clockSyncAfterLogin": {
+ "description": "Repeater setting: auto sync device clock after successful login"
+ },
+ "@repeater_clockSyncAfterLoginSubtitle": {
+ "description": "Repeater setting subtitle: describes the clock sync after login behavior"
+ },
+ "repeater_clockSyncAfterLogin": "登录后,自动同步时钟",
+ "repeater_clockSyncAfterLoginSubtitle": "在成功登录后,自动发送“时钟同步”指令。",
+ "repeater_guestTools": "访客工具",
+ "repeater_guest": "重复器信息",
+ "chat_sendMessage": "发送消息",
+ "room_guest": "服务器信息"
}
diff --git a/lib/main.dart b/lib/main.dart
index 6dac19b..3e57eb1 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,8 +1,12 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter/foundation.dart';
import 'l10n/app_localizations.dart';
import 'package:provider/provider.dart';
+import 'screens/chrome_required_screen.dart';
+import 'utils/platform_info.dart';
+
import 'connector/meshcore_connector.dart';
import 'screens/scanner_screen.dart';
import 'services/storage_service.dart';
@@ -14,6 +18,10 @@ import 'services/ble_debug_log_service.dart';
import 'services/app_debug_log_service.dart';
import 'services/background_service.dart';
import 'services/map_tile_cache_service.dart';
+import 'services/chat_text_scale_service.dart';
+import 'services/translation_service.dart';
+import 'services/ui_view_state_service.dart';
+import 'services/timeout_prediction_service.dart';
import 'storage/prefs_manager.dart';
import 'utils/app_logger.dart';
@@ -33,6 +41,10 @@ void main() async {
final appDebugLogService = AppDebugLogService();
final backgroundService = BackgroundService();
final mapTileCacheService = MapTileCacheService();
+ final chatTextScaleService = ChatTextScaleService();
+ final translationService = TranslationService(appSettingsService);
+ final uiViewStateService = UiViewStateService();
+ final timeoutPredictionService = TimeoutPredictionService(storage);
// Load settings
await appSettingsService.loadSettings();
@@ -47,34 +59,70 @@ void main() async {
final notificationService = NotificationService();
await notificationService.initialize();
await backgroundService.initialize();
+ _registerThirdPartyLicenses();
+
+ await chatTextScaleService.initialize();
+ await translationService.refreshDownloadedModels();
+ await uiViewStateService.initialize();
+ await timeoutPredictionService.initialize();
// Wire up connector with services
connector.initialize(
retryService: retryService,
pathHistoryService: pathHistoryService,
appSettingsService: appSettingsService,
+ translationService: translationService,
bleDebugLogService: bleDebugLogService,
appDebugLogService: appDebugLogService,
backgroundService: backgroundService,
+ timeoutPredictionService: timeoutPredictionService,
);
await connector.loadContactCache();
await connector.loadChannelSettings();
+ await connector.loadCachedChannels();
// Load persisted channel messages
await connector.loadAllChannelMessages();
await connector.loadUnreadState();
- runApp(MeshCoreApp(
- connector: connector,
- retryService: retryService,
- pathHistoryService: pathHistoryService,
- storage: storage,
- appSettingsService: appSettingsService,
- bleDebugLogService: bleDebugLogService,
- appDebugLogService: appDebugLogService,
- mapTileCacheService: mapTileCacheService,
- ));
+ runApp(
+ MeshCoreApp(
+ connector: connector,
+ retryService: retryService,
+ pathHistoryService: pathHistoryService,
+ storage: storage,
+ appSettingsService: appSettingsService,
+ bleDebugLogService: bleDebugLogService,
+ appDebugLogService: appDebugLogService,
+ mapTileCacheService: mapTileCacheService,
+ chatTextScaleService: chatTextScaleService,
+ translationService: translationService,
+ uiViewStateService: uiViewStateService,
+ timeoutPredictionService: timeoutPredictionService,
+ ),
+ );
+}
+
+void _registerThirdPartyLicenses() {
+ LicenseRegistry.addLicense(() async* {
+ yield const LicenseEntryWithLineBreaks(
+