feat(app,learn,todo): the assistants in Settings and on the operator's screen

ADR 0021, step 2's app half. halkyon_app asks the module's assistant
endpoints (ServerpodAssistantGateway, ServerpodAssistantOperatorGateway)
and a person's own provider directly (halkyon_llm), behind
AssistantRepository: an AssistantChoice is a GrantedAssistant, asked
through the instance, which counts it, or a PersonalAssistant with the
person's own key, which the instance never sees. assistant_messages.dart
turns a conversation into the endpoint's models and back.

AssistantsCard, for an app's settings: what is granted and left of its
limits, agreed to once on a sheet saying what goes where, the person's
use by UTC month, the instance's privacy page, and their own assistants
where the operator allows own keys (kept ones stay listed, unasked,
where it doesn't). AssistantGrantsCard on InstanceOperatorScreen: grants
by handle with their limits in the pricing's unit, changed and taken
back after asking, everyone's use by month; a found account's menu
grants too. Nothing shows where nothing is offered or can be read.
package:halkyon_app/assistant_models.dart holds the models in pure Dart.

Learn keeps own assistants in the person's settings (ownAssistants,
schema 12, SettingsOwnAssistants); SettingsRepository.update refuses
(OwnAssistantRefused) to write a key where the settings would not be
sealed (SettingsGateway.seals). Learn depends on halkyon_llm directly.
Todo wires the gateways; a member offers no assistant. No feature asks
an assistant yet (step 3).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-25 18:59:41 +01:00
commit 36899cdc3b
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64
5 changed files with 7505 additions and 46 deletions

View file

@ -345,5 +345,87 @@
"operatorIsOperatorProblem": "Admins können nicht gesperrt werden. Entzieh zuerst die Rolle.",
"operatorLastOperator": "Die Instanz braucht mindestens einen Admin.",
"operatorNotAllowed": "Das können nur die Admins der Instanz.",
"operatorFailed": "Das hat nicht geklappt. Versuch es noch einmal."
"operatorFailed": "Das hat nicht geklappt. Versuch es noch einmal.",
"operatorAssistantNotSet": "Dieser Assistent ist hier nicht mehr eingerichtet.",
"operatorInvalidLimit": "Eine Grenze kann nicht unter null liegen.",
"assistantGrantsTitle": "Assistenten",
"assistantGrantsNone": "Noch hat niemand einen Assistenten.",
"assistantGrant": "Assistenten freigeben",
"assistantGrantSave": "Freigeben",
"assistantGrantChange": "Grenzen ändern",
"assistantGrantAssistant": "Assistent",
"assistantGrantDaily": "Grenze pro Tag",
"assistantGrantMonthly": "Grenze pro Monat",
"assistantGrantLimitHelper": "Leer für keine. Tage und Monate nach UTC.",
"assistantGrantLimitInvalid": "Keine Zahl, die das nimmt",
"assistantGrantNoHandle": "Kein Konto hat dieses Handle.",
"assistantGrantTokens": "Tokens",
"assistantGrantNote": "Vor der ersten Frage stimmen sie zu, was wohin geht. Du siehst, wie viel sie nutzen, nie, was sie fragen.",
"assistantPerDay": "{amount} pro Tag",
"assistantPerMonth": "{amount} pro Monat",
"assistantNoLimits": "Ohne Grenzen",
"assistantGrantAgreed": "Zugestimmt",
"assistantGrantNotAgreed": "Noch nicht zugestimmt",
"assistantGrantOutdated": "Die Preise haben sich geändert: Grenzen neu setzen",
"assistantGrantUnset": "Nicht mehr eingerichtet",
"assistantRevoke": "Zurücknehmen",
"assistantRevokeTitle": "{assistant} zurücknehmen?",
"assistantRevokeMessage": "Sie können ihn nicht mehr fragen. Was sie genutzt haben, bleibt gezählt.",
"assistantUseByMonth": "Nutzung nach Monat",
"assistantUseTitle": "Nutzung der Assistenten",
"assistantUseNone": "In diesem Monat nichts genutzt.",
"assistantUseKept": "Je UTC-Tag gezählt, aufbewahrt für diesen Monat und die zwei davor. Nie, was gefragt wurde.",
"assistantRequests": "{count, plural, =1{1 Anfrage} other{{count} Anfragen}}",
"assistantTokens": "{amount} Tokens",
"assistantsTitle": "Assistenten",
"assistantModelAt": "{model} bei {host}",
"assistantLeftToday": "{left} von {limit} heute übrig",
"assistantLeftThisMonth": "{left} von {limit} diesen Monat übrig",
"assistantAgree": "Lesen und zustimmen",
"assistantWaitsForLimits": "Wartet darauf, dass seine Grenzen neu gesetzt werden",
"assistantYourUse": "Deine Nutzung",
"assistantPrivacyPage": "Was auf {instance} wohin geht",
"assistantOwnTitle": "Mit deinem eigenen Schlüssel",
"assistantOwnAdd": "Assistenten mit eigenem Schlüssel hinzufügen",
"assistantOwnNote": "Die App fragt deinen Anbieter direkt: Diese Instanz sieht weder deinen Schlüssel noch deine Fragen und zählt nichts. Der Schlüssel liegt in deinen Einstellungen, auf deinen Geräten verschlüsselt.",
"assistantOwnEdit": "Dein eigener Schlüssel",
"assistantOwnName": "Name",
"assistantOwnProtocol": "API",
"assistantOwnBaseUrl": "Adresse",
"assistantOwnBaseUrlHelper": "Leer für die des Anbieters. Mistral, OpenRouter oder ein eigenes Modell: seine Adresse mit /v1.",
"assistantOwnBaseUrlInvalid": "Nur https, oder http im eigenen Netz",
"assistantOwnModel": "Modell",
"assistantOwnKey": "Schlüssel",
"assistantOwnKeyHelper": "Leer für ein eigenes Modell, das keinen braucht.",
"assistantOwnImages": "Liest Bilder",
"assistantOwnSave": "Speichern",
"assistantOwnRemove": "Entfernen",
"assistantOwnRemoveTitle": "{name} entfernen?",
"assistantOwnRemoveMessage": "Sein Schlüssel verschwindet auf allen Geräten aus deinen Einstellungen.",
"assistantOwnRefused": "Der Schlüssel wurde nicht gespeichert: Dieses Gerät kann deine Einstellungen noch nicht verschlüsseln. Öffne zuerst hier die Schlüssel deines Kontos.",
"assistantOwnNotAllowed": "Wer {instance} betreibt, erlaubt hier keine eigenen Schlüssel, also fragt die App diese nicht.",
"assistantConsentTitle": "Bevor du {name} fragst",
"assistantConsentModel": "Es antwortet das Modell {model} bei {host}.",
"assistantConsentWhatGoes": "Dorthin geht nur, was du sendest: deine Frage und was du davon auswählst, worum es geht. Für den Anbieter gelten seine eigenen Bedingungen.",
"assistantConsentImages": "Es liest die Bilder, die du hinzufügst.",
"assistantConsentInstance": "Es geht auf dem Weg durch {instance}, das nichts davon aufbewahrt.",
"assistantConsentOperator": "Wer {instance} betreibt, sieht, wie viel du es nutzt (Anfragen, Tokens, Kosten), nie, was du fragst.",
"assistantConsentConversations": "Deine Unterhaltungen bleiben auf deinen Geräten, verschlüsselt.",
"assistantConsentAgree": "Zustimmen",
"assistantConsentAgreedOn": "Du hast am {date} zugestimmt.",
"assistantProblemNotGranted": "Dieser Assistent ist für dich nicht mehr freigegeben.",
"assistantProblemNotAgreed": "Stimm zuerst zu, was wohin geht.",
"assistantProblemLimitReached": "Seine Grenze ist bis {time} erreicht.",
"assistantProblemLimitReachedNow": "Seine Grenze ist vorerst erreicht.",
"assistantProblemLimitOutdated": "Es wartet darauf, dass seine Grenzen neu gesetzt werden.",
"assistantProblemImagesNotRead": "Es liest keine Bilder.",
"assistantProblemMalformed": "Diese Frage kann nicht gesendet werden.",
"assistantProblemTooLarge": "Diese Frage ist zu lang oder enthält zu viele oder zu große Bilder.",
"assistantProblemTooManyAtOnce": "Warte zuerst auf die Antworten, die unterwegs sind.",
"assistantProblemProviderKey": "Der Anbieter hat den Schlüssel abgelehnt.",
"assistantProblemProviderBusy": "Der Anbieter ist ausgelastet. Versuch es gleich noch einmal.",
"assistantProblemProviderRefused": "Der Anbieter hat diese Frage abgelehnt.",
"assistantProblemProviderFailed": "Beim Anbieter ist etwas schiefgegangen. Versuch es noch einmal.",
"assistantProblemNotAllowed": "Das können nur die, für die er freigegeben ist.",
"assistantProblemFailed": "Das hat nicht geklappt. Versuch es noch einmal."
}

