Added the choice between formal and informal address to the whole app (#1200)

This commit is contained in:
Thorsten Sommer authored and GitHub committed 2026-11-13 10:16:56 +01:00
1 parent 382db4b8d0
commit c9a376f742
342 files changed
+14378 -1893

No files matched your search

@@ -7,7 +7,7 @@
@T("Input")
</MudText>
<SelectDirectory Label="@T("Folder containing your documents")" DirectoryDialogTitle="@T("Select the folder containing your documents")" @bind-Directory="@this.inputDirectory" Validation="@this.ValidateInputDirectory" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
<SelectDirectory Label="@TA("Folder containing your documents")" DirectoryDialogTitle="@TA("Select the folder containing your documents")" @bind-Directory="@this.inputDirectory" Validation="@this.ValidateInputDirectory" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
<MudTextSwitch Label="@T("Include subfolders?")" Disabled="@this.isProcessingBatch" Value="@this.includeSubdirectories" ValueChanged="@(v => this.includeSubdirectories = v)" LabelOn="@T("Yes, process files in subfolders as well")" LabelOff="@T("No, only process files in the selected folder")"/>
@@ -31,7 +31,7 @@
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("Images in JPG, PNG, WebP, GIF, BMP, or TIFF format are given to the model as images, so you need a model that understands images. If the selected model cannot read images, remove the image patterns, such as *.jpg or *.png, before you start.")
@TA("Images in JPG, PNG, WebP, GIF, BMP, or TIFF format are given to the model as images, so you need a model that understands images. If the selected model cannot read images, remove the image patterns, such as *.jpg or *.png, before you start.")
</MudJustifiedText>
<MudStack Class="mb-3">
@@ -81,7 +81,7 @@
}
else if (this.promptSource is BatchProcessingPromptSource.FILE_IMPORT)
{
<ReadFileContent Text="@T("Select the file with your instructions")" @bind-FileContent="@this.ImportedPrompt" Filter="@([FileTypes.MARKDOWN])" ShowAttachedDocumentState="@true" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
<ReadFileContent Text="@TA("Select the file with your instructions")" @bind-FileContent="@this.ImportedPrompt" Filter="@([FileTypes.MARKDOWN])" ShowAttachedDocumentState="@true" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
@if (!string.IsNullOrWhiteSpace(this.promptFilePath))
{
@@ -103,13 +103,13 @@ else
{
@if (this.ConfiguredPolicyIsMissing)
{
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@T("The configured default policy no longer exists. Please select another document analysis policy.")</MudAlert>
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@TA("The configured default policy no longer exists. Please select another document analysis policy.")</MudAlert>
}
@if (this.SettingsManager.ConfigurationData.DocumentAnalysis.Policies.Count is 0)
{
<MudJustifiedText Typo="Typo.body1" Class="mb-3">
@T("You have not yet created any document analysis policies. Please use the Document Analysis Assistant to create a policy first.")
@TA("You have not yet created any document analysis policies. Please use the Document Analysis Assistant to create a policy first.")
</MudJustifiedText>
<MudButton Href="@Routes.ASSISTANT_DOCUMENT_ANALYSIS" Variant="Variant.Filled" Color="Color.Primary" Class="mb-3">
@T("Open the Document Analysis Assistant")
@@ -156,7 +156,7 @@ else
@if (this.outputMode is BatchProcessingOutputMode.INDIVIDUAL_FILES)
{
<MudSelect T="FileExportFormat" @bind-Value="@this.resultFileFormat" Disabled="@this.isProcessingBatch" AdornmentIcon="@AppIcons.DOCUMENT" Adornment="Adornment.Start" Label="@T("File format")" HelperText="@T("Choose the format of the result files. Everything except Markdown is converted by Pandoc, which AI Studio offers to install when it is missing.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
<MudSelect T="FileExportFormat" @bind-Value="@this.resultFileFormat" Disabled="@this.isProcessingBatch" AdornmentIcon="@AppIcons.DOCUMENT" Adornment="Adornment.Start" Label="@T("File format")" HelperText="@TA("Choose the format of the result files. Everything except Markdown is converted by Pandoc, which AI Studio offers to install when it is missing.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
@foreach (var format in FileExportFormat.AnswerFormats)
{
<MudSelectItem Value="@format">
@@ -171,11 +171,11 @@ else
}
else
{
<MudTextField T="string" @bind-Text="@this.csvFileName" Validation="@this.ValidateCsvFileName" Immediate="@true" Disabled="@this.isProcessingBatch" Label="@T("Name of the results table (optional)")" HelperText="@T("The file name of the CSV results table. We add the ending .csv when it is missing. When left empty, we use 'batch-results.csv'.")" AdornmentIcon="@AppIcons.DOCUMENT" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.csvFileName" Validation="@this.ValidateCsvFileName" Immediate="@true" Disabled="@this.isProcessingBatch" Label="@T("Name of the results table (optional)")" HelperText="@TA("The file name of the CSV results table. We add the ending .csv when it is missing. When left empty, we use 'batch-results.csv'.")" AdornmentIcon="@AppIcons.DOCUMENT" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.resultColumnHeader" Disabled="@this.isProcessingBatch" Label="@T("Header of the result column (optional)")" HelperText="@T("The results table contains one row per document, starting with the file name. Here you can name the column that holds the AI answer, e.g., Summary. When left empty, we use 'Result'.")" AdornmentIcon="@AppIcons.RESULT_COLUMN" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.resultColumnHeader" Disabled="@this.isProcessingBatch" Label="@T("Header of the result column (optional)")" HelperText="@TA("The results table contains one row per document, starting with the file name. Here you can name the column that holds the AI answer, e.g., Summary. When left empty, we use 'Result'.")" AdornmentIcon="@AppIcons.RESULT_COLUMN" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudSelect T="BatchProcessingCsvSeparator" @bind-Value="@this.csvSeparator" Disabled="@this.isProcessingBatch" AdornmentIcon="@AppIcons.CSV_SEPARATOR" Adornment="Adornment.Start" Label="@T("Column separator")" HelperText="@T("Choose which character separates the columns of the results table.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
<MudSelect T="BatchProcessingCsvSeparator" @bind-Value="@this.csvSeparator" Disabled="@this.isProcessingBatch" AdornmentIcon="@AppIcons.CSV_SEPARATOR" Adornment="Adornment.Start" Label="@T("Column separator")" HelperText="@TA("Choose which character separates the columns of the results table.")" Variant="Variant.Outlined" Margin="Margin.Dense" Class="mb-3">
@foreach (var separator in Enum.GetValues<BatchProcessingCsvSeparator>())
{
<MudSelectItem Value="@separator">
@@ -186,14 +186,14 @@ else
@if (this.csvSeparator is BatchProcessingCsvSeparator.CUSTOM)
{
<MudTextField T="string" @bind-Text="@this.customCsvSeparator" Validation="@this.ValidateCustomCsvSeparator" Immediate="@true" Disabled="@this.isProcessingBatch" Label="@T("Custom column separator")" HelperText="@T("Enter one punctuation or symbol character.")" AdornmentIcon="@AppIcons.CSV_SEPARATOR" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.customCsvSeparator" Validation="@this.ValidateCustomCsvSeparator" Immediate="@true" Disabled="@this.isProcessingBatch" Label="@T("Custom column separator")" HelperText="@TA("Enter one punctuation or symbol character.")" AdornmentIcon="@AppIcons.CSV_SEPARATOR" Adornment="Adornment.Start" Variant="Variant.Outlined" Margin="Margin.Normal" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
}
}
<SelectDirectory Label="@T("Output folder (optional)")" DirectoryDialogTitle="@T("Select the output folder")" @bind-Directory="@this.outputDirectory" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
<SelectDirectory Label="@T("Output folder (optional)")" DirectoryDialogTitle="@TA("Select the output folder")" @bind-Directory="@this.outputDirectory" EnableDragDrop="true" Disabled="@this.isProcessingBatch"/>
<MudJustifiedText Typo="Typo.body2" Class="mb-3">
@T("We always write a semicolon-separated log named log.csv, which lists every document with its processing time, the model, the status, and the details of any error. When you start another run on the same output folder, we ask you whether to continue that run: documents which failed or are missing in the log are then processed again. When no output folder is selected, everything is written to the subfolder 'ai-results' within the input folder.")
@TA("We always write a semicolon-separated log named log.csv, which lists every document with its processing time, the model, the status, and the details of any error. When you start another run on the same output folder, we ask you whether to continue that run: documents which failed or are missing in the log are then processed again. When no output folder is selected, everything is written to the subfolder 'ai-results' within the input folder.")
</MudJustifiedText>
<MudText Typo="Typo.h6" Class="mb-3 mt-6">
@@ -203,7 +203,7 @@ else
@if (MinimumDelayIsManaged)
{
<MudAlert Severity="Severity.Info" Dense="true" Class="mb-3">
@(string.Format(T("Your organization requires a pause of at least {0} seconds between files."), this.ManagedMinimumDelaySeconds))
@(string.Format(TA("Your organization requires a pause of at least {0} seconds between files."), this.ManagedMinimumDelaySeconds))
</MudAlert>
}
else
@@ -58,7 +58,7 @@ public partial class AssistantBatchProcessing
{
if (!this.ProviderSettings.SupportsImageInput())
{
this.FinishFileResult(fileResult, BatchProcessingFileStatus.FAILED, T("The selected model cannot read images. Select a model with image support to process this image."));
this.FinishFileResult(fileResult, BatchProcessingFileStatus.FAILED, TA("The selected model cannot read images. Select a model with image support to process this image."));
return null;
}
@@ -162,7 +162,7 @@ public partial class AssistantBatchProcessing
return;
this.hasReportedWriteFailure = true;
await this.MessageBus.SendError(new(AppIcons.SAVE_FAILED, string.Format(T("Was not able to write '{0}'. Please make sure that the file is not opened in another application. The results of this run are incomplete on disk. The message is: '{1}'"), Path.GetFileName(targetFilePath), e.Message)));
await this.MessageBus.SendError(new(AppIcons.SAVE_FAILED, string.Format(TA("Was not able to write '{0}'. Please make sure that the file is not opened in another application. The results of this run are incomplete on disk. The message is: '{1}'"), Path.GetFileName(targetFilePath), e.Message)));
}
}
@@ -196,8 +196,8 @@ public partial class AssistantBatchProcessing
if (failedFiles > 0)
{
var failureMessage = failedFiles == 1
? T("The batch run finished, but one file could not be processed. See the progress table and log for details.")
: string.Format(T("The batch run finished, but {0} files could not be processed. See the progress table and log for details."), failedFiles);
? TA("The batch run finished, but one file could not be processed. See the progress table and log for details.")
: string.Format(TA("The batch run finished, but {0} files could not be processed. See the progress table and log for details."), failedFiles);
await this.MessageBus.SendError(new(AppIcons.ERROR, failureMessage));
}
}
@@ -8,7 +8,7 @@ public partial class AssistantBatchProcessing
private string? ValidateInputDirectory(string directory)
{
if (string.IsNullOrWhiteSpace(directory))
return T("Please select the folder that contains the documents you want to process.");
return TA("Please select the folder that contains the documents you want to process.");
if (!Directory.Exists(directory))
return T("The selected folder does not exist.");
@@ -24,7 +24,7 @@ public partial class AssistantBatchProcessing
return null;
if (fileName.Trim().IndexOfAny(Path.GetInvalidFileNameChars()) >= 0)
return T("Please provide a file name without a path, e.g., my-results.csv");
return TA("Please provide a file name without a path, e.g., my-results.csv");
return null;
}
@@ -36,7 +36,7 @@ public partial class AssistantBatchProcessing
return null;
if (!BatchProcessingCsvSeparator.IsValidCustomSeparator(separator))
return T("Please enter exactly one punctuation or symbol character. Letters, numbers, spaces, quotation marks, and line breaks cannot be used as CSV separators.");
return TA("Please enter exactly one punctuation or symbol character. Letters, numbers, spaces, quotation marks, and line breaks cannot be used as CSV separators.");
return null;
}
@@ -44,7 +44,7 @@ public partial class AssistantBatchProcessing
private string? ValidateFreePrompt(string prompt)
{
if (this.promptSource is BatchProcessingPromptSource.FREE_PROMPT && string.IsNullOrWhiteSpace(prompt))
return T("Please describe what the AI should do with each document.");
return TA("Please describe what the AI should do with each document.");
return null;
}
@@ -54,10 +54,10 @@ public partial class AssistantBatchProcessing
/// </summary>
private string? ValidateInstructionSource() => this.promptSource switch
{
BatchProcessingPromptSource.POLICY when this.ConfiguredPolicyIsMissing => T("The configured default policy no longer exists. Please select another document analysis policy."),
BatchProcessingPromptSource.POLICY when this.selectedPolicy is null => T("Please select a document analysis policy."),
BatchProcessingPromptSource.POLICY when this.ConfiguredPolicyIsMissing => TA("The configured default policy no longer exists. Please select another document analysis policy."),
BatchProcessingPromptSource.POLICY when this.selectedPolicy is null => TA("Please select a document analysis policy."),
BatchProcessingPromptSource.FILE_IMPORT when !string.IsNullOrWhiteSpace(this.promptFileLoadIssue) => this.promptFileLoadIssue,
BatchProcessingPromptSource.FILE_IMPORT when string.IsNullOrWhiteSpace(this.importedPrompt) => T("Please select the file which contains your instructions."),
BatchProcessingPromptSource.FILE_IMPORT when string.IsNullOrWhiteSpace(this.importedPrompt) => TA("Please select the file which contains your instructions."),
_ => null,
};
@@ -165,7 +165,7 @@ public partial class AssistantBatchProcessing
if (files.Count == 0)
{
this.AddInputIssue(this.numTooLargeFiles > 0
? string.Format(T("Every matching file in the selected folder is larger than {0}. Raise the size limit to process them."), FolderFileSelection.MegabytesToBytes(this.maxFileSizeMegabytes).FileSize())
? string.Format(TA("Every matching file in the selected folder is larger than {0}. Raise the size limit to process them."), FolderFileSelection.MegabytesToBytes(this.maxFileSizeMegabytes).FileSize())
: T("No matching files were found in the selected folder."));
return null;
}
@@ -173,7 +173,7 @@ public partial class AssistantBatchProcessing
var requiresTranscription = files.Any(file => IsTranscribableMedia(file) && !HasReusableTranscript(file));
if (requiresTranscription && !this.MediaTranscriptionService.HasUsableTranscriptionProvider)
{
this.AddInputIssue(T("The selected files include audio or video without an existing transcript, but no usable transcription provider is configured. Configure one in the transcription settings or remove the media patterns."));
this.AddInputIssue(TA("The selected files include audio or video without an existing transcript, but no usable transcription provider is configured. Configure one in the transcription settings or remove the media patterns."));
return null;
}
@@ -184,7 +184,7 @@ public partial class AssistantBatchProcessing
//
if (files.All(IsImage) && !this.ProviderSettings.SupportsImageInput())
{
this.AddInputIssue(T("All selected files are images, but the selected model cannot read images. Select a model with image support."));
this.AddInputIssue(TA("All selected files are images, but the selected model cannot read images. Select a model with image support."));
return null;
}
@@ -54,7 +54,7 @@ public partial class AssistantBatchProcessing : AssistantBaseCore<SettingsDialog
protected override string Title => T("Batch Processing Assistant");
protected override string Description => T("Process all documents and media files of a folder in one batch run: documents are converted to Markdown, while audio and video files are transcribed automatically, before their content is sent to the AI along with your instructions. You choose whether each answer is stored as its own Markdown file or whether all answers are collected in one CSV results table. A log records what happened to every file, so a run which was interrupted or produced errors can be continued later. A single failing file never stops the entire run.");
protected override string Description => TA("Process all documents and media files of a folder in one batch run: documents are converted to Markdown, while audio and video files are transcribed automatically, before their content is sent to the AI along with your instructions. You choose whether each answer is stored as its own Markdown file or whether all answers are collected in one CSV results table. A log records what happened to every file, so a run which was interrupted or produced errors can be continued later. A single failing file never stops the entire run.");
// The results of this assistant go into files, so no chat continues its conversations:
protected override ChatSystemPromptChoice ChatSystemPrompt => ChatSystemPromptChoice.AI_STUDIO_DEFAULT;