From fcbf41a9dddd8d2ba1a94e5d5ea1b6574a821cfe Mon Sep 17 00:00:00 2001 From: cogwheel0 <172976095+cogwheel0@users.noreply.github.com> Date: Sun, 7 Sep 2025 12:22:02 +0530 Subject: [PATCH] chore: prepare for weblate --- .github/workflows/l10n.yml | 30 ++ .gitignore | 4 +- docs/localization.md | 96 +++--- l10n.yaml | 10 + lib/l10n/app_en.arb | 276 ++++++++++++++- lib/l10n/app_localizations.dart | 521 +++++++++++++++-------------- lib/l10n/app_localizations_de.dart | 126 ++++--- lib/l10n/app_localizations_en.dart | 105 ++++-- lib/l10n/app_localizations_fr.dart | 153 ++++++--- lib/l10n/app_localizations_it.dart | 120 ++++--- pubspec.yaml | 17 +- tool/validate_arb_locales.dart | 139 ++++++++ tool/verify_arb_descriptions.dart | 63 ++++ weblate.yaml | 19 ++ 14 files changed, 1177 insertions(+), 502 deletions(-) create mode 100644 .github/workflows/l10n.yml create mode 100644 l10n.yaml create mode 100644 tool/validate_arb_locales.dart create mode 100644 tool/verify_arb_descriptions.dart create mode 100644 weblate.yaml diff --git a/.github/workflows/l10n.yml b/.github/workflows/l10n.yml new file mode 100644 index 0000000..620ae63 --- /dev/null +++ b/.github/workflows/l10n.yml @@ -0,0 +1,30 @@ +name: L10n + +on: + pull_request: + paths: + - 'lib/l10n/*.arb' + push: + paths: + - 'lib/l10n/*.arb' + +jobs: + l10n: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: subosito/flutter-action@v2 + with: + channel: 'stable' + - run: flutter --version + - run: flutter pub get + - run: flutter pub run build_runner build --delete-conflicting-outputs + - name: Generate localizations + run: flutter gen-l10n --arb-dir=lib/l10n --output-dir=lib/l10n --template-arb-file=app_en.arb --output-localization-file=app_localizations.dart + - name: Analyze + run: flutter analyze + - name: Verify ARB descriptions + run: dart run tool/verify_arb_descriptions.dart + - name: Validate ARB locales and placeholders + run: dart run tool/validate_arb_locales.dart + # Generated files are ignored; no diff check needed. diff --git a/.gitignore b/.gitignore index c6e013c..3710e71 100644 --- a/.gitignore +++ b/.gitignore @@ -93,6 +93,8 @@ coverage/ # Generated files *.g.dart *.freezed.dart +lib/l10n/app_localizations.dart +lib/l10n/app_localizations_*.dart # FVM (Flutter Version Management) .fvm/ @@ -138,4 +140,4 @@ GoogleService-Info.plist !**/ios/**/default.mode2v3 !**/ios/**/default.pbxuser !**/ios/**/default.perspectivev3 -!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages \ No newline at end of file +!/packages/flutter_tools/test/data/dart_dependencies_test/**/.packages diff --git a/docs/localization.md b/docs/localization.md index 2107a74..b27cd87 100644 --- a/docs/localization.md +++ b/docs/localization.md @@ -1,65 +1,63 @@ -## Localization (i18n) +# Localization Guidelines -- Supported locales: `en`, `de`, `fr`, `it`. -- Uses Flutter's `gen_l10n` with ARB files and the `intl` package for date/number formatting. +This app uses Flutter's gen-l10n with ARB files in `lib/l10n`. -### Install & Generate +- Source language: `en` (`lib/l10n/app_en.arb`) +- Translations: `lib/l10n/app_.arb` (e.g., `app_de.arb`) +- Generated Dart: `lib/l10n/app_localizations*.dart` (generated by Flutter) -- Install packages: - - `flutter_localizations` (Flutter SDK) - - `intl: ^0.20.2` -- Files are under `lib/l10n/*.arb`. The template is `app_en.arb`. -- Generate localizations: - - `flutter gen-l10n` - - or run a full build: `flutter pub get && flutter gen-l10n` +## Adding New Strings -### Usage Examples +1. Edit `lib/l10n/app_en.arb` and add a key with a clear description meta entry. + - Example: + ```json + "greeting": "Hello, {name}!", + "@greeting": { + "description": "Friendly greeting with user name.", + "placeholders": { "name": { "type": "String", "example": "Alex" } } + } + ``` +2. Run `flutter gen-l10n` or just `flutter run` to regenerate localizations. +3. Use it in code: `AppLocalizations.of(context)!.greeting(name)`. -- Basic text: - - `Text(AppLocalizations.of(context)!.appTitle)` -- With placeholder: - - `Text(AppLocalizations.of(context)!.dynamicContentWithPlaceholder('Alex'))` -- Pluralization: - - `Text(AppLocalizations.of(context)!.itemsCount(3))` -- Date/time formatting: - - `final dateText = DateFormat.yMMMMEEEEd(Localizations.localeOf(context).toString()).format(DateTime.now());` - - `Text(dateText)` -- Number formatting: - - `final price = NumberFormat.currency(locale: Localizations.localeOf(context).toString(), symbol: '€').format(1234.56);` - - `Text(price)` +## Placeholders and ICU -### Add a New Language +- Always declare placeholders under the `@key.placeholders` block with `type` and optional `example`. +- Use ICU for plurals/select: + ```json + "itemsCount": "{count, plural, =0{No items} one{1 item} other{{count} items}}", + "@itemsCount": { "placeholders": { "count": { "type": "int" } } } + ``` -- Create a new ARB file in `lib/l10n/`, e.g. `app_es.arb`. -- Copy keys from `app_en.arb` and provide translated values. -- Ensure placeholders and plural rules match the template. -- Add the locale to `supportedLocales` in `MaterialApp` (see `lib/main.dart`). -- Regenerate: `flutter gen-l10n`. +## Punctuation and Style -### Best Practices +- Prefer typographic ellipsis `…` over `...`. +- Keep capitalization consistent across locales. +- Avoid embedding brand names in placeholders; keep them literal in strings. -- Key naming: use lowerCamelCase (e.g., `loginButton`, `errorMessage`). -- Include `@` metadata with `description` for context and `placeholders` with examples. -- Prefer ICU plural/select syntax for quantities and genders. -- Avoid concatenating strings at runtime; use placeholders in ARB. +## Weblate -### In‑App Locale Switching +This repository includes `weblate.yaml` to help auto-configure Weblate. -- Open the Profile page → Settings tile → choose `System`, `English`, `Deutsch`, `Français`, or `Italiano`. -- Selection persists across app launches. +- File mask: `lib/l10n/app_*.arb` +- Template: `lib/l10n/app_en.arb` +- New language file path: `lib/l10n/app_{language}.arb` +- Monolingual: `true` +- Base language: `en` -### Troubleshooting +Recommended Weblate add-ons: +- “JSON/ARB reformat” to keep formatting stable. +- “String freeze” (when preparing releases). +- Checks for placeholders to ensure `{name}`, `{count}`, etc. are preserved. -- Build fails with ARB placeholder errors: - - Ensure every placeholder has an example string and correct type. -- Missing translation at runtime: - - Flutter falls back to English; search for hard‑coded strings and replace with `AppLocalizations`. -- iOS strings not changing: - - Restart the app after changing system language or use the in‑app language selector. +## Generated Files Policy -### References - -- Flutter localization: https://docs.flutter.dev/ui/accessibility-and-localization/internationalization -- Intl package: https://pub.dev/packages/intl +- Generated localization Dart files are NOT committed. They are ignored via `.gitignore` and generated during CI/build. +- Run `flutter gen-l10n` locally when you need IDE completion, or rely on `flutter run` which generates them automatically. +## Common Tasks +- Regenerate after ARB edits: + - `flutter gen-l10n --arb-dir=lib/l10n --output-dir=lib/l10n --template-arb-file=app_en.arb --output-localization-file=app_localizations.dart` +- Validate: + - `flutter analyze` diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 0000000..3de0410 --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,10 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +output-dir: lib/l10n +preferred-supported-locales: + - en + - de + - fr + - it diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 200afaf..d63128b 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,26 +1,47 @@ { "@@locale": "en", "appTitle": "Conduit", + "@appTitle": {"description": "Application name displayed in the app and OS UI."}, "initializationFailed": "Initialization Failed", + "@initializationFailed": {"description": "Shown if the app fails to initialize critical services."}, "retry": "Retry", + "@retry": {"description": "Button label to try an action again."}, "back": "Back", + "@back": {"description": "Back navigation label/tooltip."}, "you": "You", + "@you": {"description": "Profile tab title."}, "loadingProfile": "Loading profile...", + "@loadingProfile": {"description": "Progress message while fetching profile data."}, "unableToLoadProfile": "Unable to load profile", + "@unableToLoadProfile": {"description": "Error title shown when profile request fails."}, "pleaseCheckConnection": "Please check your connection and try again", + "@pleaseCheckConnection": {"description": "Generic connectivity hint after an error."}, "account": "Account", + "@account": {"description": "Section header for account-related options."}, "signOut": "Sign Out", + "@signOut": {"description": "Button/title for signing out of the app."}, "endYourSession": "End your session", + "@endYourSession": {"description": "Subtitle explaining the sign-out action."}, "defaultModel": "Default Model", + "@defaultModel": {"description": "Label for choosing a default AI model."}, "autoSelect": "Auto-select", + "@autoSelect": {"description": "Option to let the app pick a suitable model automatically."}, "loadingModels": "Loading models...", + "@loadingModels": {"description": "Progress message while fetching model list."}, "failedToLoadModels": "Failed to load models", + "@failedToLoadModels": {"description": "Error message shown when model list cannot be retrieved."}, "availableModels": "Available Models", + "@availableModels": {"description": "Header above a list of models to select from."}, "noResults": "No results", + "@noResults": {"description": "Shown when a search returns no matches."}, "searchModels": "Search models...", + "@searchModels": {"description": "Hint text for model search input."}, "errorMessage": "Something went wrong. Please try again.", + "@errorMessage": {"description": "Generic error message for unexpected failures."}, "loginButton": "Login", + "@loginButton": {"description": "Button text for the login action."}, "menuItem": "Settings", + "@menuItem": {"description": "Generic settings menu item label."}, "dynamicContentWithPlaceholder": "Welcome, {name}!", "@dynamicContentWithPlaceholder": { "description": "Greeting message with a dynamic user name.", @@ -42,242 +63,459 @@ } }, "closeButtonSemantic": "Close", + "@closeButtonSemantic": {"description": "Accessible label for a generic Close button."}, "loadingContent": "Loading content", + "@loadingContent": {"description": "Shown while loading page content."}, "noItems": "No items", + "@noItems": {"description": "Placeholder text when a list is empty."}, "noItemsToDisplay": "No items to display", + "@noItemsToDisplay": {"description": "Alternative empty-state description."}, "loadMore": "Load More", + "@loadMore": {"description": "Button label to load additional items in a paged list."}, "workspace": "Workspace", + "@workspace": {"description": "Section/tab label for documents and files."}, "recentFiles": "Recent Files", + "@recentFiles": {"description": "Header for recently accessed files."}, "knowledgeBase": "Knowledge Base", + "@knowledgeBase": {"description": "Section for knowledge base content."}, "noFilesYet": "No files yet", + "@noFilesYet": {"description": "Empty state when no files are present."}, "uploadDocsPrompt": "Upload documents to reference in your conversations with Conduit", + "@uploadDocsPrompt": {"description": "Prompt encouraging users to upload documents."}, "uploadFirstFile": "Upload your first file", + "@uploadFirstFile": {"description": "CTA to add the first file."}, "knowledgeBaseEmpty": "Knowledge base is empty", + "@knowledgeBaseEmpty": {"description": "Empty state title for the knowledge base section."}, "createCollectionsPrompt": "Create collections of related documents for easy reference", + "@createCollectionsPrompt": {"description": "Prompt describing the benefit of creating collections."}, "chooseSourcePhoto": "Choose your source", + "@chooseSourcePhoto": {"description": "Sheet title to pick camera or photo library."}, "takePhoto": "Take a photo", + "@takePhoto": {"description": "Action to open camera and capture a new photo."}, "chooseFromGallery": "Choose from your photos", + "@chooseFromGallery": {"description": "Action to pick an existing photo from library."}, "document": "Document", + "@document": {"description": "Generic document label used in UI."}, "documentHint": "PDF, Word, or text file", + "@documentHint": {"description": "Helper hint listing supported document types."}, "uploadFileTitle": "Upload File", + "@uploadFileTitle": {"description": "Dialog/sheet title for file upload."}, "fileUploadComingSoon": "File upload for {type} is coming soon!", "@fileUploadComingSoon": { "placeholders": {"type": {"type": "String", "example": "gallery"}}, "description": "Temporary message for upcoming upload feature by type" }, "kbCreationComingSoon": "Knowledge base creation is coming soon!", + "@kbCreationComingSoon": {"description": "Temporary message indicating KB creation feature is not yet available."}, "backToServerSetup": "Back to server setup", + "@backToServerSetup": {"description": "Button/back label to return to server configuration flow."}, "connectedToServer": "Connected to Server", + "@connectedToServer": {"description": "Status label indicating a successful server connection."}, "signIn": "Sign In", + "@signIn": {"description": "Button/heading for sign-in flows."}, "enterCredentials": "Enter your credentials to access your AI conversations", + "@enterCredentials": {"description": "Instructional text on the sign-in screen."}, "credentials": "Credentials", + "@credentials": {"description": "Header for credential input section."}, "apiKey": "API Key", + "@apiKey": {"description": "Label for API key input field."}, "usernameOrEmail": "Username or Email", + "@usernameOrEmail": {"description": "Label for username/email input field."}, "password": "Password", + "@password": {"description": "Label for password input field."}, "signInWithApiKey": "Sign in with API Key", + "@signInWithApiKey": {"description": "Alternative sign-in method using an API key."}, "connectToServer": "Connect to Server", + "@connectToServer": {"description": "Call-to-action button for server connection."}, "enterServerAddress": "Enter your Open-WebUI server address to get started", + "@enterServerAddress": {"description": "Instruction telling user to provide server URL to begin."}, "serverUrl": "Server URL", + "@serverUrl": {"description": "Label for server URL field."}, "serverUrlHint": "https://your-server.com", + "@serverUrlHint": {"description": "Hint text showing example server URL format."}, "enterServerUrlSemantic": "Enter your server URL or IP address", + "@enterServerUrlSemantic": {"description": "Semantic/ARIA label instructing to enter server URL or IP."}, "headerName": "Header Name", + "@headerName": {"description": "Label for custom header key."}, "headerValue": "Header Value", + "@headerValue": {"description": "Label for custom header value."}, "headerValueHint": "api-key-123 or Bearer token", + "@headerValueHint": {"description": "Hint text with example header values, including API key or Bearer token."}, "addHeader": "Add header", + "@addHeader": {"description": "Button to add a new custom header row."}, "maximumHeadersReached": "Maximum headers reached", + "@maximumHeadersReached": {"description": "Warning when custom header limit is reached."}, "removeHeader": "Remove header", + "@removeHeader": {"description": "Action to remove a custom header row."}, "connecting": "Connecting...", + "@connecting": {"description": "Status while attempting to connect to server."}, "connectToServerButton": "Connect to Server", + "@connectToServerButton": {"description": "Primary action button to initiate server connection."}, "demoModeActive": "Demo Mode Active", + "@demoModeActive": {"description": "Banner/text indicating the app runs in demo mode."}, "skipServerSetupTryDemo": "Skip server setup and try the demo", + "@skipServerSetupTryDemo": {"description": "CTA to bypass server configuration and enter demo mode."}, "enterDemo": "Enter Demo", + "@enterDemo": {"description": "Button to enter demo mode."}, "demoBadge": "Demo", + "@demoBadge": {"description": "Small badge label for demo content."}, "serverNotOpenWebUI": "This does not appear to be an Open-WebUI server.", + "@serverNotOpenWebUI": {"description": "Validation error when the server does not resemble Open-WebUI."}, "serverUrlEmpty": "Server URL cannot be empty", + "@serverUrlEmpty": {"description": "Validation message for empty server URL."}, "invalidUrlFormat": "Invalid URL format. Please check your input.", + "@invalidUrlFormat": {"description": "Validation message when URL format is incorrect."}, "onlyHttpHttps": "Only HTTP and HTTPS protocols are supported.", + "@onlyHttpHttps": {"description": "Validation note restricting protocols to HTTP/HTTPS."}, "serverAddressRequired": "Server address is required (e.g., 192.168.1.10 or example.com).", + "@serverAddressRequired": {"description": "Validation hint providing examples for server addresses."}, "portRange": "Port must be between 1 and 65535.", + "@portRange": {"description": "Validation message for allowed port range."}, "invalidIpFormat": "Invalid IP address format. Use format like 192.168.1.10.", + "@invalidIpFormat": {"description": "Validation message for IP addresses with example."}, "couldNotConnectGeneric": "Couldn't connect. Double-check the address and try again.", + "@couldNotConnectGeneric": {"description": "Generic failure when connecting to the server."}, "weCouldntReachServer": "We couldn't reach the server. Check your connection and that the server is running.", + "@weCouldntReachServer": {"description": "Connectivity error with hints to verify server status."}, "connectionTimedOut": "Connection timed out. The server might be busy or blocked by a firewall.", + "@connectionTimedOut": {"description": "Timeout error while connecting to server."}, "useHttpOrHttpsOnly": "Use http:// or https:// only.", + "@useHttpOrHttpsOnly": {"description": "Note instructing the user to include protocol in URL."}, "loginFailed": "Login failed", + "@loginFailed": {"description": "Title for failed login attempts."}, "invalidCredentials": "Invalid username or password. Please try again.", + "@invalidCredentials": {"description": "Detailed message when authentication fails."}, "serverRedirectingHttps": "The server is redirecting requests. Check your server's HTTPS configuration.", + "@serverRedirectingHttps": {"description": "Warning about HTTP→HTTPS redirect issues."}, "unableToConnectServer": "Unable to connect to server. Please check your connection.", + "@unableToConnectServer": {"description": "Generic server connection failure message."}, "requestTimedOut": "The request timed out. Please try again.", + "@requestTimedOut": {"description": "Timeout while waiting for a server response."}, "genericSignInFailed": "We couldn't sign you in. Check your credentials and server settings.", + "@genericSignInFailed": {"description": "Fallback sign-in error when no specific cause is known."} + , "skip": "Skip", + "@skip": {"description": "Onboarding: skip current step."}, "next": "Next", + "@next": {"description": "Onboarding: go to the next step."}, "done": "Done", + "@done": {"description": "Onboarding: finish the flow."}, "onboardStartTitle": "Start a conversation", + "@onboardStartTitle": {"description": "Onboarding card: start chatting title."}, "onboardStartSubtitle": "Choose a model, then type below to begin. Tap New Chat anytime.", + "@onboardStartSubtitle": {"description": "Onboarding card: brief guidance to begin a chat."}, "onboardStartBullet1": "Tap the model name in the top bar to switch models", + "@onboardStartBullet1": {"description": "Bullet: how to switch models."}, "onboardStartBullet2": "Use New Chat to reset context", + "@onboardStartBullet2": {"description": "Bullet: how to reset context."}, "onboardAttachTitle": "Add context", + "@onboardAttachTitle": {"description": "Onboarding card: attach context title."}, "onboardAttachSubtitle": "Ground replies with content from Workspace or photos.", + "@onboardAttachSubtitle": {"description": "Onboarding card: why attaching context helps."}, "onboardAttachBullet1": "Workspace: PDFs, docs, datasets", + "@onboardAttachBullet1": {"description": "Bullet: types of workspace files."}, "onboardAttachBullet2": "Photos: camera or library", + "@onboardAttachBullet2": {"description": "Bullet: photo sources supported."}, "onboardSpeakTitle": "Speak naturally", + "@onboardSpeakTitle": {"description": "Onboarding card: voice input title."}, "onboardSpeakSubtitle": "Tap the mic to dictate with live waveform feedback.", + "@onboardSpeakSubtitle": {"description": "Onboarding card: how voice input works."}, "onboardSpeakBullet1": "Stop anytime; partial text is preserved", + "@onboardSpeakBullet1": {"description": "Bullet: stop dictation preserves text."}, "onboardSpeakBullet2": "Great for quick notes or long prompts", + "@onboardSpeakBullet2": {"description": "Bullet: benefits of voice input."}, "onboardQuickTitle": "Quick actions", + "@onboardQuickTitle": {"description": "Onboarding card: quick actions title."}, "onboardQuickSubtitle": "Open the menu to switch between Chats, Workspace, and Profile.", + "@onboardQuickSubtitle": {"description": "Onboarding card: how to use the app menu."}, "onboardQuickBullet1": "Tap the menu to access Chats, Workspace, Profile", - "onboardQuickBullet2": "Start New Chat or manage models from the top bar" + "@onboardQuickBullet1": {"description": "Bullet: menu access to sections."}, + "onboardQuickBullet2": "Start New Chat or manage models from the top bar", + "@onboardQuickBullet2": {"description": "Bullet: actions available in the top bar."} , "addAttachment": "Add attachment", + "@addAttachment": {"description": "Button to add an attachment (file/photo)."}, "tools": "Tools", + "@tools": {"description": "Header for a tools/actions section."}, "voiceInput": "Voice input", + "@voiceInput": {"description": "Label for voice input feature."}, "messageInputLabel": "Message input", + "@messageInputLabel": {"description": "Accessibility label for the message input."}, "messageInputHint": "Type your message", + "@messageInputHint": {"description": "Hint shown in the message input field."}, "messageHintText": "Message...", + "@messageHintText": {"description": "Short placeholder text in the message input."}, "stopGenerating": "Stop generating", + "@stopGenerating": {"description": "Action to stop the assistant's response generation."}, "send": "Send", + "@send": {"description": "Primary action to send a message."}, "sendMessage": "Send message", + "@sendMessage": {"description": "Semantic label for sending a message."}, "file": "File", + "@file": {"description": "A file item or attachment type label."}, "photo": "Photo", + "@photo": {"description": "A photo item or attachment type label."}, "camera": "Camera", + "@camera": {"description": "Camera source label."}, "apiUnavailable": "API service not available", + "@apiUnavailable": {"description": "Shown when backend API service is unavailable."}, "unableToLoadImage": "Unable to load image", + "@unableToLoadImage": {"description": "General failure to load an image."}, "notAnImageFile": "Not an image file: {fileName}", - "@notAnImageFile": {"placeholders": {"fileName": {"type": "String", "example": "image.txt"}}}, + "@notAnImageFile": { + "description": "Error when a referenced file is not an image.", + "placeholders": {"fileName": {"type": "String", "example": "image.txt"}} + }, "failedToLoadImage": "Failed to load image: {error}", - "@failedToLoadImage": {"placeholders": {"error": {"type": "String", "example": "Network error"}}}, + "@failedToLoadImage": { + "description": "Error including the underlying reason when image loading fails.", + "placeholders": {"error": {"type": "String", "example": "Network error"}} + }, "invalidDataUrl": "Invalid data URL format", + "@invalidDataUrl": {"description": "Error for malformed data: URLs."}, "failedToDecodeImage": "Failed to decode image", + "@failedToDecodeImage": {"description": "Error when decoding image bytes/base64."}, "invalidImageFormat": "Invalid image format", - "emptyImageData": "Empty image data" + "@invalidImageFormat": {"description": "Error when image type/format is not supported."}, + "emptyImageData": "Empty image data", + "@emptyImageData": {"description": "Error when image data buffer is empty."} , "offlineBanner": "You're offline. Some features may be limited.", + "@offlineBanner": {"description": "Banner warning when device is offline."}, "featureRequiresInternet": "This feature requires an internet connection", + "@featureRequiresInternet": {"description": "Informational text explaining internet requirement."}, "messagesWillSendWhenOnline": "Messages will be sent when you're back online", + "@messagesWillSendWhenOnline": {"description": "Queue behavior notice while offline."}, "confirm": "Confirm", - "cancel": "Cancel" + "@confirm": {"description": "Confirmation button label."}, + "cancel": "Cancel", + "@cancel": {"description": "Cancel button label."} , "ok": "OK", + "@ok": {"description": "Generic OK button label."}, "inputField": "Input field", + "@inputField": {"description": "Accessibility label describing an input field."}, "captureDocumentOrImage": "Capture a document or image", + "@captureDocumentOrImage": {"description": "Action to capture a document or image using camera."}, "checkConnection": "Check Connection", + "@checkConnection": {"description": "CTA to verify network connectivity."}, "openSettings": "Open Settings", + "@openSettings": {"description": "CTA to open device or app settings."}, "chooseDifferentFile": "Choose Different File", + "@chooseDifferentFile": {"description": "CTA to pick an alternative file."}, "goBack": "Go Back", + "@goBack": {"description": "CTA to navigate back."}, "technicalDetails": "Technical Details", + "@technicalDetails": {"description": "Expandable section label to show error details or logs."}, "save": "Save", + "@save": {"description": "Primary action to save changes."}, "chooseModel": "Choose Model", + "@chooseModel": {"description": "Button/label to choose a model."}, "reviewerMode": "REVIEWER MODE", + "@reviewerMode": {"description": "Developer/reviewer mode indicator."}, "selectLanguage": "Select Language", + "@selectLanguage": {"description": "Dialog title to pick application language."}, "newFolder": "New Folder", + "@newFolder": {"description": "Action to create a new folder."}, "folderName": "Folder name", + "@folderName": {"description": "Label for entering a folder's name."}, "newChat": "New Chat", + "@newChat": {"description": "Action to start a new chat."}, "more": "More", + "@more": {"description": "Opens additional actions or content."}, "clear": "Clear", + "@clear": {"description": "Action to clear input or selection."}, "searchHint": "Search...", + "@searchHint": {"description": "Generic search input hint."}, "searchConversations": "Search conversations...", + "@searchConversations": {"description": "Search input hint scoped to conversations."}, "create": "Create", + "@create": {"description": "Primary action to create a resource."}, "folderCreated": "Folder created", + "@folderCreated": {"description": "Toast/notice after successfully creating a folder."}, "failedToCreateFolder": "Failed to create folder", + "@failedToCreateFolder": {"description": "Error notice when folder creation fails."}, "movedChatToFolder": "Moved \"{title}\" to \"{folder}\"", "@movedChatToFolder": { + "description": "Toast indicating a chat titled {title} was moved to folder {folder}.", "placeholders": { "title": {"type": "String"}, "folder": {"type": "String"} } }, "failedToMoveChat": "Failed to move chat", + "@failedToMoveChat": {"description": "Error notice when moving a chat fails."}, "failedToLoadChats": "Failed to load chats", + "@failedToLoadChats": {"description": "Error notice when fetching chat list fails."}, "failedToUpdatePin": "Failed to update pin", + "@failedToUpdatePin": {"description": "Error notice when updating pin star/flag fails."}, "failedToDeleteChat": "Failed to delete chat", + "@failedToDeleteChat": {"description": "Error notice when deleting a chat fails."}, "manage": "Manage", + "@manage": {"description": "Context action to manage an item."}, "rename": "Rename", + "@rename": {"description": "Context action to rename an item."}, "delete": "Delete", + "@delete": {"description": "Context action to delete an item."}, "renameChat": "Rename Chat", + "@renameChat": {"description": "Dialog title to rename a chat."}, "enterChatName": "Enter chat name", + "@enterChatName": {"description": "Input hint/label for new chat name."}, "failedToRenameChat": "Failed to rename chat", + "@failedToRenameChat": {"description": "Error notice when renaming chat fails."}, "failedToUpdateArchive": "Failed to update archive", + "@failedToUpdateArchive": {"description": "Error notice when archiving/unarchiving fails."}, "unarchive": "Unarchive", + "@unarchive": {"description": "Action to unarchive an item."}, "archive": "Archive", + "@archive": {"description": "Action to archive an item."}, "pin": "Pin", + "@pin": {"description": "Action to pin/star an item."}, "unpin": "Unpin", + "@unpin": {"description": "Action to remove pin from an item."}, "recent": "Recent", + "@recent": {"description": "List filter for recently used items."}, "system": "System", + "@system": {"description": "Option indicating the device/system default."}, "english": "English", + "@english": {"description": "Language name: English."}, "deutsch": "Deutsch", + "@deutsch": {"description": "Language name: German."}, "francais": "Français", + "@francais": {"description": "Language name: French."}, "italiano": "Italiano", + "@italiano": {"description": "Language name: Italian."}, "deleteMessagesTitle": "Delete Messages", + "@deleteMessagesTitle": {"description": "Dialog title asking to confirm deletion of messages."}, "deleteMessagesMessage": "Delete {count} messages?", "@deleteMessagesMessage": { + "description": "Confirmation prompt asking to delete a number of messages.", "placeholders": { "count": {"type": "int"} } }, "routeNotFound": "Route not found: {routeName}", "@routeNotFound": { + "description": "Displayed when navigation fails to find a route name.", "placeholders": { "routeName": {"type": "String"} } }, "deleteChatTitle": "Delete Chat", + "@deleteChatTitle": {"description": "Dialog title asking to confirm deletion of a chat."}, "deleteChatMessage": "This chat will be permanently deleted.", + "@deleteChatMessage": {"description": "Warning that deleting a chat cannot be undone."}, "aboutApp": "About App", + "@aboutApp": {"description": "Settings tile title to view app information."}, "aboutAppSubtitle": "Conduit information and links", + "@aboutAppSubtitle": {"description": "Subtitle/description for the About section."}, "typeBelowToBegin": "Type below to begin", + "@typeBelowToBegin": {"description": "Hint shown in empty chat input area."}, "web": "Web", + "@web": {"description": "Tab/section label for web features."}, "imageGen": "Image Gen", + "@imageGen": {"description": "Short label for image generation section/tab."}, "pinned": "Pinned", + "@pinned": {"description": "Filter/tab for pinned items."}, "folders": "Folders", + "@folders": {"description": "Tab listing chat folders."}, "archived": "Archived", + "@archived": {"description": "Filter/tab for archived chats."}, "appLanguage": "App Language", + "@appLanguage": {"description": "Label for choosing the app's display language."}, "darkMode": "Dark Mode", + "@darkMode": {"description": "Label for toggling dark theme."}, "webSearch": "Web Search", + "@webSearch": {"description": "Feature toggle/section for web search."}, "webSearchDescription": "Search the web and cite sources in replies.", + "@webSearchDescription": {"description": "Explains that responses can include citations from the web."}, "imageGeneration": "Image Generation", + "@imageGeneration": {"description": "Feature toggle/section for image generation."}, "imageGenerationDescription": "Create images from your prompts.", + "@imageGenerationDescription": {"description": "Explains creating images via model prompts."}, "copy": "Copy", + "@copy": {"description": "Action to copy text to clipboard."}, "edit": "Edit", + "@edit": {"description": "Action to edit an item/message."}, "regenerate": "Regenerate", - "noConversationsYet": "No conversations yet" + "@regenerate": {"description": "Action to request a new assistant response."}, + "noConversationsYet": "No conversations yet", + "@noConversationsYet": {"description": "Empty state when the user has no chats."} , "usernameOrEmailHint": "Enter your username or email", + "@usernameOrEmailHint": {"description": "Hint text for username/email input."}, "passwordHint": "Enter your password", + "@passwordHint": {"description": "Hint text for password input."}, "enterApiKey": "Enter your API key", + "@enterApiKey": {"description": "Hint text for API key input."}, "signingIn": "Signing in...", + "@signingIn": {"description": "Status message shown while signing in."}, "advancedSettings": "Advanced Settings", + "@advancedSettings": {"description": "Section that contains additional/optional configuration."}, "customHeaders": "Custom Headers", + "@customHeaders": {"description": "Section title for adding custom HTTP headers."}, "customHeadersDescription": "Add custom HTTP headers for authentication, API keys, or special server requirements.", + "@customHeadersDescription": {"description": "Helper text explaining use-cases for custom headers."}, "headerNameEmpty": "Header name cannot be empty", + "@headerNameEmpty": {"description": "Validation message for empty header name."}, "headerNameTooLong": "Header name too long (max 64 characters)", + "@headerNameTooLong": {"description": "Validation message for header name length."}, "headerNameInvalidChars": "Invalid header name. Use only letters, numbers, and these symbols: !#$&-^_`|~", + "@headerNameInvalidChars": {"description": "Validation message for invalid characters in header name."}, "headerNameReserved": "Cannot override reserved header \"{key}\"", - "@headerNameReserved": {"placeholders": {"key": {"type": "String"}}}, + "@headerNameReserved": { + "description": "Error when attempting to override a reserved HTTP header {key}.", + "placeholders": {"key": {"type": "String"}} + }, "headerValueEmpty": "Header value cannot be empty", + "@headerValueEmpty": {"description": "Validation message for empty header value."}, "headerValueTooLong": "Header value too long (max 1024 characters)", + "@headerValueTooLong": {"description": "Validation message for header value length."}, "headerValueInvalidChars": "Header value contains invalid characters. Use only printable ASCII.", + "@headerValueInvalidChars": {"description": "Validation message for invalid characters in header value."}, "headerValueUnsafe": "Header value appears to contain potentially unsafe content", + "@headerValueUnsafe": {"description": "Security warning for suspicious header values."}, "headerAlreadyExists": "Header \"{key}\" already exists. Remove it first to update.", - "@headerAlreadyExists": {"placeholders": {"key": {"type": "String"}}}, - "maxHeadersReachedDetail": "Maximum of 10 custom headers allowed. Remove some to add more." + "@headerAlreadyExists": { + "description": "Error when a custom header with key {key} already exists.", + "placeholders": {"key": {"type": "String"}} + }, + "maxHeadersReachedDetail": "Maximum of 10 custom headers allowed. Remove some to add more.", + "@maxHeadersReachedDetail": {"description": "Explains the upper limit of custom headers."} , - "editMessage": "Edit Message" + "editMessage": "Edit Message", + "@editMessage": {"description": "Action to edit a previously sent message."} , "noModelsAvailable": "No models available", + "@noModelsAvailable": {"description": "Shown when model list is empty or failed to load."}, "followingSystem": "Following system: {theme}", - "@followingSystem": {"placeholders": {"theme": {"type": "String"}}}, + "@followingSystem": { + "description": "Indicates the app is following the system theme (\"Dark\"/\"Light\").", + "placeholders": {"theme": {"type": "String"}} + }, "themeDark": "Dark", + "@themeDark": {"description": "Theme label for dark appearance."}, "themeLight": "Light", + "@themeLight": {"description": "Theme label for light appearance."}, "currentlyUsingDarkTheme": "Currently using Dark theme", + "@currentlyUsingDarkTheme": {"description": "Status text indicating dark theme is active."}, "currentlyUsingLightTheme": "Currently using Light theme", + "@currentlyUsingLightTheme": {"description": "Status text indicating light theme is active."}, "aboutConduit": "About Conduit", + "@aboutConduit": {"description": "Dialog title for app information."}, "versionLabel": "Version: {version} ({build})", - "@versionLabel": {"placeholders": {"version": {"type": "String"}, "build": {"type": "String"}}}, + "@versionLabel": { + "description": "Displays version and build number in the About dialog.", + "placeholders": {"version": {"type": "String"}, "build": {"type": "String"}} + }, "githubRepository": "GitHub Repository", + "@githubRepository": {"description": "Link label pointing to the app repository."}, "unableToLoadAppInfo": "Unable to load app info", + "@unableToLoadAppInfo": {"description": "Error text when package info cannot be retrieved."}, "thinking": "Thinking…", + "@thinking": {"description": "Label shown while the assistant is reasoning."}, "thoughts": "Thoughts", + "@thoughts": {"description": "Section title for showing reasoning content."}, "thoughtForDuration": "Thought for {duration}", "@thoughtForDuration": { "description": "Shows how long the assistant thought before replying.", @@ -285,16 +523,30 @@ } , "appCustomization": "App Customization", + "@appCustomization": {"description": "Title of the customization settings page."}, "appCustomizationSubtitle": "Personalize how names and UI display", + "@appCustomizationSubtitle": {"description": "Subtitle shown under App Customization tile and page header."}, "display": "Display", + "@display": {"description": "Section header for visual and layout related settings."}, "realtime": "Realtime", + "@realtime": {"description": "Section header for realtime/transport settings."}, "hideProviderInModelNames": "Hide provider in model names", + "@hideProviderInModelNames": {"description": "Toggle label to hide the provider prefix in model names (e.g., show gpt-4o instead of openai/gpt-4o)."}, "hideProviderInModelNamesDescription": "Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".", + "@hideProviderInModelNamesDescription": {"description": "Helper text for provider hiding toggle."}, "transportMode": "Transport mode", + "@transportMode": {"description": "Title for selecting the networking transport used for realtime."}, "transportModeDescription": "Choose how the app connects for realtime updates.", + "@transportModeDescription": {"description": "Helper text explaining the transport setting."}, "mode": "Mode", + "@mode": {"description": "Form field label for transport mode dropdown."}, "transportModeAuto": "Auto (Polling + WebSocket)", + "@transportModeAuto": {"description": "Dropdown option label for automatic transport selection."}, "transportModeWs": "WebSocket only", + "@transportModeWs": {"description": "Dropdown option label for WebSocket-only transport."}, "transportModeAutoInfo": "More robust on restrictive networks. Upgrades to WebSocket when possible.", + "@transportModeAutoInfo": {"description": "Footnote text for the Auto transport mode."}, "transportModeWsInfo": "Lower overhead, but may fail behind strict proxies/firewalls." + , + "@transportModeWsInfo": {"description": "Footnote text for the WebSocket-only transport mode."} } diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 6626d67..ee48477 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -64,7 +64,8 @@ import 'app_localizations_it.dart'; /// be consistent with the languages listed in the AppLocalizations.supportedLocales /// property. abstract class AppLocalizations { - AppLocalizations(String locale) : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); final String localeName; @@ -72,7 +73,8 @@ abstract class AppLocalizations { return Localizations.of(context, AppLocalizations); } - static const LocalizationsDelegate delegate = _AppLocalizationsDelegate(); + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); /// A list of this localizations delegate along with the default localizations /// delegates. @@ -84,142 +86,143 @@ abstract class AppLocalizations { /// Additional delegates can be added by appending to this list in /// MaterialApp. This list does not have to be used at all if a custom list /// of delegates is preferred or required. - static const List> localizationsDelegates = >[ - delegate, - GlobalMaterialLocalizations.delegate, - GlobalCupertinoLocalizations.delegate, - GlobalWidgetsLocalizations.delegate, - ]; + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; /// A list of this localizations delegate's supported locales. static const List supportedLocales = [ - Locale('de'), Locale('en'), + Locale('de'), Locale('fr'), - Locale('it') + Locale('it'), ]; - /// No description provided for @appTitle. + /// Application name displayed in the app and OS UI. /// /// In en, this message translates to: /// **'Conduit'** String get appTitle; - /// No description provided for @initializationFailed. + /// Shown if the app fails to initialize critical services. /// /// In en, this message translates to: /// **'Initialization Failed'** String get initializationFailed; - /// No description provided for @retry. + /// Button label to try an action again. /// /// In en, this message translates to: /// **'Retry'** String get retry; - /// No description provided for @back. + /// Back navigation label/tooltip. /// /// In en, this message translates to: /// **'Back'** String get back; - /// No description provided for @you. + /// Profile tab title. /// /// In en, this message translates to: /// **'You'** String get you; - /// No description provided for @loadingProfile. + /// Progress message while fetching profile data. /// /// In en, this message translates to: /// **'Loading profile...'** String get loadingProfile; - /// No description provided for @unableToLoadProfile. + /// Error title shown when profile request fails. /// /// In en, this message translates to: /// **'Unable to load profile'** String get unableToLoadProfile; - /// No description provided for @pleaseCheckConnection. + /// Generic connectivity hint after an error. /// /// In en, this message translates to: /// **'Please check your connection and try again'** String get pleaseCheckConnection; - /// No description provided for @account. + /// Section header for account-related options. /// /// In en, this message translates to: /// **'Account'** String get account; - /// No description provided for @signOut. + /// Button/title for signing out of the app. /// /// In en, this message translates to: /// **'Sign Out'** String get signOut; - /// No description provided for @endYourSession. + /// Subtitle explaining the sign-out action. /// /// In en, this message translates to: /// **'End your session'** String get endYourSession; - /// No description provided for @defaultModel. + /// Label for choosing a default AI model. /// /// In en, this message translates to: /// **'Default Model'** String get defaultModel; - /// No description provided for @autoSelect. + /// Option to let the app pick a suitable model automatically. /// /// In en, this message translates to: /// **'Auto-select'** String get autoSelect; - /// No description provided for @loadingModels. + /// Progress message while fetching model list. /// /// In en, this message translates to: /// **'Loading models...'** String get loadingModels; - /// No description provided for @failedToLoadModels. + /// Error message shown when model list cannot be retrieved. /// /// In en, this message translates to: /// **'Failed to load models'** String get failedToLoadModels; - /// No description provided for @availableModels. + /// Header above a list of models to select from. /// /// In en, this message translates to: /// **'Available Models'** String get availableModels; - /// No description provided for @noResults. + /// Shown when a search returns no matches. /// /// In en, this message translates to: /// **'No results'** String get noResults; - /// No description provided for @searchModels. + /// Hint text for model search input. /// /// In en, this message translates to: /// **'Search models...'** String get searchModels; - /// No description provided for @errorMessage. + /// Generic error message for unexpected failures. /// /// In en, this message translates to: /// **'Something went wrong. Please try again.'** String get errorMessage; - /// No description provided for @loginButton. + /// Button text for the login action. /// /// In en, this message translates to: /// **'Login'** String get loginButton; - /// No description provided for @menuItem. + /// Generic settings menu item label. /// /// In en, this message translates to: /// **'Settings'** @@ -237,115 +240,115 @@ abstract class AppLocalizations { /// **'{count, plural, =0{No items} one{1 item} other{{count} items}}'** String itemsCount(int count); - /// No description provided for @closeButtonSemantic. + /// Accessible label for a generic Close button. /// /// In en, this message translates to: /// **'Close'** String get closeButtonSemantic; - /// No description provided for @loadingContent. + /// Shown while loading page content. /// /// In en, this message translates to: /// **'Loading content'** String get loadingContent; - /// No description provided for @noItems. + /// Placeholder text when a list is empty. /// /// In en, this message translates to: /// **'No items'** String get noItems; - /// No description provided for @noItemsToDisplay. + /// Alternative empty-state description. /// /// In en, this message translates to: /// **'No items to display'** String get noItemsToDisplay; - /// No description provided for @loadMore. + /// Button label to load additional items in a paged list. /// /// In en, this message translates to: /// **'Load More'** String get loadMore; - /// No description provided for @workspace. + /// Section/tab label for documents and files. /// /// In en, this message translates to: /// **'Workspace'** String get workspace; - /// No description provided for @recentFiles. + /// Header for recently accessed files. /// /// In en, this message translates to: /// **'Recent Files'** String get recentFiles; - /// No description provided for @knowledgeBase. + /// Section for knowledge base content. /// /// In en, this message translates to: /// **'Knowledge Base'** String get knowledgeBase; - /// No description provided for @noFilesYet. + /// Empty state when no files are present. /// /// In en, this message translates to: /// **'No files yet'** String get noFilesYet; - /// No description provided for @uploadDocsPrompt. + /// Prompt encouraging users to upload documents. /// /// In en, this message translates to: /// **'Upload documents to reference in your conversations with Conduit'** String get uploadDocsPrompt; - /// No description provided for @uploadFirstFile. + /// CTA to add the first file. /// /// In en, this message translates to: /// **'Upload your first file'** String get uploadFirstFile; - /// No description provided for @knowledgeBaseEmpty. + /// Empty state title for the knowledge base section. /// /// In en, this message translates to: /// **'Knowledge base is empty'** String get knowledgeBaseEmpty; - /// No description provided for @createCollectionsPrompt. + /// Prompt describing the benefit of creating collections. /// /// In en, this message translates to: /// **'Create collections of related documents for easy reference'** String get createCollectionsPrompt; - /// No description provided for @chooseSourcePhoto. + /// Sheet title to pick camera or photo library. /// /// In en, this message translates to: /// **'Choose your source'** String get chooseSourcePhoto; - /// No description provided for @takePhoto. + /// Action to open camera and capture a new photo. /// /// In en, this message translates to: /// **'Take a photo'** String get takePhoto; - /// No description provided for @chooseFromGallery. + /// Action to pick an existing photo from library. /// /// In en, this message translates to: /// **'Choose from your photos'** String get chooseFromGallery; - /// No description provided for @document. + /// Generic document label used in UI. /// /// In en, this message translates to: /// **'Document'** String get document; - /// No description provided for @documentHint. + /// Helper hint listing supported document types. /// /// In en, this message translates to: /// **'PDF, Word, or text file'** String get documentHint; - /// No description provided for @uploadFileTitle. + /// Dialog/sheet title for file upload. /// /// In en, this message translates to: /// **'Upload File'** @@ -357,1111 +360,1111 @@ abstract class AppLocalizations { /// **'File upload for {type} is coming soon!'** String fileUploadComingSoon(String type); - /// No description provided for @kbCreationComingSoon. + /// Temporary message indicating KB creation feature is not yet available. /// /// In en, this message translates to: /// **'Knowledge base creation is coming soon!'** String get kbCreationComingSoon; - /// No description provided for @backToServerSetup. + /// Button/back label to return to server configuration flow. /// /// In en, this message translates to: /// **'Back to server setup'** String get backToServerSetup; - /// No description provided for @connectedToServer. + /// Status label indicating a successful server connection. /// /// In en, this message translates to: /// **'Connected to Server'** String get connectedToServer; - /// No description provided for @signIn. + /// Button/heading for sign-in flows. /// /// In en, this message translates to: /// **'Sign In'** String get signIn; - /// No description provided for @enterCredentials. + /// Instructional text on the sign-in screen. /// /// In en, this message translates to: /// **'Enter your credentials to access your AI conversations'** String get enterCredentials; - /// No description provided for @credentials. + /// Header for credential input section. /// /// In en, this message translates to: /// **'Credentials'** String get credentials; - /// No description provided for @apiKey. + /// Label for API key input field. /// /// In en, this message translates to: /// **'API Key'** String get apiKey; - /// No description provided for @usernameOrEmail. + /// Label for username/email input field. /// /// In en, this message translates to: /// **'Username or Email'** String get usernameOrEmail; - /// No description provided for @password. + /// Label for password input field. /// /// In en, this message translates to: /// **'Password'** String get password; - /// No description provided for @signInWithApiKey. + /// Alternative sign-in method using an API key. /// /// In en, this message translates to: /// **'Sign in with API Key'** String get signInWithApiKey; - /// No description provided for @connectToServer. + /// Call-to-action button for server connection. /// /// In en, this message translates to: /// **'Connect to Server'** String get connectToServer; - /// No description provided for @enterServerAddress. + /// Instruction telling user to provide server URL to begin. /// /// In en, this message translates to: /// **'Enter your Open-WebUI server address to get started'** String get enterServerAddress; - /// No description provided for @serverUrl. + /// Label for server URL field. /// /// In en, this message translates to: /// **'Server URL'** String get serverUrl; - /// No description provided for @serverUrlHint. + /// Hint text showing example server URL format. /// /// In en, this message translates to: /// **'https://your-server.com'** String get serverUrlHint; - /// No description provided for @enterServerUrlSemantic. + /// Semantic/ARIA label instructing to enter server URL or IP. /// /// In en, this message translates to: /// **'Enter your server URL or IP address'** String get enterServerUrlSemantic; - /// No description provided for @headerName. + /// Label for custom header key. /// /// In en, this message translates to: /// **'Header Name'** String get headerName; - /// No description provided for @headerValue. + /// Label for custom header value. /// /// In en, this message translates to: /// **'Header Value'** String get headerValue; - /// No description provided for @headerValueHint. + /// Hint text with example header values, including API key or Bearer token. /// /// In en, this message translates to: /// **'api-key-123 or Bearer token'** String get headerValueHint; - /// No description provided for @addHeader. + /// Button to add a new custom header row. /// /// In en, this message translates to: /// **'Add header'** String get addHeader; - /// No description provided for @maximumHeadersReached. + /// Warning when custom header limit is reached. /// /// In en, this message translates to: /// **'Maximum headers reached'** String get maximumHeadersReached; - /// No description provided for @removeHeader. + /// Action to remove a custom header row. /// /// In en, this message translates to: /// **'Remove header'** String get removeHeader; - /// No description provided for @connecting. + /// Status while attempting to connect to server. /// /// In en, this message translates to: /// **'Connecting...'** String get connecting; - /// No description provided for @connectToServerButton. + /// Primary action button to initiate server connection. /// /// In en, this message translates to: /// **'Connect to Server'** String get connectToServerButton; - /// No description provided for @demoModeActive. + /// Banner/text indicating the app runs in demo mode. /// /// In en, this message translates to: /// **'Demo Mode Active'** String get demoModeActive; - /// No description provided for @skipServerSetupTryDemo. + /// CTA to bypass server configuration and enter demo mode. /// /// In en, this message translates to: /// **'Skip server setup and try the demo'** String get skipServerSetupTryDemo; - /// No description provided for @enterDemo. + /// Button to enter demo mode. /// /// In en, this message translates to: /// **'Enter Demo'** String get enterDemo; - /// No description provided for @demoBadge. + /// Small badge label for demo content. /// /// In en, this message translates to: /// **'Demo'** String get demoBadge; - /// No description provided for @serverNotOpenWebUI. + /// Validation error when the server does not resemble Open-WebUI. /// /// In en, this message translates to: /// **'This does not appear to be an Open-WebUI server.'** String get serverNotOpenWebUI; - /// No description provided for @serverUrlEmpty. + /// Validation message for empty server URL. /// /// In en, this message translates to: /// **'Server URL cannot be empty'** String get serverUrlEmpty; - /// No description provided for @invalidUrlFormat. + /// Validation message when URL format is incorrect. /// /// In en, this message translates to: /// **'Invalid URL format. Please check your input.'** String get invalidUrlFormat; - /// No description provided for @onlyHttpHttps. + /// Validation note restricting protocols to HTTP/HTTPS. /// /// In en, this message translates to: /// **'Only HTTP and HTTPS protocols are supported.'** String get onlyHttpHttps; - /// No description provided for @serverAddressRequired. + /// Validation hint providing examples for server addresses. /// /// In en, this message translates to: /// **'Server address is required (e.g., 192.168.1.10 or example.com).'** String get serverAddressRequired; - /// No description provided for @portRange. + /// Validation message for allowed port range. /// /// In en, this message translates to: /// **'Port must be between 1 and 65535.'** String get portRange; - /// No description provided for @invalidIpFormat. + /// Validation message for IP addresses with example. /// /// In en, this message translates to: /// **'Invalid IP address format. Use format like 192.168.1.10.'** String get invalidIpFormat; - /// No description provided for @couldNotConnectGeneric. + /// Generic failure when connecting to the server. /// /// In en, this message translates to: /// **'Couldn\'t connect. Double-check the address and try again.'** String get couldNotConnectGeneric; - /// No description provided for @weCouldntReachServer. + /// Connectivity error with hints to verify server status. /// /// In en, this message translates to: /// **'We couldn\'t reach the server. Check your connection and that the server is running.'** String get weCouldntReachServer; - /// No description provided for @connectionTimedOut. + /// Timeout error while connecting to server. /// /// In en, this message translates to: /// **'Connection timed out. The server might be busy or blocked by a firewall.'** String get connectionTimedOut; - /// No description provided for @useHttpOrHttpsOnly. + /// Note instructing the user to include protocol in URL. /// /// In en, this message translates to: /// **'Use http:// or https:// only.'** String get useHttpOrHttpsOnly; - /// No description provided for @loginFailed. + /// Title for failed login attempts. /// /// In en, this message translates to: /// **'Login failed'** String get loginFailed; - /// No description provided for @invalidCredentials. + /// Detailed message when authentication fails. /// /// In en, this message translates to: /// **'Invalid username or password. Please try again.'** String get invalidCredentials; - /// No description provided for @serverRedirectingHttps. + /// Warning about HTTP→HTTPS redirect issues. /// /// In en, this message translates to: /// **'The server is redirecting requests. Check your server\'s HTTPS configuration.'** String get serverRedirectingHttps; - /// No description provided for @unableToConnectServer. + /// Generic server connection failure message. /// /// In en, this message translates to: /// **'Unable to connect to server. Please check your connection.'** String get unableToConnectServer; - /// No description provided for @requestTimedOut. + /// Timeout while waiting for a server response. /// /// In en, this message translates to: /// **'The request timed out. Please try again.'** String get requestTimedOut; - /// No description provided for @genericSignInFailed. + /// Fallback sign-in error when no specific cause is known. /// /// In en, this message translates to: /// **'We couldn\'t sign you in. Check your credentials and server settings.'** String get genericSignInFailed; - /// No description provided for @skip. + /// Onboarding: skip current step. /// /// In en, this message translates to: /// **'Skip'** String get skip; - /// No description provided for @next. + /// Onboarding: go to the next step. /// /// In en, this message translates to: /// **'Next'** String get next; - /// No description provided for @done. + /// Onboarding: finish the flow. /// /// In en, this message translates to: /// **'Done'** String get done; - /// No description provided for @onboardStartTitle. + /// Onboarding card: start chatting title. /// /// In en, this message translates to: /// **'Start a conversation'** String get onboardStartTitle; - /// No description provided for @onboardStartSubtitle. + /// Onboarding card: brief guidance to begin a chat. /// /// In en, this message translates to: /// **'Choose a model, then type below to begin. Tap New Chat anytime.'** String get onboardStartSubtitle; - /// No description provided for @onboardStartBullet1. + /// Bullet: how to switch models. /// /// In en, this message translates to: /// **'Tap the model name in the top bar to switch models'** String get onboardStartBullet1; - /// No description provided for @onboardStartBullet2. + /// Bullet: how to reset context. /// /// In en, this message translates to: /// **'Use New Chat to reset context'** String get onboardStartBullet2; - /// No description provided for @onboardAttachTitle. + /// Onboarding card: attach context title. /// /// In en, this message translates to: /// **'Add context'** String get onboardAttachTitle; - /// No description provided for @onboardAttachSubtitle. + /// Onboarding card: why attaching context helps. /// /// In en, this message translates to: /// **'Ground replies with content from Workspace or photos.'** String get onboardAttachSubtitle; - /// No description provided for @onboardAttachBullet1. + /// Bullet: types of workspace files. /// /// In en, this message translates to: /// **'Workspace: PDFs, docs, datasets'** String get onboardAttachBullet1; - /// No description provided for @onboardAttachBullet2. + /// Bullet: photo sources supported. /// /// In en, this message translates to: /// **'Photos: camera or library'** String get onboardAttachBullet2; - /// No description provided for @onboardSpeakTitle. + /// Onboarding card: voice input title. /// /// In en, this message translates to: /// **'Speak naturally'** String get onboardSpeakTitle; - /// No description provided for @onboardSpeakSubtitle. + /// Onboarding card: how voice input works. /// /// In en, this message translates to: /// **'Tap the mic to dictate with live waveform feedback.'** String get onboardSpeakSubtitle; - /// No description provided for @onboardSpeakBullet1. + /// Bullet: stop dictation preserves text. /// /// In en, this message translates to: /// **'Stop anytime; partial text is preserved'** String get onboardSpeakBullet1; - /// No description provided for @onboardSpeakBullet2. + /// Bullet: benefits of voice input. /// /// In en, this message translates to: /// **'Great for quick notes or long prompts'** String get onboardSpeakBullet2; - /// No description provided for @onboardQuickTitle. + /// Onboarding card: quick actions title. /// /// In en, this message translates to: /// **'Quick actions'** String get onboardQuickTitle; - /// No description provided for @onboardQuickSubtitle. + /// Onboarding card: how to use the app menu. /// /// In en, this message translates to: /// **'Open the menu to switch between Chats, Workspace, and Profile.'** String get onboardQuickSubtitle; - /// No description provided for @onboardQuickBullet1. + /// Bullet: menu access to sections. /// /// In en, this message translates to: /// **'Tap the menu to access Chats, Workspace, Profile'** String get onboardQuickBullet1; - /// No description provided for @onboardQuickBullet2. + /// Bullet: actions available in the top bar. /// /// In en, this message translates to: /// **'Start New Chat or manage models from the top bar'** String get onboardQuickBullet2; - /// No description provided for @addAttachment. + /// Button to add an attachment (file/photo). /// /// In en, this message translates to: /// **'Add attachment'** String get addAttachment; - /// No description provided for @tools. + /// Header for a tools/actions section. /// /// In en, this message translates to: /// **'Tools'** String get tools; - /// No description provided for @voiceInput. + /// Label for voice input feature. /// /// In en, this message translates to: /// **'Voice input'** String get voiceInput; - /// No description provided for @messageInputLabel. + /// Accessibility label for the message input. /// /// In en, this message translates to: /// **'Message input'** String get messageInputLabel; - /// No description provided for @messageInputHint. + /// Hint shown in the message input field. /// /// In en, this message translates to: /// **'Type your message'** String get messageInputHint; - /// No description provided for @messageHintText. + /// Short placeholder text in the message input. /// /// In en, this message translates to: /// **'Message...'** String get messageHintText; - /// No description provided for @stopGenerating. + /// Action to stop the assistant's response generation. /// /// In en, this message translates to: /// **'Stop generating'** String get stopGenerating; - /// No description provided for @send. + /// Primary action to send a message. /// /// In en, this message translates to: /// **'Send'** String get send; - /// No description provided for @sendMessage. + /// Semantic label for sending a message. /// /// In en, this message translates to: /// **'Send message'** String get sendMessage; - /// No description provided for @file. + /// A file item or attachment type label. /// /// In en, this message translates to: /// **'File'** String get file; - /// No description provided for @photo. + /// A photo item or attachment type label. /// /// In en, this message translates to: /// **'Photo'** String get photo; - /// No description provided for @camera. + /// Camera source label. /// /// In en, this message translates to: /// **'Camera'** String get camera; - /// No description provided for @apiUnavailable. + /// Shown when backend API service is unavailable. /// /// In en, this message translates to: /// **'API service not available'** String get apiUnavailable; - /// No description provided for @unableToLoadImage. + /// General failure to load an image. /// /// In en, this message translates to: /// **'Unable to load image'** String get unableToLoadImage; - /// No description provided for @notAnImageFile. + /// Error when a referenced file is not an image. /// /// In en, this message translates to: /// **'Not an image file: {fileName}'** String notAnImageFile(String fileName); - /// No description provided for @failedToLoadImage. + /// Error including the underlying reason when image loading fails. /// /// In en, this message translates to: /// **'Failed to load image: {error}'** String failedToLoadImage(String error); - /// No description provided for @invalidDataUrl. + /// Error for malformed data: URLs. /// /// In en, this message translates to: /// **'Invalid data URL format'** String get invalidDataUrl; - /// No description provided for @failedToDecodeImage. + /// Error when decoding image bytes/base64. /// /// In en, this message translates to: /// **'Failed to decode image'** String get failedToDecodeImage; - /// No description provided for @invalidImageFormat. + /// Error when image type/format is not supported. /// /// In en, this message translates to: /// **'Invalid image format'** String get invalidImageFormat; - /// No description provided for @emptyImageData. + /// Error when image data buffer is empty. /// /// In en, this message translates to: /// **'Empty image data'** String get emptyImageData; - /// No description provided for @offlineBanner. + /// Banner warning when device is offline. /// /// In en, this message translates to: /// **'You\'re offline. Some features may be limited.'** String get offlineBanner; - /// No description provided for @featureRequiresInternet. + /// Informational text explaining internet requirement. /// /// In en, this message translates to: /// **'This feature requires an internet connection'** String get featureRequiresInternet; - /// No description provided for @messagesWillSendWhenOnline. + /// Queue behavior notice while offline. /// /// In en, this message translates to: /// **'Messages will be sent when you\'re back online'** String get messagesWillSendWhenOnline; - /// No description provided for @confirm. + /// Confirmation button label. /// /// In en, this message translates to: /// **'Confirm'** String get confirm; - /// No description provided for @cancel. + /// Cancel button label. /// /// In en, this message translates to: /// **'Cancel'** String get cancel; - /// No description provided for @ok. + /// Generic OK button label. /// /// In en, this message translates to: /// **'OK'** String get ok; - /// No description provided for @inputField. + /// Accessibility label describing an input field. /// /// In en, this message translates to: /// **'Input field'** String get inputField; - /// No description provided for @captureDocumentOrImage. + /// Action to capture a document or image using camera. /// /// In en, this message translates to: /// **'Capture a document or image'** String get captureDocumentOrImage; - /// No description provided for @checkConnection. + /// CTA to verify network connectivity. /// /// In en, this message translates to: /// **'Check Connection'** String get checkConnection; - /// No description provided for @openSettings. + /// CTA to open device or app settings. /// /// In en, this message translates to: /// **'Open Settings'** String get openSettings; - /// No description provided for @chooseDifferentFile. + /// CTA to pick an alternative file. /// /// In en, this message translates to: /// **'Choose Different File'** String get chooseDifferentFile; - /// No description provided for @goBack. + /// CTA to navigate back. /// /// In en, this message translates to: /// **'Go Back'** String get goBack; - /// No description provided for @technicalDetails. + /// Expandable section label to show error details or logs. /// /// In en, this message translates to: /// **'Technical Details'** String get technicalDetails; - /// No description provided for @save. + /// Primary action to save changes. /// /// In en, this message translates to: /// **'Save'** String get save; - /// No description provided for @chooseModel. + /// Button/label to choose a model. /// /// In en, this message translates to: /// **'Choose Model'** String get chooseModel; - /// No description provided for @reviewerMode. + /// Developer/reviewer mode indicator. /// /// In en, this message translates to: /// **'REVIEWER MODE'** String get reviewerMode; - /// No description provided for @selectLanguage. + /// Dialog title to pick application language. /// /// In en, this message translates to: /// **'Select Language'** String get selectLanguage; - /// No description provided for @newFolder. + /// Action to create a new folder. /// /// In en, this message translates to: /// **'New Folder'** String get newFolder; - /// No description provided for @folderName. + /// Label for entering a folder's name. /// /// In en, this message translates to: /// **'Folder name'** String get folderName; - /// No description provided for @newChat. + /// Action to start a new chat. /// /// In en, this message translates to: /// **'New Chat'** String get newChat; - /// No description provided for @more. + /// Opens additional actions or content. /// /// In en, this message translates to: /// **'More'** String get more; - /// No description provided for @clear. + /// Action to clear input or selection. /// /// In en, this message translates to: /// **'Clear'** String get clear; - /// No description provided for @searchHint. + /// Generic search input hint. /// /// In en, this message translates to: /// **'Search...'** String get searchHint; - /// No description provided for @searchConversations. + /// Search input hint scoped to conversations. /// /// In en, this message translates to: /// **'Search conversations...'** String get searchConversations; - /// No description provided for @create. + /// Primary action to create a resource. /// /// In en, this message translates to: /// **'Create'** String get create; - /// No description provided for @folderCreated. + /// Toast/notice after successfully creating a folder. /// /// In en, this message translates to: /// **'Folder created'** String get folderCreated; - /// No description provided for @failedToCreateFolder. + /// Error notice when folder creation fails. /// /// In en, this message translates to: /// **'Failed to create folder'** String get failedToCreateFolder; - /// No description provided for @movedChatToFolder. + /// Toast indicating a chat titled {title} was moved to folder {folder}. /// /// In en, this message translates to: /// **'Moved \"{title}\" to \"{folder}\"'** String movedChatToFolder(String title, String folder); - /// No description provided for @failedToMoveChat. + /// Error notice when moving a chat fails. /// /// In en, this message translates to: /// **'Failed to move chat'** String get failedToMoveChat; - /// No description provided for @failedToLoadChats. + /// Error notice when fetching chat list fails. /// /// In en, this message translates to: /// **'Failed to load chats'** String get failedToLoadChats; - /// No description provided for @failedToUpdatePin. + /// Error notice when updating pin star/flag fails. /// /// In en, this message translates to: /// **'Failed to update pin'** String get failedToUpdatePin; - /// No description provided for @failedToDeleteChat. + /// Error notice when deleting a chat fails. /// /// In en, this message translates to: /// **'Failed to delete chat'** String get failedToDeleteChat; - /// No description provided for @manage. + /// Context action to manage an item. /// /// In en, this message translates to: /// **'Manage'** String get manage; - /// No description provided for @rename. + /// Context action to rename an item. /// /// In en, this message translates to: /// **'Rename'** String get rename; - /// No description provided for @delete. + /// Context action to delete an item. /// /// In en, this message translates to: /// **'Delete'** String get delete; - /// No description provided for @renameChat. + /// Dialog title to rename a chat. /// /// In en, this message translates to: /// **'Rename Chat'** String get renameChat; - /// No description provided for @enterChatName. + /// Input hint/label for new chat name. /// /// In en, this message translates to: /// **'Enter chat name'** String get enterChatName; - /// No description provided for @failedToRenameChat. + /// Error notice when renaming chat fails. /// /// In en, this message translates to: /// **'Failed to rename chat'** String get failedToRenameChat; - /// No description provided for @failedToUpdateArchive. + /// Error notice when archiving/unarchiving fails. /// /// In en, this message translates to: /// **'Failed to update archive'** String get failedToUpdateArchive; - /// No description provided for @unarchive. + /// Action to unarchive an item. /// /// In en, this message translates to: /// **'Unarchive'** String get unarchive; - /// No description provided for @archive. + /// Action to archive an item. /// /// In en, this message translates to: /// **'Archive'** String get archive; - /// No description provided for @pin. + /// Action to pin/star an item. /// /// In en, this message translates to: /// **'Pin'** String get pin; - /// No description provided for @unpin. + /// Action to remove pin from an item. /// /// In en, this message translates to: /// **'Unpin'** String get unpin; - /// No description provided for @recent. + /// List filter for recently used items. /// /// In en, this message translates to: /// **'Recent'** String get recent; - /// No description provided for @system. + /// Option indicating the device/system default. /// /// In en, this message translates to: /// **'System'** String get system; - /// No description provided for @english. + /// Language name: English. /// /// In en, this message translates to: /// **'English'** String get english; - /// No description provided for @deutsch. + /// Language name: German. /// /// In en, this message translates to: /// **'Deutsch'** String get deutsch; - /// No description provided for @francais. + /// Language name: French. /// /// In en, this message translates to: /// **'Français'** String get francais; - /// No description provided for @italiano. + /// Language name: Italian. /// /// In en, this message translates to: /// **'Italiano'** String get italiano; - /// No description provided for @deleteMessagesTitle. + /// Dialog title asking to confirm deletion of messages. /// /// In en, this message translates to: /// **'Delete Messages'** String get deleteMessagesTitle; - /// No description provided for @deleteMessagesMessage. + /// Confirmation prompt asking to delete a number of messages. /// /// In en, this message translates to: /// **'Delete {count} messages?'** String deleteMessagesMessage(int count); - /// No description provided for @routeNotFound. + /// Displayed when navigation fails to find a route name. /// /// In en, this message translates to: /// **'Route not found: {routeName}'** String routeNotFound(String routeName); - /// No description provided for @deleteChatTitle. + /// Dialog title asking to confirm deletion of a chat. /// /// In en, this message translates to: /// **'Delete Chat'** String get deleteChatTitle; - /// No description provided for @deleteChatMessage. + /// Warning that deleting a chat cannot be undone. /// /// In en, this message translates to: /// **'This chat will be permanently deleted.'** String get deleteChatMessage; - /// No description provided for @aboutApp. + /// Settings tile title to view app information. /// /// In en, this message translates to: /// **'About App'** String get aboutApp; - /// No description provided for @aboutAppSubtitle. + /// Subtitle/description for the About section. /// /// In en, this message translates to: /// **'Conduit information and links'** String get aboutAppSubtitle; - /// No description provided for @typeBelowToBegin. + /// Hint shown in empty chat input area. /// /// In en, this message translates to: /// **'Type below to begin'** String get typeBelowToBegin; - /// No description provided for @web. + /// Tab/section label for web features. /// /// In en, this message translates to: /// **'Web'** String get web; - /// No description provided for @imageGen. + /// Short label for image generation section/tab. /// /// In en, this message translates to: /// **'Image Gen'** String get imageGen; - /// No description provided for @pinned. + /// Filter/tab for pinned items. /// /// In en, this message translates to: /// **'Pinned'** String get pinned; - /// No description provided for @folders. + /// Tab listing chat folders. /// /// In en, this message translates to: /// **'Folders'** String get folders; - /// No description provided for @archived. + /// Filter/tab for archived chats. /// /// In en, this message translates to: /// **'Archived'** String get archived; - /// No description provided for @appLanguage. + /// Label for choosing the app's display language. /// /// In en, this message translates to: /// **'App Language'** String get appLanguage; - /// No description provided for @darkMode. + /// Label for toggling dark theme. /// /// In en, this message translates to: /// **'Dark Mode'** String get darkMode; - /// No description provided for @webSearch. + /// Feature toggle/section for web search. /// /// In en, this message translates to: /// **'Web Search'** String get webSearch; - /// No description provided for @webSearchDescription. + /// Explains that responses can include citations from the web. /// /// In en, this message translates to: /// **'Search the web and cite sources in replies.'** String get webSearchDescription; - /// No description provided for @imageGeneration. + /// Feature toggle/section for image generation. /// /// In en, this message translates to: /// **'Image Generation'** String get imageGeneration; - /// No description provided for @imageGenerationDescription. + /// Explains creating images via model prompts. /// /// In en, this message translates to: /// **'Create images from your prompts.'** String get imageGenerationDescription; - /// No description provided for @copy. + /// Action to copy text to clipboard. /// /// In en, this message translates to: /// **'Copy'** String get copy; - /// No description provided for @edit. + /// Action to edit an item/message. /// /// In en, this message translates to: /// **'Edit'** String get edit; - /// No description provided for @regenerate. + /// Action to request a new assistant response. /// /// In en, this message translates to: /// **'Regenerate'** String get regenerate; - /// No description provided for @noConversationsYet. + /// Empty state when the user has no chats. /// /// In en, this message translates to: /// **'No conversations yet'** String get noConversationsYet; - /// No description provided for @usernameOrEmailHint. + /// Hint text for username/email input. /// /// In en, this message translates to: /// **'Enter your username or email'** String get usernameOrEmailHint; - /// No description provided for @passwordHint. + /// Hint text for password input. /// /// In en, this message translates to: /// **'Enter your password'** String get passwordHint; - /// No description provided for @enterApiKey. + /// Hint text for API key input. /// /// In en, this message translates to: /// **'Enter your API key'** String get enterApiKey; - /// No description provided for @signingIn. + /// Status message shown while signing in. /// /// In en, this message translates to: /// **'Signing in...'** String get signingIn; - /// No description provided for @advancedSettings. + /// Section that contains additional/optional configuration. /// /// In en, this message translates to: /// **'Advanced Settings'** String get advancedSettings; - /// No description provided for @customHeaders. + /// Section title for adding custom HTTP headers. /// /// In en, this message translates to: /// **'Custom Headers'** String get customHeaders; - /// No description provided for @customHeadersDescription. + /// Helper text explaining use-cases for custom headers. /// /// In en, this message translates to: /// **'Add custom HTTP headers for authentication, API keys, or special server requirements.'** String get customHeadersDescription; - /// No description provided for @headerNameEmpty. + /// Validation message for empty header name. /// /// In en, this message translates to: /// **'Header name cannot be empty'** String get headerNameEmpty; - /// No description provided for @headerNameTooLong. + /// Validation message for header name length. /// /// In en, this message translates to: /// **'Header name too long (max 64 characters)'** String get headerNameTooLong; - /// No description provided for @headerNameInvalidChars. + /// Validation message for invalid characters in header name. /// /// In en, this message translates to: /// **'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~'** String get headerNameInvalidChars; - /// No description provided for @headerNameReserved. + /// Error when attempting to override a reserved HTTP header {key}. /// /// In en, this message translates to: /// **'Cannot override reserved header \"{key}\"'** String headerNameReserved(String key); - /// No description provided for @headerValueEmpty. + /// Validation message for empty header value. /// /// In en, this message translates to: /// **'Header value cannot be empty'** String get headerValueEmpty; - /// No description provided for @headerValueTooLong. + /// Validation message for header value length. /// /// In en, this message translates to: /// **'Header value too long (max 1024 characters)'** String get headerValueTooLong; - /// No description provided for @headerValueInvalidChars. + /// Validation message for invalid characters in header value. /// /// In en, this message translates to: /// **'Header value contains invalid characters. Use only printable ASCII.'** String get headerValueInvalidChars; - /// No description provided for @headerValueUnsafe. + /// Security warning for suspicious header values. /// /// In en, this message translates to: /// **'Header value appears to contain potentially unsafe content'** String get headerValueUnsafe; - /// No description provided for @headerAlreadyExists. + /// Error when a custom header with key {key} already exists. /// /// In en, this message translates to: /// **'Header \"{key}\" already exists. Remove it first to update.'** String headerAlreadyExists(String key); - /// No description provided for @maxHeadersReachedDetail. + /// Explains the upper limit of custom headers. /// /// In en, this message translates to: /// **'Maximum of 10 custom headers allowed. Remove some to add more.'** String get maxHeadersReachedDetail; - /// No description provided for @editMessage. + /// Action to edit a previously sent message. /// /// In en, this message translates to: /// **'Edit Message'** String get editMessage; - /// No description provided for @noModelsAvailable. + /// Shown when model list is empty or failed to load. /// /// In en, this message translates to: /// **'No models available'** String get noModelsAvailable; - /// No description provided for @followingSystem. + /// Indicates the app is following the system theme ("Dark"/"Light"). /// /// In en, this message translates to: /// **'Following system: {theme}'** String followingSystem(String theme); - /// No description provided for @themeDark. + /// Theme label for dark appearance. /// /// In en, this message translates to: /// **'Dark'** String get themeDark; - /// No description provided for @themeLight. + /// Theme label for light appearance. /// /// In en, this message translates to: /// **'Light'** String get themeLight; - /// No description provided for @currentlyUsingDarkTheme. + /// Status text indicating dark theme is active. /// /// In en, this message translates to: /// **'Currently using Dark theme'** String get currentlyUsingDarkTheme; - /// No description provided for @currentlyUsingLightTheme. + /// Status text indicating light theme is active. /// /// In en, this message translates to: /// **'Currently using Light theme'** String get currentlyUsingLightTheme; - /// No description provided for @aboutConduit. + /// Dialog title for app information. /// /// In en, this message translates to: /// **'About Conduit'** String get aboutConduit; - /// No description provided for @versionLabel. + /// Displays version and build number in the About dialog. /// /// In en, this message translates to: /// **'Version: {version} ({build})'** String versionLabel(String version, String build); - /// No description provided for @githubRepository. + /// Link label pointing to the app repository. /// /// In en, this message translates to: /// **'GitHub Repository'** String get githubRepository; - /// No description provided for @unableToLoadAppInfo. + /// Error text when package info cannot be retrieved. /// /// In en, this message translates to: /// **'Unable to load app info'** String get unableToLoadAppInfo; - /// No description provided for @thinking. + /// Label shown while the assistant is reasoning. /// /// In en, this message translates to: /// **'Thinking…'** String get thinking; - /// No description provided for @thoughts. + /// Section title for showing reasoning content. /// /// In en, this message translates to: /// **'Thoughts'** @@ -1473,86 +1476,87 @@ abstract class AppLocalizations { /// **'Thought for {duration}'** String thoughtForDuration(String duration); - /// No description provided for @appCustomization. + /// Title of the customization settings page. /// /// In en, this message translates to: /// **'App Customization'** String get appCustomization; - /// No description provided for @appCustomizationSubtitle. + /// Subtitle shown under App Customization tile and page header. /// /// In en, this message translates to: /// **'Personalize how names and UI display'** String get appCustomizationSubtitle; - /// No description provided for @display. + /// Section header for visual and layout related settings. /// /// In en, this message translates to: /// **'Display'** String get display; - /// No description provided for @realtime. + /// Section header for realtime/transport settings. /// /// In en, this message translates to: /// **'Realtime'** String get realtime; - /// No description provided for @hideProviderInModelNames. + /// Toggle label to hide the provider prefix in model names (e.g., show gpt-4o instead of openai/gpt-4o). /// /// In en, this message translates to: /// **'Hide provider in model names'** String get hideProviderInModelNames; - /// No description provided for @hideProviderInModelNamesDescription. + /// Helper text for provider hiding toggle. /// /// In en, this message translates to: /// **'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'** String get hideProviderInModelNamesDescription; - /// No description provided for @transportMode. + /// Title for selecting the networking transport used for realtime. /// /// In en, this message translates to: /// **'Transport mode'** String get transportMode; - /// No description provided for @transportModeDescription. + /// Helper text explaining the transport setting. /// /// In en, this message translates to: /// **'Choose how the app connects for realtime updates.'** String get transportModeDescription; - /// No description provided for @mode. + /// Form field label for transport mode dropdown. /// /// In en, this message translates to: /// **'Mode'** String get mode; - /// No description provided for @transportModeAuto. + /// Dropdown option label for automatic transport selection. /// /// In en, this message translates to: /// **'Auto (Polling + WebSocket)'** String get transportModeAuto; - /// No description provided for @transportModeWs. + /// Dropdown option label for WebSocket-only transport. /// /// In en, this message translates to: /// **'WebSocket only'** String get transportModeWs; - /// No description provided for @transportModeAutoInfo. + /// Footnote text for the Auto transport mode. /// /// In en, this message translates to: /// **'More robust on restrictive networks. Upgrades to WebSocket when possible.'** String get transportModeAutoInfo; - /// No description provided for @transportModeWsInfo. + /// Footnote text for the WebSocket-only transport mode. /// /// In en, this message translates to: /// **'Lower overhead, but may fail behind strict proxies/firewalls.'** String get transportModeWsInfo; } -class _AppLocalizationsDelegate extends LocalizationsDelegate { +class _AppLocalizationsDelegate + extends LocalizationsDelegate { const _AppLocalizationsDelegate(); @override @@ -1561,27 +1565,30 @@ class _AppLocalizationsDelegate extends LocalizationsDelegate } @override - bool isSupported(Locale locale) => ['de', 'en', 'fr', 'it'].contains(locale.languageCode); + bool isSupported(Locale locale) => + ['de', 'en', 'fr', 'it'].contains(locale.languageCode); @override bool shouldReload(_AppLocalizationsDelegate old) => false; } AppLocalizations lookupAppLocalizations(Locale locale) { - - // Lookup logic when only language code is specified. switch (locale.languageCode) { - case 'de': return AppLocalizationsDe(); - case 'en': return AppLocalizationsEn(); - case 'fr': return AppLocalizationsFr(); - case 'it': return AppLocalizationsIt(); + case 'de': + return AppLocalizationsDe(); + case 'en': + return AppLocalizationsEn(); + case 'fr': + return AppLocalizationsFr(); + case 'it': + return AppLocalizationsIt(); } throw FlutterError( 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' 'an issue with the localizations generation tool. Please file an issue ' 'on GitHub with a reproducible sample app and the gen-l10n configuration ' - 'that was used.' + 'that was used.', ); } diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart index 0b7b246..fcf04b6 100644 --- a/lib/l10n/app_localizations_de.dart +++ b/lib/l10n/app_localizations_de.dart @@ -30,7 +30,8 @@ class AppLocalizationsDe extends AppLocalizations { String get unableToLoadProfile => 'Profil konnte nicht geladen werden'; @override - String get pleaseCheckConnection => 'Bitte überprüfe deine Verbindung und versuche es erneut'; + String get pleaseCheckConnection => + 'Bitte überprüfe deine Verbindung und versuche es erneut'; @override String get account => 'Konto'; @@ -63,7 +64,8 @@ class AppLocalizationsDe extends AppLocalizations { String get searchModels => 'Modelle suchen...'; @override - String get errorMessage => 'Etwas ist schief gelaufen. Bitte versuche es erneut.'; + String get errorMessage => + 'Etwas ist schief gelaufen. Bitte versuche es erneut.'; @override String get loginButton => 'Anmelden'; @@ -116,7 +118,8 @@ class AppLocalizationsDe extends AppLocalizations { String get noFilesYet => 'Noch keine Dateien'; @override - String get uploadDocsPrompt => 'Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden'; + String get uploadDocsPrompt => + 'Lade Dokumente hoch, um sie in deinen Unterhaltungen mit Conduit zu verwenden'; @override String get uploadFirstFile => 'Erste Datei hochladen'; @@ -125,7 +128,8 @@ class AppLocalizationsDe extends AppLocalizations { String get knowledgeBaseEmpty => 'Wissensdatenbank ist leer'; @override - String get createCollectionsPrompt => 'Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz'; + String get createCollectionsPrompt => + 'Erstelle Sammlungen verwandter Dokumente zur einfachen Referenz'; @override String get chooseSourcePhoto => 'Quelle auswählen'; @@ -151,7 +155,8 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get kbCreationComingSoon => 'Erstellung der Wissensdatenbank kommt bald!'; + String get kbCreationComingSoon => + 'Erstellung der Wissensdatenbank kommt bald!'; @override String get backToServerSetup => 'Zur Servereinrichtung zurück'; @@ -163,7 +168,8 @@ class AppLocalizationsDe extends AppLocalizations { String get signIn => 'Anmelden'; @override - String get enterCredentials => 'Gib deine Anmeldedaten ein, um auf deine KI-Unterhaltungen zuzugreifen'; + String get enterCredentials => + 'Gib deine Anmeldedaten ein, um auf deine KI-Unterhaltungen zuzugreifen'; @override String get credentials => 'Zugangsdaten'; @@ -184,7 +190,8 @@ class AppLocalizationsDe extends AppLocalizations { String get connectToServer => 'Mit Server verbinden'; @override - String get enterServerAddress => 'Gib die Adresse deines Open-WebUI-Servers ein, um zu beginnen'; + String get enterServerAddress => + 'Gib die Adresse deines Open-WebUI-Servers ein, um zu beginnen'; @override String get serverUrl => 'Server-URL'; @@ -193,7 +200,8 @@ class AppLocalizationsDe extends AppLocalizations { String get serverUrlHint => 'https://dein-server.com'; @override - String get enterServerUrlSemantic => 'Gib deine Server-URL oder IP-Adresse ein'; + String get enterServerUrlSemantic => + 'Gib deine Server-URL oder IP-Adresse ein'; @override String get headerName => 'Header-Name'; @@ -223,7 +231,8 @@ class AppLocalizationsDe extends AppLocalizations { String get demoModeActive => 'Demo-Modus aktiv'; @override - String get skipServerSetupTryDemo => 'Servereinrichtung überspringen und Demo testen'; + String get skipServerSetupTryDemo => + 'Servereinrichtung überspringen und Demo testen'; @override String get enterDemo => 'Demo starten'; @@ -232,7 +241,8 @@ class AppLocalizationsDe extends AppLocalizations { String get demoBadge => 'Demo'; @override - String get serverNotOpenWebUI => 'Dies scheint kein Open-WebUI-Server zu sein.'; + String get serverNotOpenWebUI => + 'Dies scheint kein Open-WebUI-Server zu sein.'; @override String get serverUrlEmpty => 'Server-URL darf nicht leer sein'; @@ -241,10 +251,12 @@ class AppLocalizationsDe extends AppLocalizations { String get invalidUrlFormat => 'Ungültiges URL-Format. Bitte Eingabe prüfen.'; @override - String get onlyHttpHttps => 'Nur HTTP- und HTTPS-Protokolle werden unterstützt.'; + String get onlyHttpHttps => + 'Nur HTTP- und HTTPS-Protokolle werden unterstützt.'; @override - String get serverAddressRequired => 'Serveradresse erforderlich (z. B. 192.168.1.10 oder example.com).'; + String get serverAddressRequired => + 'Serveradresse erforderlich (z. B. 192.168.1.10 oder example.com).'; @override String get portRange => 'Port muss zwischen 1 und 65535 liegen.'; @@ -253,13 +265,16 @@ class AppLocalizationsDe extends AppLocalizations { String get invalidIpFormat => 'Ungültiges IP-Format. Beispiel: 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Verbindung fehlgeschlagen. Adresse prüfen und erneut versuchen.'; + String get couldNotConnectGeneric => + 'Verbindung fehlgeschlagen. Adresse prüfen und erneut versuchen.'; @override - String get weCouldntReachServer => 'Server nicht erreichbar. Verbindung und Serverstatus prüfen.'; + String get weCouldntReachServer => + 'Server nicht erreichbar. Verbindung und Serverstatus prüfen.'; @override - String get connectionTimedOut => 'Zeitüberschreitung. Server eventuell ausgelastet oder blockiert.'; + String get connectionTimedOut => + 'Zeitüberschreitung. Server eventuell ausgelastet oder blockiert.'; @override String get useHttpOrHttpsOnly => 'Nur http:// oder https:// verwenden.'; @@ -268,19 +283,23 @@ class AppLocalizationsDe extends AppLocalizations { String get loginFailed => 'Anmeldung fehlgeschlagen'; @override - String get invalidCredentials => 'Ungültiger Benutzername oder Passwort. Bitte erneut versuchen.'; + String get invalidCredentials => + 'Ungültiger Benutzername oder Passwort. Bitte erneut versuchen.'; @override - String get serverRedirectingHttps => 'Server leitet um. HTTPS-Konfiguration prüfen.'; + String get serverRedirectingHttps => + 'Server leitet um. HTTPS-Konfiguration prüfen.'; @override - String get unableToConnectServer => 'Verbindung zum Server nicht möglich. Bitte Verbindung prüfen.'; + String get unableToConnectServer => + 'Verbindung zum Server nicht möglich. Bitte Verbindung prüfen.'; @override String get requestTimedOut => 'Zeitüberschreitung. Bitte erneut versuchen.'; @override - String get genericSignInFailed => 'Anmeldung nicht möglich. Zugangsdaten und Server prüfen.'; + String get genericSignInFailed => + 'Anmeldung nicht möglich. Zugangsdaten und Server prüfen.'; @override String get skip => 'Überspringen'; @@ -295,7 +314,8 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardStartTitle => 'Unterhaltung starten'; @override - String get onboardStartSubtitle => 'Wähle ein Modell und tippe los. Tippe jederzeit auf Neuer Chat.'; + String get onboardStartSubtitle => + 'Wähle ein Modell und tippe los. Tippe jederzeit auf Neuer Chat.'; @override String get onboardStartBullet1 => 'Modellname oben antippen, um zu wechseln'; @@ -307,10 +327,12 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardAttachTitle => 'Kontext hinzufügen'; @override - String get onboardAttachSubtitle => 'Antworten mit Inhalten aus Arbeitsbereich oder Fotos untermauern.'; + String get onboardAttachSubtitle => + 'Antworten mit Inhalten aus Arbeitsbereich oder Fotos untermauern.'; @override - String get onboardAttachBullet1 => 'Arbeitsbereich: PDFs, Dokumente, Datensätze'; + String get onboardAttachBullet1 => + 'Arbeitsbereich: PDFs, Dokumente, Datensätze'; @override String get onboardAttachBullet2 => 'Fotos: Kamera oder Bibliothek'; @@ -325,19 +347,23 @@ class AppLocalizationsDe extends AppLocalizations { String get onboardSpeakBullet1 => 'Jederzeit stoppen; Text bleibt erhalten'; @override - String get onboardSpeakBullet2 => 'Ideal für kurze Notizen oder lange Prompts'; + String get onboardSpeakBullet2 => + 'Ideal für kurze Notizen oder lange Prompts'; @override String get onboardQuickTitle => 'Schnellaktionen'; @override - String get onboardQuickSubtitle => 'Menü öffnen, um zwischen Chats, Arbeitsbereich und Profil zu wechseln.'; + String get onboardQuickSubtitle => + 'Menü öffnen, um zwischen Chats, Arbeitsbereich und Profil zu wechseln.'; @override - String get onboardQuickBullet1 => 'Menü tippen für Chats, Arbeitsbereich, Profil'; + String get onboardQuickBullet1 => + 'Menü tippen für Chats, Arbeitsbereich, Profil'; @override - String get onboardQuickBullet2 => 'Neuer Chat starten oder Modelle oben verwalten'; + String get onboardQuickBullet2 => + 'Neuer Chat starten oder Modelle oben verwalten'; @override String get addAttachment => 'Anhang hinzufügen'; @@ -404,13 +430,16 @@ class AppLocalizationsDe extends AppLocalizations { String get emptyImageData => 'Leere Bilddaten'; @override - String get offlineBanner => 'Du bist offline. Einige Funktionen sind eingeschränkt.'; + String get offlineBanner => + 'Du bist offline. Einige Funktionen sind eingeschränkt.'; @override - String get featureRequiresInternet => 'Diese Funktion erfordert eine Internetverbindung'; + String get featureRequiresInternet => + 'Diese Funktion erfordert eine Internetverbindung'; @override - String get messagesWillSendWhenOnline => 'Nachrichten werden gesendet, sobald du wieder online bist'; + String get messagesWillSendWhenOnline => + 'Nachrichten werden gesendet, sobald du wieder online bist'; @override String get confirm => 'Bestätigen'; @@ -611,7 +640,8 @@ class AppLocalizationsDe extends AppLocalizations { String get imageGeneration => 'Bildgenerierung'; @override - String get imageGenerationDescription => 'Bilder aus deinen Prompts erstellen.'; + String get imageGenerationDescription => + 'Bilder aus deinen Prompts erstellen.'; @override String get copy => 'Kopieren'; @@ -626,7 +656,8 @@ class AppLocalizationsDe extends AppLocalizations { String get noConversationsYet => 'Noch keine Unterhaltungen'; @override - String get usernameOrEmailHint => 'Gib deinen Benutzernamen oder deine E‑Mail ein'; + String get usernameOrEmailHint => + 'Gib deinen Benutzernamen oder deine E‑Mail ein'; @override String get passwordHint => 'Gib dein Passwort ein'; @@ -644,7 +675,8 @@ class AppLocalizationsDe extends AppLocalizations { String get customHeaders => 'Benutzerdefinierte Header'; @override - String get customHeadersDescription => 'Füge benutzerdefinierte HTTP-Header für Authentifizierung, API-Schlüssel oder spezielle Serveranforderungen hinzu.'; + String get customHeadersDescription => + 'Füge benutzerdefinierte HTTP-Header für Authentifizierung, API-Schlüssel oder spezielle Serveranforderungen hinzu.'; @override String get headerNameEmpty => 'Header-Name darf nicht leer sein'; @@ -653,7 +685,8 @@ class AppLocalizationsDe extends AppLocalizations { String get headerNameTooLong => 'Header-Name zu lang (max. 64 Zeichen)'; @override - String get headerNameInvalidChars => 'Ungültiger Header-Name. Verwende nur Buchstaben, Zahlen und diese Zeichen: !#\$&-^_`|~'; + String get headerNameInvalidChars => + 'Ungültiger Header-Name. Verwende nur Buchstaben, Zahlen und diese Zeichen: !#\$&-^_`|~'; @override String headerNameReserved(String key) { @@ -667,10 +700,12 @@ class AppLocalizationsDe extends AppLocalizations { String get headerValueTooLong => 'Header-Wert zu lang (max. 1024 Zeichen)'; @override - String get headerValueInvalidChars => 'Header-Wert enthält ungültige Zeichen. Nur druckbare ASCII-Zeichen verwenden.'; + String get headerValueInvalidChars => + 'Header-Wert enthält ungültige Zeichen. Nur druckbare ASCII-Zeichen verwenden.'; @override - String get headerValueUnsafe => 'Header-Wert scheint potenziell unsicheren Inhalt zu enthalten'; + String get headerValueUnsafe => + 'Header-Wert scheint potenziell unsicheren Inhalt zu enthalten'; @override String headerAlreadyExists(String key) { @@ -678,7 +713,8 @@ class AppLocalizationsDe extends AppLocalizations { } @override - String get maxHeadersReachedDetail => 'Maximal 10 benutzerdefinierte Header zulässig. Einige entfernen, um mehr hinzuzufügen.'; + String get maxHeadersReachedDetail => + 'Maximal 10 benutzerdefinierte Header zulässig. Einige entfernen, um mehr hinzuzufügen.'; @override String get editMessage => 'Nachricht bearbeiten'; @@ -715,7 +751,8 @@ class AppLocalizationsDe extends AppLocalizations { String get githubRepository => 'GitHub-Repository'; @override - String get unableToLoadAppInfo => 'App-Informationen konnten nicht geladen werden'; + String get unableToLoadAppInfo => + 'App-Informationen konnten nicht geladen werden'; @override String get thinking => 'Denkt…'; @@ -732,7 +769,8 @@ class AppLocalizationsDe extends AppLocalizations { String get appCustomization => 'App-Anpassung'; @override - String get appCustomizationSubtitle => 'Personalisieren, wie Namen und UI angezeigt werden'; + String get appCustomizationSubtitle => + 'Personalisieren, wie Namen und UI angezeigt werden'; @override String get display => 'Anzeige'; @@ -744,13 +782,15 @@ class AppLocalizationsDe extends AppLocalizations { String get hideProviderInModelNames => 'Anbieter in Modellnamen ausblenden'; @override - String get hideProviderInModelNamesDescription => 'Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".'; + String get hideProviderInModelNamesDescription => + 'Zeige Namen wie \"gpt-4o\" statt \"openai/gpt-4o\".'; @override String get transportMode => 'Transportmodus'; @override - String get transportModeDescription => 'Wähle, wie die App für Echtzeit-Updates verbindet.'; + String get transportModeDescription => + 'Wähle, wie die App für Echtzeit-Updates verbindet.'; @override String get mode => 'Modus'; @@ -762,8 +802,10 @@ class AppLocalizationsDe extends AppLocalizations { String get transportModeWs => 'Nur WebSocket'; @override - String get transportModeAutoInfo => 'Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.'; + String get transportModeAutoInfo => + 'Robuster in restriktiven Netzwerken. Wechselt nach Möglichkeit zu WebSocket.'; @override - String get transportModeWsInfo => 'Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.'; + String get transportModeWsInfo => + 'Geringerer Overhead, kann jedoch hinter strikten Proxys/Firewalls fehlschlagen.'; } diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 96f24a6..c092f81 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -30,7 +30,8 @@ class AppLocalizationsEn extends AppLocalizations { String get unableToLoadProfile => 'Unable to load profile'; @override - String get pleaseCheckConnection => 'Please check your connection and try again'; + String get pleaseCheckConnection => + 'Please check your connection and try again'; @override String get account => 'Account'; @@ -116,7 +117,8 @@ class AppLocalizationsEn extends AppLocalizations { String get noFilesYet => 'No files yet'; @override - String get uploadDocsPrompt => 'Upload documents to reference in your conversations with Conduit'; + String get uploadDocsPrompt => + 'Upload documents to reference in your conversations with Conduit'; @override String get uploadFirstFile => 'Upload your first file'; @@ -125,7 +127,8 @@ class AppLocalizationsEn extends AppLocalizations { String get knowledgeBaseEmpty => 'Knowledge base is empty'; @override - String get createCollectionsPrompt => 'Create collections of related documents for easy reference'; + String get createCollectionsPrompt => + 'Create collections of related documents for easy reference'; @override String get chooseSourcePhoto => 'Choose your source'; @@ -163,7 +166,8 @@ class AppLocalizationsEn extends AppLocalizations { String get signIn => 'Sign In'; @override - String get enterCredentials => 'Enter your credentials to access your AI conversations'; + String get enterCredentials => + 'Enter your credentials to access your AI conversations'; @override String get credentials => 'Credentials'; @@ -184,7 +188,8 @@ class AppLocalizationsEn extends AppLocalizations { String get connectToServer => 'Connect to Server'; @override - String get enterServerAddress => 'Enter your Open-WebUI server address to get started'; + String get enterServerAddress => + 'Enter your Open-WebUI server address to get started'; @override String get serverUrl => 'Server URL'; @@ -232,7 +237,8 @@ class AppLocalizationsEn extends AppLocalizations { String get demoBadge => 'Demo'; @override - String get serverNotOpenWebUI => 'This does not appear to be an Open-WebUI server.'; + String get serverNotOpenWebUI => + 'This does not appear to be an Open-WebUI server.'; @override String get serverUrlEmpty => 'Server URL cannot be empty'; @@ -244,22 +250,27 @@ class AppLocalizationsEn extends AppLocalizations { String get onlyHttpHttps => 'Only HTTP and HTTPS protocols are supported.'; @override - String get serverAddressRequired => 'Server address is required (e.g., 192.168.1.10 or example.com).'; + String get serverAddressRequired => + 'Server address is required (e.g., 192.168.1.10 or example.com).'; @override String get portRange => 'Port must be between 1 and 65535.'; @override - String get invalidIpFormat => 'Invalid IP address format. Use format like 192.168.1.10.'; + String get invalidIpFormat => + 'Invalid IP address format. Use format like 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Couldn\'t connect. Double-check the address and try again.'; + String get couldNotConnectGeneric => + 'Couldn\'t connect. Double-check the address and try again.'; @override - String get weCouldntReachServer => 'We couldn\'t reach the server. Check your connection and that the server is running.'; + String get weCouldntReachServer => + 'We couldn\'t reach the server. Check your connection and that the server is running.'; @override - String get connectionTimedOut => 'Connection timed out. The server might be busy or blocked by a firewall.'; + String get connectionTimedOut => + 'Connection timed out. The server might be busy or blocked by a firewall.'; @override String get useHttpOrHttpsOnly => 'Use http:// or https:// only.'; @@ -268,19 +279,23 @@ class AppLocalizationsEn extends AppLocalizations { String get loginFailed => 'Login failed'; @override - String get invalidCredentials => 'Invalid username or password. Please try again.'; + String get invalidCredentials => + 'Invalid username or password. Please try again.'; @override - String get serverRedirectingHttps => 'The server is redirecting requests. Check your server\'s HTTPS configuration.'; + String get serverRedirectingHttps => + 'The server is redirecting requests. Check your server\'s HTTPS configuration.'; @override - String get unableToConnectServer => 'Unable to connect to server. Please check your connection.'; + String get unableToConnectServer => + 'Unable to connect to server. Please check your connection.'; @override String get requestTimedOut => 'The request timed out. Please try again.'; @override - String get genericSignInFailed => 'We couldn\'t sign you in. Check your credentials and server settings.'; + String get genericSignInFailed => + 'We couldn\'t sign you in. Check your credentials and server settings.'; @override String get skip => 'Skip'; @@ -295,10 +310,12 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardStartTitle => 'Start a conversation'; @override - String get onboardStartSubtitle => 'Choose a model, then type below to begin. Tap New Chat anytime.'; + String get onboardStartSubtitle => + 'Choose a model, then type below to begin. Tap New Chat anytime.'; @override - String get onboardStartBullet1 => 'Tap the model name in the top bar to switch models'; + String get onboardStartBullet1 => + 'Tap the model name in the top bar to switch models'; @override String get onboardStartBullet2 => 'Use New Chat to reset context'; @@ -307,7 +324,8 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardAttachTitle => 'Add context'; @override - String get onboardAttachSubtitle => 'Ground replies with content from Workspace or photos.'; + String get onboardAttachSubtitle => + 'Ground replies with content from Workspace or photos.'; @override String get onboardAttachBullet1 => 'Workspace: PDFs, docs, datasets'; @@ -319,7 +337,8 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardSpeakTitle => 'Speak naturally'; @override - String get onboardSpeakSubtitle => 'Tap the mic to dictate with live waveform feedback.'; + String get onboardSpeakSubtitle => + 'Tap the mic to dictate with live waveform feedback.'; @override String get onboardSpeakBullet1 => 'Stop anytime; partial text is preserved'; @@ -331,13 +350,16 @@ class AppLocalizationsEn extends AppLocalizations { String get onboardQuickTitle => 'Quick actions'; @override - String get onboardQuickSubtitle => 'Open the menu to switch between Chats, Workspace, and Profile.'; + String get onboardQuickSubtitle => + 'Open the menu to switch between Chats, Workspace, and Profile.'; @override - String get onboardQuickBullet1 => 'Tap the menu to access Chats, Workspace, Profile'; + String get onboardQuickBullet1 => + 'Tap the menu to access Chats, Workspace, Profile'; @override - String get onboardQuickBullet2 => 'Start New Chat or manage models from the top bar'; + String get onboardQuickBullet2 => + 'Start New Chat or manage models from the top bar'; @override String get addAttachment => 'Add attachment'; @@ -407,10 +429,12 @@ class AppLocalizationsEn extends AppLocalizations { String get offlineBanner => 'You\'re offline. Some features may be limited.'; @override - String get featureRequiresInternet => 'This feature requires an internet connection'; + String get featureRequiresInternet => + 'This feature requires an internet connection'; @override - String get messagesWillSendWhenOnline => 'Messages will be sent when you\'re back online'; + String get messagesWillSendWhenOnline => + 'Messages will be sent when you\'re back online'; @override String get confirm => 'Confirm'; @@ -605,7 +629,8 @@ class AppLocalizationsEn extends AppLocalizations { String get webSearch => 'Web Search'; @override - String get webSearchDescription => 'Search the web and cite sources in replies.'; + String get webSearchDescription => + 'Search the web and cite sources in replies.'; @override String get imageGeneration => 'Image Generation'; @@ -644,7 +669,8 @@ class AppLocalizationsEn extends AppLocalizations { String get customHeaders => 'Custom Headers'; @override - String get customHeadersDescription => 'Add custom HTTP headers for authentication, API keys, or special server requirements.'; + String get customHeadersDescription => + 'Add custom HTTP headers for authentication, API keys, or special server requirements.'; @override String get headerNameEmpty => 'Header name cannot be empty'; @@ -653,7 +679,8 @@ class AppLocalizationsEn extends AppLocalizations { String get headerNameTooLong => 'Header name too long (max 64 characters)'; @override - String get headerNameInvalidChars => 'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~'; + String get headerNameInvalidChars => + 'Invalid header name. Use only letters, numbers, and these symbols: !#\$&-^_`|~'; @override String headerNameReserved(String key) { @@ -664,13 +691,16 @@ class AppLocalizationsEn extends AppLocalizations { String get headerValueEmpty => 'Header value cannot be empty'; @override - String get headerValueTooLong => 'Header value too long (max 1024 characters)'; + String get headerValueTooLong => + 'Header value too long (max 1024 characters)'; @override - String get headerValueInvalidChars => 'Header value contains invalid characters. Use only printable ASCII.'; + String get headerValueInvalidChars => + 'Header value contains invalid characters. Use only printable ASCII.'; @override - String get headerValueUnsafe => 'Header value appears to contain potentially unsafe content'; + String get headerValueUnsafe => + 'Header value appears to contain potentially unsafe content'; @override String headerAlreadyExists(String key) { @@ -678,7 +708,8 @@ class AppLocalizationsEn extends AppLocalizations { } @override - String get maxHeadersReachedDetail => 'Maximum of 10 custom headers allowed. Remove some to add more.'; + String get maxHeadersReachedDetail => + 'Maximum of 10 custom headers allowed. Remove some to add more.'; @override String get editMessage => 'Edit Message'; @@ -744,13 +775,15 @@ class AppLocalizationsEn extends AppLocalizations { String get hideProviderInModelNames => 'Hide provider in model names'; @override - String get hideProviderInModelNamesDescription => 'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'; + String get hideProviderInModelNamesDescription => + 'Show names like \"gpt-4o\" instead of \"openai/gpt-4o\".'; @override String get transportMode => 'Transport mode'; @override - String get transportModeDescription => 'Choose how the app connects for realtime updates.'; + String get transportModeDescription => + 'Choose how the app connects for realtime updates.'; @override String get mode => 'Mode'; @@ -762,8 +795,10 @@ class AppLocalizationsEn extends AppLocalizations { String get transportModeWs => 'WebSocket only'; @override - String get transportModeAutoInfo => 'More robust on restrictive networks. Upgrades to WebSocket when possible.'; + String get transportModeAutoInfo => + 'More robust on restrictive networks. Upgrades to WebSocket when possible.'; @override - String get transportModeWsInfo => 'Lower overhead, but may fail behind strict proxies/firewalls.'; + String get transportModeWsInfo => + 'Lower overhead, but may fail behind strict proxies/firewalls.'; } diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart index 54acd2c..ee9dc13 100644 --- a/lib/l10n/app_localizations_fr.dart +++ b/lib/l10n/app_localizations_fr.dart @@ -30,7 +30,8 @@ class AppLocalizationsFr extends AppLocalizations { String get unableToLoadProfile => 'Impossible de charger le profil'; @override - String get pleaseCheckConnection => 'Veuillez vérifier votre connexion et réessayer'; + String get pleaseCheckConnection => + 'Veuillez vérifier votre connexion et réessayer'; @override String get account => 'Compte'; @@ -116,7 +117,8 @@ class AppLocalizationsFr extends AppLocalizations { String get noFilesYet => 'Pas encore de fichiers'; @override - String get uploadDocsPrompt => 'Importez des documents à utiliser dans vos conversations avec Conduit'; + String get uploadDocsPrompt => + 'Importez des documents à utiliser dans vos conversations avec Conduit'; @override String get uploadFirstFile => 'Importer votre premier fichier'; @@ -125,7 +127,8 @@ class AppLocalizationsFr extends AppLocalizations { String get knowledgeBaseEmpty => 'La base de connaissances est vide'; @override - String get createCollectionsPrompt => 'Créez des collections de documents liés pour une référence facile'; + String get createCollectionsPrompt => + 'Créez des collections de documents liés pour une référence facile'; @override String get chooseSourcePhoto => 'Choisir la source'; @@ -151,7 +154,8 @@ class AppLocalizationsFr extends AppLocalizations { } @override - String get kbCreationComingSoon => 'La création de la base de connaissances arrive bientôt !'; + String get kbCreationComingSoon => + 'La création de la base de connaissances arrive bientôt !'; @override String get backToServerSetup => 'Retour à la configuration du serveur'; @@ -163,7 +167,8 @@ class AppLocalizationsFr extends AppLocalizations { String get signIn => 'Se connecter'; @override - String get enterCredentials => 'Entrez vos identifiants pour accéder à vos conversations IA'; + String get enterCredentials => + 'Entrez vos identifiants pour accéder à vos conversations IA'; @override String get credentials => 'Identifiants'; @@ -184,7 +189,8 @@ class AppLocalizationsFr extends AppLocalizations { String get connectToServer => 'Se connecter au serveur'; @override - String get enterServerAddress => 'Saisissez l\'adresse de votre serveur Open-WebUI pour commencer'; + String get enterServerAddress => + 'Saisissez l\'adresse de votre serveur Open-WebUI pour commencer'; @override String get serverUrl => 'URL du serveur'; @@ -193,7 +199,8 @@ class AppLocalizationsFr extends AppLocalizations { String get serverUrlHint => 'https://votre-serveur.com'; @override - String get enterServerUrlSemantic => 'Saisissez l\'URL ou l\'adresse IP de votre serveur'; + String get enterServerUrlSemantic => + 'Saisissez l\'URL ou l\'adresse IP de votre serveur'; @override String get headerName => 'Nom de l\'en-tête'; @@ -223,7 +230,8 @@ class AppLocalizationsFr extends AppLocalizations { String get demoModeActive => 'Mode démo activé'; @override - String get skipServerSetupTryDemo => 'Ignorer la configuration et essayer la démo'; + String get skipServerSetupTryDemo => + 'Ignorer la configuration et essayer la démo'; @override String get enterDemo => 'Entrer en démo'; @@ -232,34 +240,42 @@ class AppLocalizationsFr extends AppLocalizations { String get demoBadge => 'Démo'; @override - String get serverNotOpenWebUI => 'Ceci ne semble pas être un serveur Open-WebUI.'; + String get serverNotOpenWebUI => + 'Ceci ne semble pas être un serveur Open-WebUI.'; @override String get serverUrlEmpty => 'L\'URL du serveur ne peut pas être vide'; @override - String get invalidUrlFormat => 'Format d\'URL invalide. Veuillez vérifier votre saisie.'; + String get invalidUrlFormat => + 'Format d\'URL invalide. Veuillez vérifier votre saisie.'; @override - String get onlyHttpHttps => 'Seuls les protocoles HTTP et HTTPS sont pris en charge.'; + String get onlyHttpHttps => + 'Seuls les protocoles HTTP et HTTPS sont pris en charge.'; @override - String get serverAddressRequired => 'Adresse du serveur requise (ex. 192.168.1.10 ou example.com).'; + String get serverAddressRequired => + 'Adresse du serveur requise (ex. 192.168.1.10 ou example.com).'; @override String get portRange => 'Le port doit être compris entre 1 et 65535.'; @override - String get invalidIpFormat => 'Format d\'IP invalide. Exemple : 192.168.1.10.'; + String get invalidIpFormat => + 'Format d\'IP invalide. Exemple : 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Connexion impossible. Vérifiez l\'adresse et réessayez.'; + String get couldNotConnectGeneric => + 'Connexion impossible. Vérifiez l\'adresse et réessayez.'; @override - String get weCouldntReachServer => 'Impossible d\'atteindre le serveur. Vérifiez la connexion et l\'état du serveur.'; + String get weCouldntReachServer => + 'Impossible d\'atteindre le serveur. Vérifiez la connexion et l\'état du serveur.'; @override - String get connectionTimedOut => 'Délai d\'attente dépassé. Le serveur est peut-être occupé ou bloqué.'; + String get connectionTimedOut => + 'Délai d\'attente dépassé. Le serveur est peut-être occupé ou bloqué.'; @override String get useHttpOrHttpsOnly => 'Utilisez uniquement http:// ou https://.'; @@ -268,19 +284,23 @@ class AppLocalizationsFr extends AppLocalizations { String get loginFailed => 'Échec de la connexion'; @override - String get invalidCredentials => 'Nom d\'utilisateur ou mot de passe invalide. Réessayez.'; + String get invalidCredentials => + 'Nom d\'utilisateur ou mot de passe invalide. Réessayez.'; @override - String get serverRedirectingHttps => 'Le serveur redirige les requêtes. Vérifiez la configuration HTTPS.'; + String get serverRedirectingHttps => + 'Le serveur redirige les requêtes. Vérifiez la configuration HTTPS.'; @override - String get unableToConnectServer => 'Impossible de se connecter au serveur. Vérifiez votre connexion.'; + String get unableToConnectServer => + 'Impossible de se connecter au serveur. Vérifiez votre connexion.'; @override String get requestTimedOut => 'Délai d\'attente dépassé. Réessayez.'; @override - String get genericSignInFailed => 'Connexion impossible. Vérifiez vos identifiants et le serveur.'; + String get genericSignInFailed => + 'Connexion impossible. Vérifiez vos identifiants et le serveur.'; @override String get skip => 'Ignorer'; @@ -295,22 +315,27 @@ class AppLocalizationsFr extends AppLocalizations { String get onboardStartTitle => 'Commencer une conversation'; @override - String get onboardStartSubtitle => 'Choisissez un modèle puis commencez à écrire. Touchez Nouveau chat à tout moment.'; + String get onboardStartSubtitle => + 'Choisissez un modèle puis commencez à écrire. Touchez Nouveau chat à tout moment.'; @override - String get onboardStartBullet1 => 'Touchez le nom du modèle en haut pour changer'; + String get onboardStartBullet1 => + 'Touchez le nom du modèle en haut pour changer'; @override - String get onboardStartBullet2 => 'Utilisez Nouveau chat pour réinitialiser le contexte'; + String get onboardStartBullet2 => + 'Utilisez Nouveau chat pour réinitialiser le contexte'; @override String get onboardAttachTitle => 'Ajouter du contexte'; @override - String get onboardAttachSubtitle => 'Ancrez les réponses avec l\'Espace de travail ou des photos.'; + String get onboardAttachSubtitle => + 'Ancrez les réponses avec l\'Espace de travail ou des photos.'; @override - String get onboardAttachBullet1 => 'Espace de travail : PDF, documents, jeux de données'; + String get onboardAttachBullet1 => + 'Espace de travail : PDF, documents, jeux de données'; @override String get onboardAttachBullet2 => 'Photos : appareil photo ou galerie'; @@ -319,25 +344,31 @@ class AppLocalizationsFr extends AppLocalizations { String get onboardSpeakTitle => 'Parlez naturellement'; @override - String get onboardSpeakSubtitle => 'Touchez le micro pour dicter avec retour visuel.'; + String get onboardSpeakSubtitle => + 'Touchez le micro pour dicter avec retour visuel.'; @override - String get onboardSpeakBullet1 => 'Arrêtez à tout moment ; le texte partiel est conservé'; + String get onboardSpeakBullet1 => + 'Arrêtez à tout moment ; le texte partiel est conservé'; @override - String get onboardSpeakBullet2 => 'Idéal pour des notes rapides ou de longs prompts'; + String get onboardSpeakBullet2 => + 'Idéal pour des notes rapides ou de longs prompts'; @override String get onboardQuickTitle => 'Actions rapides'; @override - String get onboardQuickSubtitle => 'Ouvrez le menu pour passer entre Chats, Espace de travail et Profil.'; + String get onboardQuickSubtitle => + 'Ouvrez le menu pour passer entre Chats, Espace de travail et Profil.'; @override - String get onboardQuickBullet1 => 'Touchez le menu pour accéder à Chats, Espace, Profil'; + String get onboardQuickBullet1 => + 'Touchez le menu pour accéder à Chats, Espace, Profil'; @override - String get onboardQuickBullet2 => 'Lancez Nouveau chat ou gérez les modèles depuis la barre'; + String get onboardQuickBullet2 => + 'Lancez Nouveau chat ou gérez les modèles depuis la barre'; @override String get addAttachment => 'Ajouter une pièce jointe'; @@ -404,13 +435,16 @@ class AppLocalizationsFr extends AppLocalizations { String get emptyImageData => 'Données d\'image vides'; @override - String get offlineBanner => 'Vous êtes hors ligne. Certaines fonctions peuvent être limitées.'; + String get offlineBanner => + 'Vous êtes hors ligne. Certaines fonctions peuvent être limitées.'; @override - String get featureRequiresInternet => 'Cette fonctionnalité nécessite une connexion Internet'; + String get featureRequiresInternet => + 'Cette fonctionnalité nécessite une connexion Internet'; @override - String get messagesWillSendWhenOnline => 'Les messages seront envoyés lorsque vous serez de nouveau en ligne'; + String get messagesWillSendWhenOnline => + 'Les messages seront envoyés lorsque vous serez de nouveau en ligne'; @override String get confirm => 'Confirmer'; @@ -605,13 +639,15 @@ class AppLocalizationsFr extends AppLocalizations { String get webSearch => 'Recherche Web'; @override - String get webSearchDescription => 'Recherchez sur le web et citez les sources.'; + String get webSearchDescription => + 'Recherchez sur le web et citez les sources.'; @override String get imageGeneration => 'Génération d\'images'; @override - String get imageGenerationDescription => 'Créez des images à partir de vos prompts.'; + String get imageGenerationDescription => + 'Créez des images à partir de vos prompts.'; @override String get copy => 'Copier'; @@ -644,16 +680,19 @@ class AppLocalizationsFr extends AppLocalizations { String get customHeaders => 'En-têtes personnalisés'; @override - String get customHeadersDescription => 'Ajoutez des en-têtes HTTP personnalisés pour l\'authentification, les clés API ou des exigences spécifiques du serveur.'; + String get customHeadersDescription => + 'Ajoutez des en-têtes HTTP personnalisés pour l\'authentification, les clés API ou des exigences spécifiques du serveur.'; @override String get headerNameEmpty => 'Le nom de l\'en-tête ne peut pas être vide'; @override - String get headerNameTooLong => 'Nom d\'en-tête trop long (max 64 caractères)'; + String get headerNameTooLong => + 'Nom d\'en-tête trop long (max 64 caractères)'; @override - String get headerNameInvalidChars => 'Nom d\'en-tête invalide. Utilisez uniquement des lettres, des chiffres et ces symboles : !#\$&-^_`|~'; + String get headerNameInvalidChars => + 'Nom d\'en-tête invalide. Utilisez uniquement des lettres, des chiffres et ces symboles : !#\$&-^_`|~'; @override String headerNameReserved(String key) { @@ -661,16 +700,20 @@ class AppLocalizationsFr extends AppLocalizations { } @override - String get headerValueEmpty => 'La valeur de l\'en-tête ne peut pas être vide'; + String get headerValueEmpty => + 'La valeur de l\'en-tête ne peut pas être vide'; @override - String get headerValueTooLong => 'Valeur d\'en-tête trop longue (max 1024 caractères)'; + String get headerValueTooLong => + 'Valeur d\'en-tête trop longue (max 1024 caractères)'; @override - String get headerValueInvalidChars => 'La valeur de l\'en-tête contient des caractères invalides. Utilisez uniquement des caractères ASCII imprimables.'; + String get headerValueInvalidChars => + 'La valeur de l\'en-tête contient des caractères invalides. Utilisez uniquement des caractères ASCII imprimables.'; @override - String get headerValueUnsafe => 'La valeur de l\'en-tête semble contenir du contenu potentiellement dangereux'; + String get headerValueUnsafe => + 'La valeur de l\'en-tête semble contenir du contenu potentiellement dangereux'; @override String headerAlreadyExists(String key) { @@ -678,7 +721,8 @@ class AppLocalizationsFr extends AppLocalizations { } @override - String get maxHeadersReachedDetail => 'Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter.'; + String get maxHeadersReachedDetail => + 'Maximum 10 en-têtes personnalisés. Supprimez-en pour en ajouter.'; @override String get editMessage => 'Modifier le message'; @@ -715,7 +759,8 @@ class AppLocalizationsFr extends AppLocalizations { String get githubRepository => 'Dépôt GitHub'; @override - String get unableToLoadAppInfo => 'Impossible de charger les informations de l\'application'; + String get unableToLoadAppInfo => + 'Impossible de charger les informations de l\'application'; @override String get thinking => 'Réflexion…'; @@ -732,7 +777,8 @@ class AppLocalizationsFr extends AppLocalizations { String get appCustomization => 'Personnalisation de l\'app'; @override - String get appCustomizationSubtitle => 'Personnalisez l\'affichage des noms et de l\'UI'; + String get appCustomizationSubtitle => + 'Personnalisez l\'affichage des noms et de l\'UI'; @override String get display => 'Affichage'; @@ -741,16 +787,19 @@ class AppLocalizationsFr extends AppLocalizations { String get realtime => 'Temps réel'; @override - String get hideProviderInModelNames => 'Masquer le fournisseur dans les noms de modèles'; + String get hideProviderInModelNames => + 'Masquer le fournisseur dans les noms de modèles'; @override - String get hideProviderInModelNamesDescription => 'Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".'; + String get hideProviderInModelNamesDescription => + 'Afficher des noms comme \"gpt-4o\" au lieu de \"openai/gpt-4o\".'; @override String get transportMode => 'Mode de transport'; @override - String get transportModeDescription => 'Choisissez comment l\'app se connecte pour les mises à jour en temps réel.'; + String get transportModeDescription => + 'Choisissez comment l\'app se connecte pour les mises à jour en temps réel.'; @override String get mode => 'Mode'; @@ -762,8 +811,10 @@ class AppLocalizationsFr extends AppLocalizations { String get transportModeWs => 'WebSocket uniquement'; @override - String get transportModeAutoInfo => 'Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.'; + String get transportModeAutoInfo => + 'Plus robuste sur les réseaux restrictifs. Passe à WebSocket lorsque possible.'; @override - String get transportModeWsInfo => 'Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.'; + String get transportModeWsInfo => + 'Moins de surcharge, mais peut échouer derrière des proxys/firewalls stricts.'; } diff --git a/lib/l10n/app_localizations_it.dart b/lib/l10n/app_localizations_it.dart index 734b8a4..ef7a782 100644 --- a/lib/l10n/app_localizations_it.dart +++ b/lib/l10n/app_localizations_it.dart @@ -116,7 +116,8 @@ class AppLocalizationsIt extends AppLocalizations { String get noFilesYet => 'Ancora nessun file'; @override - String get uploadDocsPrompt => 'Carica documenti da usare nelle conversazioni con Conduit'; + String get uploadDocsPrompt => + 'Carica documenti da usare nelle conversazioni con Conduit'; @override String get uploadFirstFile => 'Carica il tuo primo file'; @@ -125,7 +126,8 @@ class AppLocalizationsIt extends AppLocalizations { String get knowledgeBaseEmpty => 'La base di conoscenza è vuota'; @override - String get createCollectionsPrompt => 'Crea raccolte di documenti correlati per un rapido riferimento'; + String get createCollectionsPrompt => + 'Crea raccolte di documenti correlati per un rapido riferimento'; @override String get chooseSourcePhoto => 'Scegli origine'; @@ -151,7 +153,8 @@ class AppLocalizationsIt extends AppLocalizations { } @override - String get kbCreationComingSoon => 'La creazione della base di conoscenza arriverà presto!'; + String get kbCreationComingSoon => + 'La creazione della base di conoscenza arriverà presto!'; @override String get backToServerSetup => 'Torna alla configurazione del server'; @@ -163,7 +166,8 @@ class AppLocalizationsIt extends AppLocalizations { String get signIn => 'Accedi'; @override - String get enterCredentials => 'Inserisci le credenziali per accedere alle conversazioni IA'; + String get enterCredentials => + 'Inserisci le credenziali per accedere alle conversazioni IA'; @override String get credentials => 'Credenziali'; @@ -184,7 +188,8 @@ class AppLocalizationsIt extends AppLocalizations { String get connectToServer => 'Connetti al server'; @override - String get enterServerAddress => 'Inserisci l\'indirizzo del server Open-WebUI per iniziare'; + String get enterServerAddress => + 'Inserisci l\'indirizzo del server Open-WebUI per iniziare'; @override String get serverUrl => 'URL del server'; @@ -193,7 +198,8 @@ class AppLocalizationsIt extends AppLocalizations { String get serverUrlHint => 'https://tuo-server.com'; @override - String get enterServerUrlSemantic => 'Inserisci l\'URL o l\'indirizzo IP del server'; + String get enterServerUrlSemantic => + 'Inserisci l\'URL o l\'indirizzo IP del server'; @override String get headerName => 'Nome header'; @@ -223,7 +229,8 @@ class AppLocalizationsIt extends AppLocalizations { String get demoModeActive => 'Modalità demo attiva'; @override - String get skipServerSetupTryDemo => 'Salta configurazione server e prova la demo'; + String get skipServerSetupTryDemo => + 'Salta configurazione server e prova la demo'; @override String get enterDemo => 'Entra in demo'; @@ -244,7 +251,8 @@ class AppLocalizationsIt extends AppLocalizations { String get onlyHttpHttps => 'Sono supportati solo i protocolli HTTP e HTTPS.'; @override - String get serverAddressRequired => 'Indirizzo server richiesto (es. 192.168.1.10 o example.com).'; + String get serverAddressRequired => + 'Indirizzo server richiesto (es. 192.168.1.10 o example.com).'; @override String get portRange => 'La porta deve essere tra 1 e 65535.'; @@ -253,13 +261,16 @@ class AppLocalizationsIt extends AppLocalizations { String get invalidIpFormat => 'Formato IP non valido. Esempio: 192.168.1.10.'; @override - String get couldNotConnectGeneric => 'Impossibile connettersi. Verifica l\'indirizzo e riprova.'; + String get couldNotConnectGeneric => + 'Impossibile connettersi. Verifica l\'indirizzo e riprova.'; @override - String get weCouldntReachServer => 'Impossibile raggiungere il server. Verifica connessione e stato del server.'; + String get weCouldntReachServer => + 'Impossibile raggiungere il server. Verifica connessione e stato del server.'; @override - String get connectionTimedOut => 'Tempo scaduto. Il server potrebbe essere occupato o bloccato.'; + String get connectionTimedOut => + 'Tempo scaduto. Il server potrebbe essere occupato o bloccato.'; @override String get useHttpOrHttpsOnly => 'Usa solo http:// o https://.'; @@ -268,19 +279,23 @@ class AppLocalizationsIt extends AppLocalizations { String get loginFailed => 'Accesso non riuscito'; @override - String get invalidCredentials => 'Nome utente o password non validi. Riprova.'; + String get invalidCredentials => + 'Nome utente o password non validi. Riprova.'; @override - String get serverRedirectingHttps => 'Il server sta reindirizzando. Controlla la configurazione HTTPS.'; + String get serverRedirectingHttps => + 'Il server sta reindirizzando. Controlla la configurazione HTTPS.'; @override - String get unableToConnectServer => 'Impossibile connettersi al server. Controlla la connessione.'; + String get unableToConnectServer => + 'Impossibile connettersi al server. Controlla la connessione.'; @override String get requestTimedOut => 'Richiesta scaduta. Riprova.'; @override - String get genericSignInFailed => 'Impossibile accedere. Controlla credenziali e server.'; + String get genericSignInFailed => + 'Impossibile accedere. Controlla credenziali e server.'; @override String get skip => 'Salta'; @@ -295,10 +310,12 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardStartTitle => 'Inizia una conversazione'; @override - String get onboardStartSubtitle => 'Scegli un modello e inizia a scrivere. Tocca Nuova chat in qualsiasi momento.'; + String get onboardStartSubtitle => + 'Scegli un modello e inizia a scrivere. Tocca Nuova chat in qualsiasi momento.'; @override - String get onboardStartBullet1 => 'Tocca il nome del modello in alto per cambiare'; + String get onboardStartBullet1 => + 'Tocca il nome del modello in alto per cambiare'; @override String get onboardStartBullet2 => 'Usa Nuova chat per azzerare il contesto'; @@ -307,7 +324,8 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardAttachTitle => 'Aggiungi contesto'; @override - String get onboardAttachSubtitle => 'Collega le risposte a Workspace o alle foto.'; + String get onboardAttachSubtitle => + 'Collega le risposte a Workspace o alle foto.'; @override String get onboardAttachBullet1 => 'Workspace: PDF, documenti, dataset'; @@ -319,10 +337,12 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardSpeakTitle => 'Parla in modo naturale'; @override - String get onboardSpeakSubtitle => 'Tocca il microfono per dettare con feedback visivo.'; + String get onboardSpeakSubtitle => + 'Tocca il microfono per dettare con feedback visivo.'; @override - String get onboardSpeakBullet1 => 'Interrompi in qualsiasi momento; il testo parziale viene mantenuto'; + String get onboardSpeakBullet1 => + 'Interrompi in qualsiasi momento; il testo parziale viene mantenuto'; @override String get onboardSpeakBullet2 => 'Ottimo per note rapide o prompt lunghi'; @@ -331,13 +351,16 @@ class AppLocalizationsIt extends AppLocalizations { String get onboardQuickTitle => 'Azioni rapide'; @override - String get onboardQuickSubtitle => 'Apri il menu per passare tra Chat, Workspace e Profilo.'; + String get onboardQuickSubtitle => + 'Apri il menu per passare tra Chat, Workspace e Profilo.'; @override - String get onboardQuickBullet1 => 'Tocca il menu per accedere a Chat, Workspace, Profilo'; + String get onboardQuickBullet1 => + 'Tocca il menu per accedere a Chat, Workspace, Profilo'; @override - String get onboardQuickBullet2 => 'Avvia Nuova chat o gestisci i modelli dalla barra'; + String get onboardQuickBullet2 => + 'Avvia Nuova chat o gestisci i modelli dalla barra'; @override String get addAttachment => 'Aggiungi allegato'; @@ -404,13 +427,16 @@ class AppLocalizationsIt extends AppLocalizations { String get emptyImageData => 'Dati immagine vuoti'; @override - String get offlineBanner => 'Sei offline. Alcune funzioni potrebbero essere limitate.'; + String get offlineBanner => + 'Sei offline. Alcune funzioni potrebbero essere limitate.'; @override - String get featureRequiresInternet => 'Questa funzione richiede una connessione Internet'; + String get featureRequiresInternet => + 'Questa funzione richiede una connessione Internet'; @override - String get messagesWillSendWhenOnline => 'I messaggi verranno inviati quando tornerai online'; + String get messagesWillSendWhenOnline => + 'I messaggi verranno inviati quando tornerai online'; @override String get confirm => 'Conferma'; @@ -569,7 +595,8 @@ class AppLocalizationsIt extends AppLocalizations { String get deleteChatTitle => 'Elimina chat'; @override - String get deleteChatMessage => 'Questa chat verrà eliminata definitivamente.'; + String get deleteChatMessage => + 'Questa chat verrà eliminata definitivamente.'; @override String get aboutApp => 'Informazioni sull\'app'; @@ -644,7 +671,8 @@ class AppLocalizationsIt extends AppLocalizations { String get customHeaders => 'Header personalizzati'; @override - String get customHeadersDescription => 'Aggiungi header HTTP personalizzati per autenticazione, chiavi API o requisiti speciali del server.'; + String get customHeadersDescription => + 'Aggiungi header HTTP personalizzati per autenticazione, chiavi API o requisiti speciali del server.'; @override String get headerNameEmpty => 'Il nome header non può essere vuoto'; @@ -653,7 +681,8 @@ class AppLocalizationsIt extends AppLocalizations { String get headerNameTooLong => 'Nome header troppo lungo (max 64 caratteri)'; @override - String get headerNameInvalidChars => 'Nome header non valido. Usa solo lettere, numeri e questi simboli: !#\$&-^_`|~'; + String get headerNameInvalidChars => + 'Nome header non valido. Usa solo lettere, numeri e questi simboli: !#\$&-^_`|~'; @override String headerNameReserved(String key) { @@ -664,13 +693,16 @@ class AppLocalizationsIt extends AppLocalizations { String get headerValueEmpty => 'Il valore dell\'header non può essere vuoto'; @override - String get headerValueTooLong => 'Valore header troppo lungo (max 1024 caratteri)'; + String get headerValueTooLong => + 'Valore header troppo lungo (max 1024 caratteri)'; @override - String get headerValueInvalidChars => 'Il valore dell\'header contiene caratteri non validi. Usa solo ASCII stampabile.'; + String get headerValueInvalidChars => + 'Il valore dell\'header contiene caratteri non validi. Usa solo ASCII stampabile.'; @override - String get headerValueUnsafe => 'Il valore dell\'header sembra contenere contenuti potenzialmente non sicuri'; + String get headerValueUnsafe => + 'Il valore dell\'header sembra contenere contenuti potenzialmente non sicuri'; @override String headerAlreadyExists(String key) { @@ -678,7 +710,8 @@ class AppLocalizationsIt extends AppLocalizations { } @override - String get maxHeadersReachedDetail => 'Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri.'; + String get maxHeadersReachedDetail => + 'Massimo 10 header personalizzati consentiti. Rimuovine alcuni per aggiungerne altri.'; @override String get editMessage => 'Modifica messaggio'; @@ -715,7 +748,8 @@ class AppLocalizationsIt extends AppLocalizations { String get githubRepository => 'Repository GitHub'; @override - String get unableToLoadAppInfo => 'Impossibile caricare le informazioni dell\'app'; + String get unableToLoadAppInfo => + 'Impossibile caricare le informazioni dell\'app'; @override String get thinking => 'Sta pensando…'; @@ -732,7 +766,8 @@ class AppLocalizationsIt extends AppLocalizations { String get appCustomization => 'Personalizzazione app'; @override - String get appCustomizationSubtitle => 'Personalizza la visualizzazione dei nomi e dell\'UI'; + String get appCustomizationSubtitle => + 'Personalizza la visualizzazione dei nomi e dell\'UI'; @override String get display => 'Schermo'; @@ -741,16 +776,19 @@ class AppLocalizationsIt extends AppLocalizations { String get realtime => 'Tempo reale'; @override - String get hideProviderInModelNames => 'Nascondi provider nei nomi dei modelli'; + String get hideProviderInModelNames => + 'Nascondi provider nei nomi dei modelli'; @override - String get hideProviderInModelNamesDescription => 'Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".'; + String get hideProviderInModelNamesDescription => + 'Mostra nomi come \"gpt-4o\" invece di \"openai/gpt-4o\".'; @override String get transportMode => 'Modalità di trasporto'; @override - String get transportModeDescription => 'Scegli come l\'app si connette per gli aggiornamenti in tempo reale.'; + String get transportModeDescription => + 'Scegli come l\'app si connette per gli aggiornamenti in tempo reale.'; @override String get mode => 'Modalità'; @@ -762,8 +800,10 @@ class AppLocalizationsIt extends AppLocalizations { String get transportModeWs => 'Solo WebSocket'; @override - String get transportModeAutoInfo => 'Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.'; + String get transportModeAutoInfo => + 'Più robusto nelle reti restrittive. Passa a WebSocket quando possibile.'; @override - String get transportModeWsInfo => 'Minore overhead, ma può fallire dietro proxy/firewall restrittivi.'; + String get transportModeWsInfo => + 'Minore overhead, ma può fallire dietro proxy/firewall restrittivi.'; } diff --git a/pubspec.yaml b/pubspec.yaml index d030e52..e8e2cb7 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -75,21 +75,8 @@ dependency_overrides: flutter: uses-material-design: true generate: true - # Localization configuration - # The default synthetic package is used for generated localizations: - # import 'package:flutter_gen/gen_l10n/app_localizations.dart'; - # ARB files live in lib/l10n - # You can customize output via the `l10n:` section if needed. - # - # l10n: - # arb-dir: lib/l10n - # template-arb-file: app_en.arb - # output-localization-file: app_localizations.dart - # preferred-supported-locales: - # - en - # - de - # - fr - # - it + # Localization + # ARB files live in lib/l10n (configured via l10n.yaml) assets: - assets/icons/ diff --git a/tool/validate_arb_locales.dart b/tool/validate_arb_locales.dart new file mode 100644 index 0000000..6391c73 --- /dev/null +++ b/tool/validate_arb_locales.dart @@ -0,0 +1,139 @@ +import 'dart:convert'; +import 'dart:io'; + +/// Validates ARB locale files against the English template (app_en.arb). +/// - Ensures no duplicate keys within any ARB file. +/// - Ensures each non-meta key in EN exists in other locales. +/// - Ensures placeholder names match between EN and other locales. +/// - Reports unused keys (best-effort) by scanning lib/ for usages of +/// AppLocalizations.of(context)!.. Unused keys are WARNINGS by default. +/// +/// Exit codes: +/// 0 = success (no hard errors; warnings may be printed) +/// 1 = validation errors (duplicates, missing keys, placeholder mismatches) +Future main(List args) async { + final basePath = 'lib/l10n/app_en.arb'; + final dir = Directory('lib/l10n'); + if (!await File(basePath).exists()) { + stderr.writeln('Base ARB not found: $basePath'); + exit(1); + } + + final arbFiles = await dir + .list() + .where((e) => e.path.endsWith('.arb')) + .map((e) => File(e.path)) + .toList(); + + final baseFile = File(basePath); + final base = _readJson(baseFile); + final baseKeys = _nonMetaKeys(base); + final basePlaceholders = _placeholdersMap(base); + + final errors = []; + final warnings = []; + + // NOTE: Duplicate keys at the top-level are invalid JSON and unlikely. + // We skip duplicate detection to avoid false positives from nested meta keys. + + // Validate translations against base + for (final f in arbFiles) { + if (f.path.endsWith('_en.arb')) continue; + final data = _readJson(f); + final keys = _nonMetaKeys(data); + + // Missing keys + final missing = baseKeys.difference(keys); + if (missing.isNotEmpty) { + errors.add('[${f.path}] Missing keys: ${missing.toList()..sort()}'); + } + + // Placeholder parity checks + final transPlaceholders = _placeholdersMap(data); + for (final k in basePlaceholders.keys) { + final basePh = basePlaceholders[k] ?? const {}; + final trPh = transPlaceholders[k]; + if (trPh == null) { + // If string exists but no meta placeholders, warn only. + if (keys.contains(k) && basePh.isNotEmpty) { + warnings.add('[${f.path}] Key "$k" missing @meta placeholders; base has ${basePh.toList()..sort()}'); + } + continue; + } + if (basePh.length != trPh.length || !basePh.containsAll(trPh)) { + warnings.add('[${f.path}] Placeholder mismatch for "$k": expected ${basePh.toList()..sort()}, got ${trPh.toList()..sort()}'); + } + } + } + + // Unused keys (best-effort) — WARNINGS only + final usedKeys = await _scanUsedLocalizationKeys(); + final unused = baseKeys.difference(usedKeys); + if (unused.isNotEmpty) { + warnings.add('Unused keys in EN (best-effort): ${unused.toList()..sort()}'); + } + + // Print results + if (errors.isNotEmpty) { + stderr.writeln('ARB validation errors:'); + for (final e in errors) { + stderr.writeln(' - $e'); + } + } + if (warnings.isNotEmpty) { + stdout.writeln('ARB validation warnings:'); + for (final w in warnings) { + stdout.writeln(' - $w'); + } + } + + exit(errors.isEmpty ? 0 : 1); +} + +Map _readJson(File f) { + final content = f.readAsStringSync(); + return json.decode(content) as Map; +} + +Set _nonMetaKeys(Map m) { + return m.keys + .where((k) => !k.startsWith('@') && k != '@@locale') + .toSet(); +} + +Map> _placeholdersMap(Map m) { + final map = >{}; + for (final entry in m.entries) { + final key = entry.key; + if (!key.startsWith('@')) continue; + final value = entry.value; + if (value is! Map) continue; + final placeholders = value['placeholders']; + if (placeholders is Map) { + map[key.substring(1)] = placeholders.keys.toSet(); + } + } + return map; +} + +// Duplicate detection intentionally omitted (see note above). + +Future> _scanUsedLocalizationKeys() async { + final libDir = Directory('lib'); + final used = {}; + final dartFiles = await libDir + .list(recursive: true) + .where((e) => e.path.endsWith('.dart')) + .map((e) => File(e.path)) + .toList(); + + final regex = RegExp(r'AppLocalizations\.of\([^)]*\)!\.([a-zA-Z0-9_]+)'); + for (final f in dartFiles) { + final text = await f.readAsString(); + for (final m in regex.allMatches(text)) { + final key = m.group(1); + if (key != null) used.add(key); + } + } + return used; +} diff --git a/tool/verify_arb_descriptions.dart b/tool/verify_arb_descriptions.dart new file mode 100644 index 0000000..83ac6f6 --- /dev/null +++ b/tool/verify_arb_descriptions.dart @@ -0,0 +1,63 @@ +import 'dart:convert'; +import 'dart:io'; + +/// Verifies that every non-meta key in app_en.arb has a corresponding +/// @key entry with a non-empty `description`. +/// +/// Usage: dart run tool/verify_arb_descriptions.dart +Future main() async { + final arbPath = 'lib/l10n/app_en.arb'; + final file = File(arbPath); + if (!await file.exists()) { + stderr.writeln('ARB file not found: $arbPath'); + exit(2); + } + + final content = await file.readAsString(); + late final Map data; + try { + data = json.decode(content) as Map; + } catch (e) { + stderr.writeln('Failed to parse $arbPath as JSON: $e'); + exit(2); + } + + final missingMeta = []; + final missingDescription = []; + + for (final entry in data.entries) { + final key = entry.key; + if (key.startsWith('@') || key == '@@locale') continue; // meta + + final metaKey = '@$key'; + final meta = data[metaKey]; + if (meta == null || meta is! Map) { + missingMeta.add(key); + continue; + } + final desc = meta['description']; + if (desc is! String || desc.trim().isEmpty) { + missingDescription.add(key); + } + } + + if (missingMeta.isEmpty && missingDescription.isEmpty) { + stdout.writeln('ARB descriptions check passed: all keys have @meta.description.'); + return; + } + + if (missingMeta.isNotEmpty) { + stderr.writeln('Missing @meta for keys (${missingMeta.length}):'); + for (final k in missingMeta) { + stderr.writeln(' - $k'); + } + } + if (missingDescription.isNotEmpty) { + stderr.writeln('Missing description in @meta for keys (${missingDescription.length}):'); + for (final k in missingDescription) { + stderr.writeln(' - $k'); + } + } + exit(1); +} + diff --git a/weblate.yaml b/weblate.yaml new file mode 100644 index 0000000..bd326dd --- /dev/null +++ b/weblate.yaml @@ -0,0 +1,19 @@ +projects: + - name: Conduit + slug: conduit + components: + - name: Mobile App + slug: app + # Weblate will use the repository connected via the UI. + # Configure file discovery for Flutter ARB localization files. + filemask: lib/l10n/app_*.arb + template: lib/l10n/app_en.arb + new_base: lib/l10n/app_{language}.arb + base_language: en + file_format: arb + monolingual: true + # Prefer PRs from Weblate instead of direct pushes + pull_request: true + # Optional: auto-add new languages when enabled in Weblate + add_new_languages: true +