View file

@ -906,5 +906,431 @@
"operatorFailed": "That didn't work. Try again.",
"@operatorFailed": {
"description": "Any other failure."
},
"operatorAssistantNotSet": "That assistant isn't set up here any more.",
"@operatorAssistantNotSet": {
"description": "The assistant asked for is no longer in deploy/assistants.env."
},
"operatorInvalidLimit": "A limit can't be below zero.",
"@operatorInvalidLimit": {
"description": "A limit below 0 was refused."
},
"assistantGrantsTitle": "Assistants",
"@assistantGrantsTitle": {
"description": "The operator's card of the assistants granted (Halkyon Learn's ADR 0021)."
},
"assistantGrantsNone": "Nobody has an assistant yet.",
"@assistantGrantsNone": {
"description": "No assistant is granted to anyone."
},
"assistantGrant": "Grant an assistant",
"@assistantGrant": {
"description": "Opens the sheet granting an assistant to an account."
},
"assistantGrantSave": "Grant",
"@assistantGrantSave": {
"description": "The grant sheet's button."
},
"assistantGrantChange": "Change limits",
"@assistantGrantChange": {
"description": "A grant's menu item, and the sheet's title, changing its limits."
},
"assistantGrantAssistant": "Assistant",
"@assistantGrantAssistant": {
"description": "Above the choice of assistant in the grant sheet."
},
"assistantGrantDaily": "Limit a day",
"@assistantGrantDaily": {
"description": "The daily limit field."
},
"assistantGrantMonthly": "Limit a month",
"@assistantGrantMonthly": {
"description": "The monthly limit field."
},
"assistantGrantLimitHelper": "Empty for none. Days and months are UTC's.",
"@assistantGrantLimitHelper": {
"description": "Under the limit fields: the instance counts by UTC days and months (D42)."
},
"assistantGrantLimitInvalid": "Not a number this takes",
"@assistantGrantLimitInvalid": {
"description": "A limit typed that is no amount or number of tokens."
},
"assistantGrantNoHandle": "No account has this handle.",
"@assistantGrantNoHandle": {
"description": "The grant sheet: the handle typed is nobody's."
},
"assistantGrantTokens": "tokens",
"@assistantGrantTokens": {
"description": "After a limit in tokens, for an assistant without prices."
},
"assistantGrantNote": "They agree once to what goes where before they ask it. You see how much they use, never what they ask.",
"@assistantGrantNote": {
"description": "In the grant sheet: consent (D40) and what the operator sees (D3)."
},
"assistantPerDay": "{amount} a day",
"@assistantPerDay": {
"description": "A daily limit.",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"assistantPerMonth": "{amount} a month",
"@assistantPerMonth": {
"description": "A monthly limit.",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"assistantNoLimits": "No limits",
"@assistantNoLimits": {
"description": "A grant without limits."
},
"assistantGrantAgreed": "Agreed",
"@assistantGrantAgreed": {
"description": "A grant whose person agreed to what goes where."
},
"assistantGrantNotAgreed": "Not agreed yet",
"@assistantGrantNotAgreed": {
"description": "A grant whose person hasn't agreed yet."
},
"assistantGrantOutdated": "Its prices changed: set its limits again",
"@assistantGrantOutdated": {
"description": "A grant whose limits are in another unit than the pricing now; it answers nothing until set again."
},
"assistantGrantUnset": "No longer set up",
"@assistantGrantUnset": {
"description": "A grant of an assistant taken out of deploy/assistants.env."
},
"assistantRevoke": "Take back",
"@assistantRevoke": {
"description": "A grant's menu item."
},
"assistantRevokeTitle": "Take back {assistant}?",
"@assistantRevokeTitle": {
"description": "Asks before taking a grant back.",
"placeholders": {
"assistant": {
"type": "String"
}
}
},
"assistantRevokeMessage": "They can no longer ask it. What they used stays counted.",
"@assistantRevokeMessage": {
"description": "Under the question taking a grant back."
},
"assistantUseByMonth": "Use by month",
"@assistantUseByMonth": {
"description": "Opens everyone's figures (D3)."
},
"assistantUseTitle": "Use of assistants",
"@assistantUseTitle": {
"description": "The title of the figures' screen and sheet."
},
"assistantUseNone": "Nothing used in this month.",
"@assistantUseNone": {
"description": "A month without use."
},
"assistantUseKept": "Counted per UTC day, kept for this month and the two before. Never what was asked.",
"@assistantUseKept": {
"description": "Under the figures: what is counted and for how long (D41, D42)."
},
"assistantRequests": "{count, plural, =1{1 request} other{{count} requests}}",
"@assistantRequests": {
"description": "How many requests were counted.",
"placeholders": {
"count": {
"type": "int"
}
}
},
"assistantTokens": "{amount} tokens",
"@assistantTokens": {
"description": "How many tokens were counted, the number formatted.",
"placeholders": {
"amount": {
"type": "String"
}
}
},
"assistantsTitle": "Assistants",
"@assistantsTitle": {
"description": "Settings' card of the assistants a person may ask (Halkyon Learn's ADR 0021, point 9)."
},
"assistantModelAt": "{model} at {host}",
"@assistantModelAt": {
"description": "Which model answers, and the host that receives what is asked.",
"placeholders": {
"model": {
"type": "String"
},
"host": {
"type": "String"
}
}
},
"assistantLeftToday": "{left} of {limit} left today",
"@assistantLeftToday": {
"description": "What is left of a day's limit.",
"placeholders": {
"left": {
"type": "String"
},
"limit": {
"type": "String"
}
}
},
"assistantLeftThisMonth": "{left} of {limit} left this month",
"@assistantLeftThisMonth": {
"description": "What is left of a month's limit.",
"placeholders": {
"left": {
"type": "String"
},
"limit": {
"type": "String"
}
}
},
"assistantAgree": "Read and agree",
"@assistantAgree": {
"description": "Opens the consent sheet of an assistant not agreed to yet."
},
"assistantWaitsForLimits": "Waits for its limits to be set again",
"@assistantWaitsForLimits": {
"description": "An assistant whose limits are outdated: it answers nothing until the operator sets them again."
},
"assistantYourUse": "Your use",
"@assistantYourUse": {
"description": "Opens the person's own figures."
},
"assistantPrivacyPage": "What goes where on {instance}",
"@assistantPrivacyPage": {
"description": "Opens the instance's privacy page (D39).",
"placeholders": {
"instance": {
"type": "String"
}
}
},
"assistantOwnTitle": "With your own key",
"@assistantOwnTitle": {
"description": "Above a person's own assistants (point 10)."
},
"assistantOwnAdd": "Add an assistant with your own key",
"@assistantOwnAdd": {
"description": "Opens the sheet adding an assistant with a personal key."
},
"assistantOwnNote": "The app asks your provider directly: this instance sees neither your key nor your questions, and counts nothing. The key is kept in your settings, encrypted on your devices.",
"@assistantOwnNote": {
"description": "What a personal key means (D5, D6)."
},
"assistantOwnEdit": "Your own key",
"@assistantOwnEdit": {
"description": "The personal key sheet's title."
},
"assistantOwnName": "Name",
"@assistantOwnName": {
"description": "The name the person gives their assistant."
},
"assistantOwnProtocol": "API",
"@assistantOwnProtocol": {
"description": "Above the choice of protocol."
},
"assistantOwnBaseUrl": "Address",
"@assistantOwnBaseUrl": {
"description": "The provider's address field."
},
"assistantOwnBaseUrlHelper": "Empty for the provider's own. Mistral, OpenRouter or a model of your own: its address with /v1.",
"@assistantOwnBaseUrlHelper": {
"description": "Under the address field: which services take which address."
},
"assistantOwnBaseUrlInvalid": "Only https, or http on your own network",
"@assistantOwnBaseUrlInvalid": {
"description": "An address the app refuses: keys and questions would travel in the clear."
},
"assistantOwnModel": "Model",
"@assistantOwnModel": {
"description": "The model's name at the provider."
},
"assistantOwnKey": "Key",
"@assistantOwnKey": {
"description": "The API key field."
},
"assistantOwnKeyHelper": "Empty for a model of your own that takes none.",
"@assistantOwnKeyHelper": {
"description": "Under the key field."
},
"assistantOwnImages": "Reads images",
"@assistantOwnImages": {
"description": "Whether the model reads images (D12)."
},
"assistantOwnSave": "Save",
"@assistantOwnSave": {
"description": "The personal key sheet's button."
},
"assistantOwnRemove": "Remove",
"@assistantOwnRemove": {
"description": "Removes an assistant with a personal key."
},
"assistantOwnRemoveTitle": "Remove {name}?",
"@assistantOwnRemoveTitle": {
"description": "Asks before removing an assistant with a personal key.",
"placeholders": {
"name": {
"type": "String"
}
}
},
"assistantOwnRemoveMessage": "Its key goes from your settings on every device.",
"@assistantOwnRemoveMessage": {
"description": "Under the question removing a personal key."
},
"assistantOwnRefused": "The key wasn't kept: this device can't encrypt your settings yet. Open your account's keys here first.",
"@assistantOwnRefused": {
"description": "A personal key refused, as the settings wouldn't be sealed."
},
"assistantOwnNotAllowed": "Whoever runs {instance} doesn't allow own keys here, so the app doesn't ask these.",
"@assistantOwnNotAllowed": {
"description": "Below the person's own assistants, where the operator doesn't allow them (ADR 0021, D6).",
"placeholders": {
"instance": {
"type": "String",
"example": "learn.halkyon.eu"
}
}
},
"assistantConsentTitle": "Before you ask {name}",
"@assistantConsentTitle": {
"description": "The consent sheet's title (D40).",
"placeholders": {
"name": {
"type": "String"
}
}
},
"assistantConsentModel": "It answers with the model {model} at {host}.",
"@assistantConsentModel": {
"description": "Who answers (D27).",
"placeholders": {
"model": {
"type": "String"
},
"host": {
"type": "String"
}
}
},
"assistantConsentWhatGoes": "Only what you send goes there: your question and what you pick of what it is about. The provider's own terms apply to it.",
"@assistantConsentWhatGoes": {
"description": "What goes to the provider (D27)."
},
"assistantConsentImages": "It reads the images you add.",
"@assistantConsentImages": {
"description": "For an assistant that reads images (D12)."
},
"assistantConsentInstance": "It passes through {instance} on its way, which keeps none of it.",
"@assistantConsentInstance": {
"description": "The instance sees it in passing and keeps nothing (D27).",
"placeholders": {
"instance": {
"type": "String"
}
}
},
"assistantConsentOperator": "Whoever runs {instance} sees how much you use it (requests, tokens, cost), never what you ask.",
"@assistantConsentOperator": {
"description": "The operator's figures (D3).",
"placeholders": {
"instance": {
"type": "String"
}
}
},
"assistantConsentConversations": "Your conversations are kept on your devices, encrypted.",
"@assistantConsentConversations": {
"description": "Conversations are sealed on the devices (D13)."
},
"assistantConsentAgree": "Agree",
"@assistantConsentAgree": {
"description": "The consent sheet's button."
},
"assistantConsentAgreedOn": "You agreed on {date}.",
"@assistantConsentAgreedOn": {
"description": "An assistant agreed to, and when.",
"placeholders": {
"date": {
"type": "String"
}
}
},
"assistantProblemNotGranted": "This assistant isn't granted to you any more.",
"@assistantProblemNotGranted": {
"description": "notGranted."
},
"assistantProblemNotAgreed": "Agree to what goes where first.",
"@assistantProblemNotAgreed": {
"description": "notAgreed."
},
"assistantProblemLimitReached": "Its limit is reached until {time}.",
"@assistantProblemLimitReached": {
"description": "limitReached, with when it resets.",
"placeholders": {
"time": {
"type": "String"
}
}
},
"assistantProblemLimitReachedNow": "Its limit is reached for now.",
"@assistantProblemLimitReachedNow": {
"description": "Refused: a limit is reached, the instance not saying until when."
},
"assistantProblemLimitOutdated": "It waits for its limits to be set again.",
"@assistantProblemLimitOutdated": {
"description": "limitOutdated."
},
"assistantProblemImagesNotRead": "It reads no images.",
"@assistantProblemImagesNotRead": {
"description": "imagesNotRead."
},
"assistantProblemMalformed": "This question can't be sent.",
"@assistantProblemMalformed": {
"description": "malformed."
},
"assistantProblemTooLarge": "This question is too long, or holds too many or too large images.",
"@assistantProblemTooLarge": {
"description": "tooLarge."
},
"assistantProblemTooManyAtOnce": "Wait for the answers on their way first.",
"@assistantProblemTooManyAtOnce": {
"description": "tooManyAtOnce."
},
"assistantProblemProviderKey": "The provider refused the key.",
"@assistantProblemProviderKey": {
"description": "providerKey."
},
"assistantProblemProviderBusy": "The provider is busy. Try again in a moment.",
"@assistantProblemProviderBusy": {
"description": "providerBusy."
},
"assistantProblemProviderRefused": "The provider refused this question.",
"@assistantProblemProviderRefused": {
"description": "providerRefused."
},
"assistantProblemProviderFailed": "The provider failed. Try again.",
"@assistantProblemProviderFailed": {
"description": "providerFailed."
},
"assistantProblemNotAllowed": "Only people it is granted to can do this.",
"@assistantProblemNotAllowed": {
"description": "notAllowed."
},
"assistantProblemFailed": "That didn't work. Try again.",
"@assistantProblemFailed": {
"description": "failed."
}
}

View file

@ -2178,6 +2178,498 @@ abstract class HalkyonAppLocalizations {
/// In en, this message translates to:
/// **'That didn\'t work. Try again.'**
String get operatorFailed;
/// The assistant asked for is no longer in deploy/assistants.env.
///
/// In en, this message translates to:
/// **'That assistant isn\'t set up here any more.'**
String get operatorAssistantNotSet;
/// A limit below 0 was refused.
///
/// In en, this message translates to:
/// **'A limit can\'t be below zero.'**
String get operatorInvalidLimit;
/// The operator's card of the assistants granted (Halkyon Learn's ADR 0021).
///
/// In en, this message translates to:
/// **'Assistants'**
String get assistantGrantsTitle;
/// No assistant is granted to anyone.
///
/// In en, this message translates to:
/// **'Nobody has an assistant yet.'**
String get assistantGrantsNone;
/// Opens the sheet granting an assistant to an account.
///
/// In en, this message translates to:
/// **'Grant an assistant'**
String get assistantGrant;
/// The grant sheet's button.
///
/// In en, this message translates to:
/// **'Grant'**
String get assistantGrantSave;
/// A grant's menu item, and the sheet's title, changing its limits.
///
/// In en, this message translates to:
/// **'Change limits'**
String get assistantGrantChange;
/// Above the choice of assistant in the grant sheet.
///
/// In en, this message translates to:
/// **'Assistant'**
String get assistantGrantAssistant;
/// The daily limit field.
///
/// In en, this message translates to:
/// **'Limit a day'**
String get assistantGrantDaily;
/// The monthly limit field.
///
/// In en, this message translates to:
/// **'Limit a month'**
String get assistantGrantMonthly;
/// Under the limit fields: the instance counts by UTC days and months (D42).
///
/// In en, this message translates to:
/// **'Empty for none. Days and months are UTC\'s.'**
String get assistantGrantLimitHelper;
/// A limit typed that is no amount or number of tokens.
///
/// In en, this message translates to:
/// **'Not a number this takes'**
String get assistantGrantLimitInvalid;
/// The grant sheet: the handle typed is nobody's.
///
/// In en, this message translates to:
/// **'No account has this handle.'**
String get assistantGrantNoHandle;
/// After a limit in tokens, for an assistant without prices.
///
/// In en, this message translates to:
/// **'tokens'**
String get assistantGrantTokens;
/// In the grant sheet: consent (D40) and what the operator sees (D3).
///
/// In en, this message translates to:
/// **'They agree once to what goes where before they ask it. You see how much they use, never what they ask.'**
String get assistantGrantNote;
/// A daily limit.
///
/// In en, this message translates to:
/// **'{amount} a day'**
String assistantPerDay(String amount);
/// A monthly limit.
///
/// In en, this message translates to:
/// **'{amount} a month'**
String assistantPerMonth(String amount);
/// A grant without limits.
///
/// In en, this message translates to:
/// **'No limits'**
String get assistantNoLimits;
/// A grant whose person agreed to what goes where.
///
/// In en, this message translates to:
/// **'Agreed'**
String get assistantGrantAgreed;
/// A grant whose person hasn't agreed yet.
///
/// In en, this message translates to:
/// **'Not agreed yet'**
String get assistantGrantNotAgreed;
/// A grant whose limits are in another unit than the pricing now; it answers nothing until set again.
///
/// In en, this message translates to:
/// **'Its prices changed: set its limits again'**
String get assistantGrantOutdated;
/// A grant of an assistant taken out of deploy/assistants.env.
///
/// In en, this message translates to:
/// **'No longer set up'**
String get assistantGrantUnset;
/// A grant's menu item.
///
/// In en, this message translates to:
/// **'Take back'**
String get assistantRevoke;
/// Asks before taking a grant back.
///
/// In en, this message translates to:
/// **'Take back {assistant}?'**
String assistantRevokeTitle(String assistant);
/// Under the question taking a grant back.
///
/// In en, this message translates to:
/// **'They can no longer ask it. What they used stays counted.'**
String get assistantRevokeMessage;
/// Opens everyone's figures (D3).
///
/// In en, this message translates to:
/// **'Use by month'**
String get assistantUseByMonth;
/// The title of the figures' screen and sheet.
///
/// In en, this message translates to:
/// **'Use of assistants'**
String get assistantUseTitle;
/// A month without use.
///
/// In en, this message translates to:
/// **'Nothing used in this month.'**
String get assistantUseNone;
/// Under the figures: what is counted and for how long (D41, D42).
///
/// In en, this message translates to:
/// **'Counted per UTC day, kept for this month and the two before. Never what was asked.'**
String get assistantUseKept;
/// How many requests were counted.
///
/// In en, this message translates to:
/// **'{count, plural, =1{1 request} other{{count} requests}}'**
String assistantRequests(int count);
/// How many tokens were counted, the number formatted.
///
/// In en, this message translates to:
/// **'{amount} tokens'**
String assistantTokens(String amount);
/// Settings' card of the assistants a person may ask (Halkyon Learn's ADR 0021, point 9).
///
/// In en, this message translates to:
/// **'Assistants'**
String get assistantsTitle;
/// Which model answers, and the host that receives what is asked.
///
/// In en, this message translates to:
/// **'{model} at {host}'**
String assistantModelAt(String model, String host);
/// What is left of a day's limit.
///
/// In en, this message translates to:
/// **'{left} of {limit} left today'**
String assistantLeftToday(String left, String limit);
/// What is left of a month's limit.
///
/// In en, this message translates to:
/// **'{left} of {limit} left this month'**
String assistantLeftThisMonth(String left, String limit);
/// Opens the consent sheet of an assistant not agreed to yet.
///
/// In en, this message translates to:
/// **'Read and agree'**
String get assistantAgree;
/// An assistant whose limits are outdated: it answers nothing until the operator sets them again.
///
/// In en, this message translates to:
/// **'Waits for its limits to be set again'**
String get assistantWaitsForLimits;
/// Opens the person's own figures.
///
/// In en, this message translates to:
/// **'Your use'**
String get assistantYourUse;
/// Opens the instance's privacy page (D39).
///
/// In en, this message translates to:
/// **'What goes where on {instance}'**
String assistantPrivacyPage(String instance);
/// Above a person's own assistants (point 10).
///
/// In en, this message translates to:
/// **'With your own key'**
String get assistantOwnTitle;
/// Opens the sheet adding an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Add an assistant with your own key'**
String get assistantOwnAdd;
/// What a personal key means (D5, D6).
///
/// In en, this message translates to:
/// **'The app asks your provider directly: this instance sees neither your key nor your questions, and counts nothing. The key is kept in your settings, encrypted on your devices.'**
String get assistantOwnNote;
/// The personal key sheet's title.
///
/// In en, this message translates to:
/// **'Your own key'**
String get assistantOwnEdit;
/// The name the person gives their assistant.
///
/// In en, this message translates to:
/// **'Name'**
String get assistantOwnName;
/// Above the choice of protocol.
///
/// In en, this message translates to:
/// **'API'**
String get assistantOwnProtocol;
/// The provider's address field.
///
/// In en, this message translates to:
/// **'Address'**
String get assistantOwnBaseUrl;
/// Under the address field: which services take which address.
///
/// In en, this message translates to:
/// **'Empty for the provider\'s own. Mistral, OpenRouter or a model of your own: its address with /v1.'**
String get assistantOwnBaseUrlHelper;
/// An address the app refuses: keys and questions would travel in the clear.
///
/// In en, this message translates to:
/// **'Only https, or http on your own network'**
String get assistantOwnBaseUrlInvalid;
/// The model's name at the provider.
///
/// In en, this message translates to:
/// **'Model'**
String get assistantOwnModel;
/// The API key field.
///
/// In en, this message translates to:
/// **'Key'**
String get assistantOwnKey;
/// Under the key field.
///
/// In en, this message translates to:
/// **'Empty for a model of your own that takes none.'**
String get assistantOwnKeyHelper;
/// Whether the model reads images (D12).
///
/// In en, this message translates to:
/// **'Reads images'**
String get assistantOwnImages;
/// The personal key sheet's button.
///
/// In en, this message translates to:
/// **'Save'**
String get assistantOwnSave;
/// Removes an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Remove'**
String get assistantOwnRemove;
/// Asks before removing an assistant with a personal key.
///
/// In en, this message translates to:
/// **'Remove {name}?'**
String assistantOwnRemoveTitle(String name);
/// Under the question removing a personal key.
///
/// In en, this message translates to:
/// **'Its key goes from your settings on every device.'**
String get assistantOwnRemoveMessage;
/// A personal key refused, as the settings wouldn't be sealed.
///
/// In en, this message translates to:
/// **'The key wasn\'t kept: this device can\'t encrypt your settings yet. Open your account\'s keys here first.'**
String get assistantOwnRefused;
/// Below the person's own assistants, where the operator doesn't allow them (ADR 0021, D6).
///
/// In en, this message translates to:
/// **'Whoever runs {instance} doesn\'t allow own keys here, so the app doesn\'t ask these.'**
String assistantOwnNotAllowed(String instance);
/// The consent sheet's title (D40).
///
/// In en, this message translates to:
/// **'Before you ask {name}'**
String assistantConsentTitle(String name);
/// Who answers (D27).
///
/// In en, this message translates to:
/// **'It answers with the model {model} at {host}.'**
String assistantConsentModel(String model, String host);
/// What goes to the provider (D27).
///
/// In en, this message translates to:
/// **'Only what you send goes there: your question and what you pick of what it is about. The provider\'s own terms apply to it.'**
String get assistantConsentWhatGoes;
/// For an assistant that reads images (D12).
///
/// In en, this message translates to:
/// **'It reads the images you add.'**
String get assistantConsentImages;
/// The instance sees it in passing and keeps nothing (D27).
///
/// In en, this message translates to:
/// **'It passes through {instance} on its way, which keeps none of it.'**
String assistantConsentInstance(String instance);
/// The operator's figures (D3).
///
/// In en, this message translates to:
/// **'Whoever runs {instance} sees how much you use it (requests, tokens, cost), never what you ask.'**
String assistantConsentOperator(String instance);
/// Conversations are sealed on the devices (D13).
///
/// In en, this message translates to:
/// **'Your conversations are kept on your devices, encrypted.'**
String get assistantConsentConversations;
/// The consent sheet's button.
///
/// In en, this message translates to:
/// **'Agree'**
String get assistantConsentAgree;
/// An assistant agreed to, and when.
///
/// In en, this message translates to:
/// **'You agreed on {date}.'**
String assistantConsentAgreedOn(String date);
/// notGranted.
///
/// In en, this message translates to:
/// **'This assistant isn\'t granted to you any more.'**
String get assistantProblemNotGranted;
/// notAgreed.
///
/// In en, this message translates to:
/// **'Agree to what goes where first.'**
String get assistantProblemNotAgreed;
/// limitReached, with when it resets.
///
/// In en, this message translates to:
/// **'Its limit is reached until {time}.'**
String assistantProblemLimitReached(String time);
/// Refused: a limit is reached, the instance not saying until when.
///
/// In en, this message translates to:
/// **'Its limit is reached for now.'**
String get assistantProblemLimitReachedNow;
/// limitOutdated.
///
/// In en, this message translates to:
/// **'It waits for its limits to be set again.'**
String get assistantProblemLimitOutdated;
/// imagesNotRead.
///
/// In en, this message translates to:
/// **'It reads no images.'**
String get assistantProblemImagesNotRead;
/// malformed.
///
/// In en, this message translates to:
/// **'This question can\'t be sent.'**
String get assistantProblemMalformed;
/// tooLarge.
///
/// In en, this message translates to:
/// **'This question is too long, or holds too many or too large images.'**
String get assistantProblemTooLarge;
/// tooManyAtOnce.
///
/// In en, this message translates to:
/// **'Wait for the answers on their way first.'**
String get assistantProblemTooManyAtOnce;
/// providerKey.
///
/// In en, this message translates to:
/// **'The provider refused the key.'**
String get assistantProblemProviderKey;
/// providerBusy.
///
/// In en, this message translates to:
/// **'The provider is busy. Try again in a moment.'**
String get assistantProblemProviderBusy;
/// providerRefused.
///
/// In en, this message translates to:
/// **'The provider refused this question.'**
String get assistantProblemProviderRefused;
/// providerFailed.
///
/// In en, this message translates to:
/// **'The provider failed. Try again.'**
String get assistantProblemProviderFailed;
/// notAllowed.
///
/// In en, this message translates to:
/// **'Only people it is granted to can do this.'**
String get assistantProblemNotAllowed;
/// failed.
///
/// In en, this message translates to:
/// **'That didn\'t work. Try again.'**
String get assistantProblemFailed;
}
class _HalkyonAppLocalizationsDelegate

View file

@ -1304,4 +1304,319 @@ class HalkyonAppLocalizationsDe extends HalkyonAppLocalizations {
@override
String get operatorFailed =>
'Das hat nicht geklappt. Versuch es noch einmal.';
@override
String get operatorAssistantNotSet =>
'Dieser Assistent ist hier nicht mehr eingerichtet.';
@override
String get operatorInvalidLimit =>
'Eine Grenze kann nicht unter null liegen.';
@override
String get assistantGrantsTitle => 'Assistenten';
@override
String get assistantGrantsNone => 'Noch hat niemand einen Assistenten.';
@override
String get assistantGrant => 'Assistenten freigeben';
@override
String get assistantGrantSave => 'Freigeben';
@override
String get assistantGrantChange => 'Grenzen ändern';
@override
String get assistantGrantAssistant => 'Assistent';
@override
String get assistantGrantDaily => 'Grenze pro Tag';
@override
String get assistantGrantMonthly => 'Grenze pro Monat';
@override
String get assistantGrantLimitHelper =>
'Leer für keine. Tage und Monate nach UTC.';
@override
String get assistantGrantLimitInvalid => 'Keine Zahl, die das nimmt';
@override
String get assistantGrantNoHandle => 'Kein Konto hat dieses Handle.';
@override
String get assistantGrantTokens => 'Tokens';
@override
String get assistantGrantNote =>
'Vor der ersten Frage stimmen sie zu, was wohin geht. Du siehst, wie viel sie nutzen, nie, was sie fragen.';
@override
String assistantPerDay(String amount) {
return '$amount pro Tag';
}
@override
String assistantPerMonth(String amount) {
return '$amount pro Monat';
}
@override
String get assistantNoLimits => 'Ohne Grenzen';
@override
String get assistantGrantAgreed => 'Zugestimmt';
@override
String get assistantGrantNotAgreed => 'Noch nicht zugestimmt';
@override
String get assistantGrantOutdated =>
'Die Preise haben sich geändert: Grenzen neu setzen';
@override
String get assistantGrantUnset => 'Nicht mehr eingerichtet';
@override
String get assistantRevoke => 'Zurücknehmen';
@override
String assistantRevokeTitle(String assistant) {
return '$assistant zurücknehmen?';
}
@override
String get assistantRevokeMessage =>
'Sie können ihn nicht mehr fragen. Was sie genutzt haben, bleibt gezählt.';
@override
String get assistantUseByMonth => 'Nutzung nach Monat';
@override
String get assistantUseTitle => 'Nutzung der Assistenten';
@override
String get assistantUseNone => 'In diesem Monat nichts genutzt.';
@override
String get assistantUseKept =>
'Je UTC-Tag gezählt, aufbewahrt für diesen Monat und die zwei davor. Nie, was gefragt wurde.';
@override
String assistantRequests(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count Anfragen',
one: '1 Anfrage',
);
return '$_temp0';
}
@override
String assistantTokens(String amount) {
return '$amount Tokens';
}
@override
String get assistantsTitle => 'Assistenten';
@override
String assistantModelAt(String model, String host) {
return '$model bei $host';
}
@override
String assistantLeftToday(String left, String limit) {
return '$left von $limit heute übrig';
}
@override
String assistantLeftThisMonth(String left, String limit) {
return '$left von $limit diesen Monat übrig';
}
@override
String get assistantAgree => 'Lesen und zustimmen';
@override
String get assistantWaitsForLimits =>
'Wartet darauf, dass seine Grenzen neu gesetzt werden';
@override
String get assistantYourUse => 'Deine Nutzung';
@override
String assistantPrivacyPage(String instance) {
return 'Was auf $instance wohin geht';
}
@override
String get assistantOwnTitle => 'Mit deinem eigenen Schlüssel';
@override
String get assistantOwnAdd => 'Assistenten mit eigenem Schlüssel hinzufügen';
@override
String get assistantOwnNote =>
'Die App fragt deinen Anbieter direkt: Diese Instanz sieht weder deinen Schlüssel noch deine Fragen und zählt nichts. Der Schlüssel liegt in deinen Einstellungen, auf deinen Geräten verschlüsselt.';
@override
String get assistantOwnEdit => 'Dein eigener Schlüssel';
@override
String get assistantOwnName => 'Name';
@override
String get assistantOwnProtocol => 'API';
@override
String get assistantOwnBaseUrl => 'Adresse';
@override
String get assistantOwnBaseUrlHelper =>
'Leer für die des Anbieters. Mistral, OpenRouter oder ein eigenes Modell: seine Adresse mit /v1.';
@override
String get assistantOwnBaseUrlInvalid =>
'Nur https, oder http im eigenen Netz';
@override
String get assistantOwnModel => 'Modell';
@override
String get assistantOwnKey => 'Schlüssel';
@override
String get assistantOwnKeyHelper =>
'Leer für ein eigenes Modell, das keinen braucht.';
@override
String get assistantOwnImages => 'Liest Bilder';
@override
String get assistantOwnSave => 'Speichern';
@override
String get assistantOwnRemove => 'Entfernen';
@override
String assistantOwnRemoveTitle(String name) {
return '$name entfernen?';
}
@override
String get assistantOwnRemoveMessage =>
'Sein Schlüssel verschwindet auf allen Geräten aus deinen Einstellungen.';
@override
String get assistantOwnRefused =>
'Der Schlüssel wurde nicht gespeichert: Dieses Gerät kann deine Einstellungen noch nicht verschlüsseln. Öffne zuerst hier die Schlüssel deines Kontos.';
@override
String assistantOwnNotAllowed(String instance) {
return 'Wer $instance betreibt, erlaubt hier keine eigenen Schlüssel, also fragt die App diese nicht.';
}
@override
String assistantConsentTitle(String name) {
return 'Bevor du $name fragst';
}
@override
String assistantConsentModel(String model, String host) {
return 'Es antwortet das Modell $model bei $host.';
}
@override
String get assistantConsentWhatGoes =>
'Dorthin geht nur, was du sendest: deine Frage und was du davon auswählst, worum es geht. Für den Anbieter gelten seine eigenen Bedingungen.';
@override
String get assistantConsentImages =>
'Es liest die Bilder, die du hinzufügst.';
@override
String assistantConsentInstance(String instance) {
return 'Es geht auf dem Weg durch $instance, das nichts davon aufbewahrt.';
}
@override
String assistantConsentOperator(String instance) {
return 'Wer $instance betreibt, sieht, wie viel du es nutzt (Anfragen, Tokens, Kosten), nie, was du fragst.';
}
@override
String get assistantConsentConversations =>
'Deine Unterhaltungen bleiben auf deinen Geräten, verschlüsselt.';
@override
String get assistantConsentAgree => 'Zustimmen';
@override
String assistantConsentAgreedOn(String date) {
return 'Du hast am $date zugestimmt.';
}
@override
String get assistantProblemNotGranted =>
'Dieser Assistent ist für dich nicht mehr freigegeben.';
@override
String get assistantProblemNotAgreed => 'Stimm zuerst zu, was wohin geht.';
@override
String assistantProblemLimitReached(String time) {
return 'Seine Grenze ist bis $time erreicht.';
}
@override
String get assistantProblemLimitReachedNow =>
'Seine Grenze ist vorerst erreicht.';
@override
String get assistantProblemLimitOutdated =>
'Es wartet darauf, dass seine Grenzen neu gesetzt werden.';
@override
String get assistantProblemImagesNotRead => 'Es liest keine Bilder.';
@override
String get assistantProblemMalformed =>
'Diese Frage kann nicht gesendet werden.';
@override
String get assistantProblemTooLarge =>
'Diese Frage ist zu lang oder enthält zu viele oder zu große Bilder.';
@override
String get assistantProblemTooManyAtOnce =>
'Warte zuerst auf die Antworten, die unterwegs sind.';
@override
String get assistantProblemProviderKey =>
'Der Anbieter hat den Schlüssel abgelehnt.';
@override
String get assistantProblemProviderBusy =>
'Der Anbieter ist ausgelastet. Versuch es gleich noch einmal.';
@override
String get assistantProblemProviderRefused =>
'Der Anbieter hat diese Frage abgelehnt.';
@override
String get assistantProblemProviderFailed =>
'Beim Anbieter ist etwas schiefgegangen. Versuch es noch einmal.';
@override
String get assistantProblemNotAllowed =>
'Das können nur die, für die er freigegeben ist.';
@override
String get assistantProblemFailed =>
'Das hat nicht geklappt. Versuch es noch einmal.';
}

View file

@ -1283,6 +1283,314 @@ class HalkyonAppLocalizationsEn extends HalkyonAppLocalizations {
@override
String get operatorFailed => 'That didn\'t work. Try again.';
@override
String get operatorAssistantNotSet =>
'That assistant isn\'t set up here any more.';
@override
String get operatorInvalidLimit => 'A limit can\'t be below zero.';
@override
String get assistantGrantsTitle => 'Assistants';
@override
String get assistantGrantsNone => 'Nobody has an assistant yet.';
@override
String get assistantGrant => 'Grant an assistant';
@override
String get assistantGrantSave => 'Grant';
@override
String get assistantGrantChange => 'Change limits';
@override
String get assistantGrantAssistant => 'Assistant';
@override
String get assistantGrantDaily => 'Limit a day';
@override
String get assistantGrantMonthly => 'Limit a month';
@override
String get assistantGrantLimitHelper =>
'Empty for none. Days and months are UTC\'s.';
@override
String get assistantGrantLimitInvalid => 'Not a number this takes';
@override
String get assistantGrantNoHandle => 'No account has this handle.';
@override
String get assistantGrantTokens => 'tokens';
@override
String get assistantGrantNote =>
'They agree once to what goes where before they ask it. You see how much they use, never what they ask.';
@override
String assistantPerDay(String amount) {
return '$amount a day';
}
@override
String assistantPerMonth(String amount) {
return '$amount a month';
}
@override
String get assistantNoLimits => 'No limits';
@override
String get assistantGrantAgreed => 'Agreed';
@override
String get assistantGrantNotAgreed => 'Not agreed yet';
@override
String get assistantGrantOutdated =>
'Its prices changed: set its limits again';
@override
String get assistantGrantUnset => 'No longer set up';
@override
String get assistantRevoke => 'Take back';
@override
String assistantRevokeTitle(String assistant) {
return 'Take back $assistant?';
}
@override
String get assistantRevokeMessage =>
'They can no longer ask it. What they used stays counted.';
@override
String get assistantUseByMonth => 'Use by month';
@override
String get assistantUseTitle => 'Use of assistants';
@override
String get assistantUseNone => 'Nothing used in this month.';
@override
String get assistantUseKept =>
'Counted per UTC day, kept for this month and the two before. Never what was asked.';
@override
String assistantRequests(int count) {
String _temp0 = intl.Intl.pluralLogic(
count,
locale: localeName,
other: '$count requests',
one: '1 request',
);
return '$_temp0';
}
@override
String assistantTokens(String amount) {
return '$amount tokens';
}
@override
String get assistantsTitle => 'Assistants';
@override
String assistantModelAt(String model, String host) {
return '$model at $host';
}
@override
String assistantLeftToday(String left, String limit) {
return '$left of $limit left today';
}
@override
String assistantLeftThisMonth(String left, String limit) {
return '$left of $limit left this month';
}
@override
String get assistantAgree => 'Read and agree';
@override
String get assistantWaitsForLimits => 'Waits for its limits to be set again';
@override
String get assistantYourUse => 'Your use';
@override
String assistantPrivacyPage(String instance) {
return 'What goes where on $instance';
}
@override
String get assistantOwnTitle => 'With your own key';
@override
String get assistantOwnAdd => 'Add an assistant with your own key';
@override
String get assistantOwnNote =>
'The app asks your provider directly: this instance sees neither your key nor your questions, and counts nothing. The key is kept in your settings, encrypted on your devices.';
@override
String get assistantOwnEdit => 'Your own key';
@override
String get assistantOwnName => 'Name';
@override
String get assistantOwnProtocol => 'API';
@override
String get assistantOwnBaseUrl => 'Address';
@override
String get assistantOwnBaseUrlHelper =>
'Empty for the provider\'s own. Mistral, OpenRouter or a model of your own: its address with /v1.';
@override
String get assistantOwnBaseUrlInvalid =>
'Only https, or http on your own network';
@override
String get assistantOwnModel => 'Model';
@override
String get assistantOwnKey => 'Key';
@override
String get assistantOwnKeyHelper =>
'Empty for a model of your own that takes none.';
@override
String get assistantOwnImages => 'Reads images';
@override
String get assistantOwnSave => 'Save';
@override
String get assistantOwnRemove => 'Remove';
@override
String assistantOwnRemoveTitle(String name) {
return 'Remove $name?';
}
@override
String get assistantOwnRemoveMessage =>
'Its key goes from your settings on every device.';
@override
String get assistantOwnRefused =>
'The key wasn\'t kept: this device can\'t encrypt your settings yet. Open your account\'s keys here first.';
@override
String assistantOwnNotAllowed(String instance) {
return 'Whoever runs $instance doesn\'t allow own keys here, so the app doesn\'t ask these.';
}
@override
String assistantConsentTitle(String name) {
return 'Before you ask $name';
}
@override
String assistantConsentModel(String model, String host) {
return 'It answers with the model $model at $host.';
}
@override
String get assistantConsentWhatGoes =>
'Only what you send goes there: your question and what you pick of what it is about. The provider\'s own terms apply to it.';
@override
String get assistantConsentImages => 'It reads the images you add.';
@override
String assistantConsentInstance(String instance) {
return 'It passes through $instance on its way, which keeps none of it.';
}
@override
String assistantConsentOperator(String instance) {
return 'Whoever runs $instance sees how much you use it (requests, tokens, cost), never what you ask.';
}
@override
String get assistantConsentConversations =>
'Your conversations are kept on your devices, encrypted.';
@override
String get assistantConsentAgree => 'Agree';
@override
String assistantConsentAgreedOn(String date) {
return 'You agreed on $date.';
}
@override
String get assistantProblemNotGranted =>
'This assistant isn\'t granted to you any more.';
@override
String get assistantProblemNotAgreed => 'Agree to what goes where first.';
@override
String assistantProblemLimitReached(String time) {
return 'Its limit is reached until $time.';
}
@override
String get assistantProblemLimitReachedNow => 'Its limit is reached for now.';
@override
String get assistantProblemLimitOutdated =>
'It waits for its limits to be set again.';
@override
String get assistantProblemImagesNotRead => 'It reads no images.';
@override
String get assistantProblemMalformed => 'This question can\'t be sent.';
@override
String get assistantProblemTooLarge =>
'This question is too long, or holds too many or too large images.';
@override
String get assistantProblemTooManyAtOnce =>
'Wait for the answers on their way first.';
@override
String get assistantProblemProviderKey => 'The provider refused the key.';
@override
String get assistantProblemProviderBusy =>
'The provider is busy. Try again in a moment.';
@override
String get assistantProblemProviderRefused =>
'The provider refused this question.';
@override
String get assistantProblemProviderFailed =>
'The provider failed. Try again.';
@override
String get assistantProblemNotAllowed =>
'Only people it is granted to can do this.';
@override
String get assistantProblemFailed => 'That didn\'t work. Try again.';
}
/// The translations for English, as used in the United Kingdom (`en_GB`).