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

@@ -6,7 +6,7 @@
<MudText Typo="Typo.h5" Class="mb-1 mt-3"> @T("Content to derive slide from")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("You can enter text, use one or more documents or images, or use both. At least one of these options is required.")
@TA("You can enter text, use one or more documents or images, or use both. At least one of these options is required.")
</MudJustifiedText>
<MudTextField T="string" @bind-Text="@this.inputContent" Validation="@this.ValidatingContext" Adornment="Adornment.Start" Lines="6" MaxLines="12" Sizing="InputSizing.Fixed" Label="@T("Text content")" Variant="Variant.Outlined" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
@@ -22,7 +22,7 @@
<MudItem xs="12" lg="6" Class="d-flex flex-column">
<MudText Typo="Typo.h6" Class="mb-1">@T("Pictures for the slides")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2 flex-grow-1">
@T("Attach the pictures which the presentation must show, e.g., photos, diagrams, or screenshots. Each of them goes on exactly one slide with room for a picture. Only PNG and JPEG pictures are possible. An LLM which can read images sees them; any other LLM knows only their file names, so clear names help.")
@TA("Attach the pictures which the presentation must show, e.g., photos, diagrams, or screenshots. Each of them goes on exactly one slide with room for a picture. Only PNG and JPEG pictures are possible. An LLM which can read images sees them; any other LLM knows only their file names, so clear names help.")
</MudJustifiedText>
<AttachDocuments Name="Pictures for the slides" @bind-DocumentPaths="@this.slidePictures" OnChange="@this.OnSlidePicturesChanged" CatchAllDocuments="false" UseSmallForm="false" AllowedFileTypes="@([FileTypes.SLIDE_PICTURE])" Provider="@this.ProviderSettings"/>
</MudItem>
@@ -35,7 +35,7 @@
<SelectDirectory Directory="@this.pictureLibraryPath" DirectoryChanged="@this.OnPictureLibraryChanged" Label="@T("(Optional) Picture library")" DirectoryDialogTitle="@T("Select a picture library")" Validation="@this.ValidatePictureLibrary" Disabled="@this.IsPictureLibraryLocked" EnableDragDrop="true"/>
@if (this.IsLockedPictureLibraryUnavailable)
{
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@T("The picture library of your organization is not available right now, e.g., because its network drive cannot be reached. Until it is, the assistant plans the slides without it.")</MudAlert>
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@TA("The picture library of your organization is not available right now, e.g., because its network drive cannot be reached. Until it is, the assistant plans the slides without it.")</MudAlert>
}
@if (this.PictureLibraryHint is { Length: > 0 } pictureLibraryHint)
{
@@ -52,19 +52,19 @@
<MudText Typo="Typo.h6" Class="mb-1"> @T("Title")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("Please enter a title for the presentation. This will help the LLM to select more relevant content.")
@TA("Please enter a title for the presentation. This will help the LLM to select more relevant content.")
</MudJustifiedText>
<MudTextField T="string" @bind-Text="@this.inputTitle" Validation="@this.ValidatingTitle" Adornment="Adornment.Start" Label="@T("Presentation title")" Variant="Variant.Outlined" Class="mb-1" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudText Typo="Typo.h6" Class="mb-1 mt-3"> @T("Important Aspects")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("You might want to specify important aspects that the LLM should consider when creating the slides. For example, the use of emojis or specific topics that should be highlighted.")
@TA("You might want to specify important aspects that the LLM should consider when creating the slides. For example, the use of emojis or specific topics that should be highlighted.")
</MudJustifiedText>
<MudTextField T="string" Sizing="InputSizing.Auto" Lines="3" @bind-Text="@this.importantAspects" class="mb-1" Label="@T("(Optional) Important Aspects")" ShrinkLabel="true" Variant="Variant.Outlined" AdornmentIcon="@AppIcons.IMPORTANT_ASPECTS" Adornment="Adornment.Start" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudText Typo="Typo.h6" Class="mb-1 mt-3"> @T("Extent of the planned presentation")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("Please specify the extent of the planned presentation. This can be the number of slides, the maximum number of bullet points per slide, or the time specification for the presentation. This will help the LLM to create a presentation that fits your needs. Leave the default values if you don't have specific requirements regarding the extent of the presentation. You might only want to specify one of these parameters, for example the time specification, and leave the others at their default values.")
@TA("Please specify the extent of the planned presentation. This can be the number of slides, the maximum number of bullet points per slide, or the time specification for the presentation. This will help the LLM to create a presentation that fits your needs. Leave the default values if you don't have specific requirements regarding the extent of the presentation. You might only want to specify one of these parameters, for example the time specification, and leave the others at their default values.")
</MudJustifiedText>
<MudGrid>
<MudItem xs="4">
@@ -105,7 +105,7 @@
<MudText Typo="Typo.h6" Class="mb-1 mt-3"> @T("Audience")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("Please specify the audience for the planned presentation. This will help the LLM to create a presentation that fits your needs. You can specify the audience profile, the age group, organizational level, and the expertise. You don't have to specify all of these parameters, for example you might only want to specify the audience profile and leave the other parameters at their default values.")
@TA("Please specify the audience for the planned presentation. This will help the LLM to create a presentation that fits your needs. You can specify the audience profile, the age group, organizational level, and the expertise. You don't have to specify all of these parameters, for example you might only want to specify the audience profile and leave the other parameters at their default values.")
</MudJustifiedText>
<EnumSelection T="AudienceProfile" NameFunc="@(profile => profile.Name)" @bind-Value="@this.selectedAudienceProfile" Icon="@AppIcons.AUDIENCE" Label="@T("Audience profile")" />
<EnumSelection T="AudienceAgeGroup" NameFunc="@(ageGroup => ageGroup.Name)" @bind-Value="@this.selectedAudienceAgeGroup" Icon="@AppIcons.AUDIENCE_AGE_GROUP" Label="@T("Audience age group")" />
@@ -114,7 +114,7 @@
<MudText Typo="Typo.h5" Class="mb-1 mt-6"> @T("PowerPoint template")</MudText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@T("Choose a PowerPoint presentation or template (.pptx, .potx) whose layouts the slides should use. The LLM then plans every slide for one of these layouts, and you can create a PowerPoint file (.pptx) in the design of your template, which other programs such as LibreOffice Impress open as well. OpenDocument presentations (.odp, .otp) cannot serve as a template yet. Without a template, you get an outline, which you can copy or export.")
@TA("Choose a PowerPoint presentation or template (.pptx, .potx) whose layouts the slides should use. The LLM then plans every slide for one of these layouts, and you can create a PowerPoint file (.pptx) in the design of your template, which other programs such as LibreOffice Impress open as well. OpenDocument presentations (.odp, .otp) cannot serve as a template yet. Without a template, you get an outline, which you can copy or export.")
</MudJustifiedText>
<MudJustifiedText Typo="Typo.body1" Class="mb-2">
@SlideAssistantTexts.WhatTheLLMKnowsOfLayouts
@@ -122,7 +122,7 @@
<SelectFile File="@this.templatePath" FileChanged="@this.OnTemplateChanged" Label="@T("PowerPoint template")" FileDialogTitle="@T("Select a PowerPoint template")" Filter="@([FileTypes.POWER_POINT_TEMPLATE])" Validation="@this.ValidateTemplate" Disabled="@this.IsTemplateLocked" EnableDragDrop="true"/>
@if (this.IsLockedTemplateUnavailable)
{
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@T("The PowerPoint template of your organization is not available right now, e.g., because its network drive cannot be reached. Until it is, the assistant plans an outline only.")</MudAlert>
<MudAlert Severity="Severity.Warning" Dense="true" Class="mb-3">@TA("The PowerPoint template of your organization is not available right now, e.g., because its network drive cannot be reached. Until it is, the assistant plans an outline only.")</MudAlert>
}
@if (!string.IsNullOrWhiteSpace(this.templatePath) && !this.IsTemplateLocked)
{
@@ -137,7 +137,7 @@
<CompactExpansionPanel HeaderText="@T("Advanced Options")" HeaderIcon="@AppIcons.ADVANCED_SETTINGS" Class="mb-3 rounded" @bind-Expanded="@this.advancedOptionsExpanded">
@if (loadedTemplate.VariantLabels.Count > 0)
{
<MudSelect T="string" @bind-Value="@this.variantLabel" Label="@T("Color variant")" HelperText="@T("Your template offers its layouts in several colors. Choose one for the presentation.")" Variant="Variant.Outlined" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.COLOR" Class="mb-3">
<MudSelect T="string" @bind-Value="@this.variantLabel" Label="@T("Color variant")" HelperText="@TA("Your template offers its layouts in several colors. Choose one for the presentation.")" Variant="Variant.Outlined" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.COLOR" Class="mb-3">
@foreach (var label in loadedTemplate.VariantLabels)
{
<MudSelectItem T="string" Value="@label">@label</MudSelectItem>
@@ -145,7 +145,7 @@
</MudSelect>
}
<MudTextField T="string" @bind-Text="@this.footerText" Label="@T("(Optional) Footer")" HelperText="@T("Shown on every slide whose layout has a footer, e.g., your name, organization, and date.")" Variant="Variant.Outlined" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.SLIDE_FOOTER" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudTextField T="string" @bind-Text="@this.footerText" Label="@T("(Optional) Footer")" HelperText="@TA("Shown on every slide whose layout has a footer, e.g., your name, organization, and date.")" Variant="Variant.Outlined" Adornment="Adornment.Start" AdornmentIcon="@AppIcons.SLIDE_FOOTER" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<MudSwitch T="bool" @bind-Value="@this.keepEmptyPicturePlaceholders" Color="Color.Primary" Class="mx-1">
@T("Keep empty picture placeholders")
@@ -176,15 +176,15 @@ public partial class SlideAssistant
get
{
if (this.slideDeck is null)
return T("Please plan the slides first.");
return TA("Please plan the slides first.");
if (this.template is null)
return T("Please choose a PowerPoint template to create a file from the slides.");
return TA("Please choose a PowerPoint template to create a file from the slides.");
if (!this.PlannedForCurrentTemplate)
return T("The slides were planned for another template. Please plan them again.");
return TA("The slides were planned for another template. Please plan them again.");
return T("Creates a PowerPoint file in the design of your template.");
return TA("Creates a PowerPoint file in the design of your template.");
}
}
@@ -288,7 +288,7 @@ public partial class SlideAssistant
private string? ValidateTemplateSlideUse(TemplateSlide slide, TemplateSlideUse use)
{
if (use is TemplateSlideUse.EDIT && !slide.IsEditable)
return T("The LLM cannot edit this slide, because its layout has no placeholders for content. Please keep it unchanged or leave it out.");
return TA("The LLM cannot edit this slide, because its layout has no placeholders for content. Please keep it unchanged or leave it out.");
return null;
}
@@ -324,7 +324,7 @@ public partial class SlideAssistant
return T("The template file does not exist.");
if (this.template is null)
return T("The template cannot be read. Please choose a PowerPoint presentation or template (.pptx, .potx).");
return TA("The template cannot be read. Please choose a PowerPoint presentation or template (.pptx, .potx).");
if (this.template.Families.Count == 0)
return T("The template has no layouts with room for content.");
@@ -574,7 +574,7 @@ public partial class SlideAssistant
if (!result.IsWritten)
{
this.Logger.LogWarning("The PowerPoint template '{TemplatePath}' changed since the slides were planned, and they no longer fit it.", this.slideDeckTemplatePath);
await this.MessageBus.SendError(new(AppIcons.SLIDES, T("The template has changed since the slides were planned. Please create the slides again.")));
await this.MessageBus.SendError(new(AppIcons.SLIDES, TA("The template has changed since the slides were planned. Please create the slides again.")));
return;
}
@@ -591,7 +591,7 @@ public partial class SlideAssistant
catch (Exception e)
{
this.Logger.LogError(e, "Creating the PowerPoint file '{FilePath}' failed.", targetPath);
await this.MessageBus.SendError(new(AppIcons.SLIDES, T("The PowerPoint file could not be created. Please check that the template can be read and that the target file is not open in another program.")));
await this.MessageBus.SendError(new(AppIcons.SLIDES, TA("The PowerPoint file could not be created. Please check that the template can be read and that the target file is not open in another program.")));
}
finally
{
@@ -15,7 +15,7 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
protected override string Title => T("Slide Assistant");
protected override string Description => T("This assistant helps you create clear, structured slides from long texts or documents. Enter a presentation title and provide the content either as text or with one or more documents. Important aspects allow you to add instructions to the LLM regarding output or formatting. Set the number of slides either directly or based on your desired presentation duration. You can also limit the number of bullet points per slide. If the default value of 0 is not changed, the LLM will independently determine how many slides or bullet points to generate. The output can be flexibly generated in various languages and tailored to a specific audience. When you choose a PowerPoint template, the slides use its layouts, and you can create a PowerPoint file in its design from the result.");
protected override string Description => TA("This assistant helps you create clear, structured slides from long texts or documents. Enter a presentation title and provide the content either as text or with one or more documents. Important aspects allow you to add instructions to the LLM regarding output or formatting. Set the number of slides either directly or based on your desired presentation duration. You can also limit the number of bullet points per slide. If the default value of 0 is not changed, the LLM will independently determine how many slides or bullet points to generate. The output can be flexibly generated in various languages and tailored to a specific audience. When you choose a PowerPoint template, the slides use its layouts, and you can create a PowerPoint file in its design from the result.");
// The prompt of this assistant demands JSON for the layouts, so the chat gets a prompt of its own, which keeps the limits of the slides:
protected override ChatSystemPromptChoice ChatSystemPrompt => ChatSystemPromptChoice.Custom(this.CreateChatSystemPrompt);
@@ -192,7 +192,7 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
ContentType = ContentType.TEXT,
Content = new ContentText
{
Text = this.T("The result of your previous Slide Assistant session."),
Text = this.TA("The result of your previous Slide Assistant session."),
FileAttachments = [..this.loadedDocumentPaths, ..this.slidePictures],
}
},
@@ -446,14 +446,14 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
private string? ValidatingTitle(string text)
{
if(string.IsNullOrWhiteSpace(text))
return T("Please provide a title");
return TA("Please provide a title");
return null;
}
private string? ValidatingContext(string text)
{
if(string.IsNullOrWhiteSpace(text) && !this.HasValidInputDocuments())
return T("Please provide a text or at least one valid document or image.");
return TA("Please provide a text or at least one valid document or image.");
return null;
}
@@ -497,7 +497,7 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
private string? ValidateCustomLanguage(string language)
{
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
return T("Please provide a custom language.");
return TA("Please provide a custom language.");
return null;
}
@@ -505,7 +505,7 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
private string? ValidateCustomOccasion(string occasion)
{
if(this.selectedOccasion == PresentationOccasion.OTHER && string.IsNullOrWhiteSpace(occasion))
return T("Please describe the occasion.");
return TA("Please describe the occasion.");
return null;
}
@@ -777,14 +777,14 @@ public partial class SlideAssistant : AssistantBaseCore<SettingsDialogSlideBuild
if (deck is null || deck.Slides.Count == 0 || deck.Slides.Any(slide => !SlideDeckSpecValidator.CanWrite(slide, catalog, chosenTemplateSlideUses)))
{
this.Logger.LogWarning("The slides the model planned cannot be used: {Issues}", string.Join(" | ", issues));
await this.MessageBus.SendError(new(AppIcons.SLIDES, T("The LLM did not describe the slides in a usable form, even after a second request. Please try again, or choose another model.")));
await this.MessageBus.SendError(new(AppIcons.SLIDES, TA("The LLM did not describe the slides in a usable form, even after a second request. Please try again, or choose another model.")));
return;
}
if (issues.Count > 0)
{
this.Logger.LogInformation("The slides the model planned still have issues after the repair round: {Issues}", string.Join(" | ", issues));
await this.MessageBus.SendWarning(new(AppIcons.SLIDES, T("Not every slide fits its layout as planned; for example, a text may run long. Please check the slides before presenting.")));
await this.MessageBus.SendWarning(new(AppIcons.SLIDES, TA("Not every slide fits its layout as planned; for example, a text may run long. Please check the slides before presenting.")));
}
this.slideDeck = deck;
@@ -7,27 +7,27 @@ namespace AIStudio.Assistants.SlideBuilder;
/// </summary>
public static class SlideAssistantTexts
{
private static string TB(string fallbackEN) => Tools.PluginSystem.I18N.I.T(fallbackEN, typeof(SlideAssistantTexts).Namespace, nameof(SlideAssistantTexts));
private static string TA(string fallbackEN) => Tools.PluginSystem.I18N.I.TA(fallbackEN, typeof(SlideAssistantTexts).Namespace, nameof(SlideAssistantTexts));
/// <summary>
/// What the LLM knows about the layouts of a template, and why their names matter.
/// </summary>
public static string WhatTheLLMKnowsOfLayouts => TB("The LLM does not see what your layouts look like. It knows only their names from the slide master of your template, where their placeholders sit, and how much text fits into them. Clear names such as “Chapter” or “Content with picture” therefore help it choose well; in PowerPoint, you rename a layout in the slide master view. Logo, background, and footer come with the layout. Layouts which differ only in their color count as one, and the color applies to the whole presentation.");
public static string WhatTheLLMKnowsOfLayouts => TA("The LLM does not see what your layouts look like. It knows only their names from the slide master of your template, where their placeholders sit, and how much text fits into them. Clear names such as “Chapter” or “Content with picture” therefore help it choose well; in PowerPoint, you rename a layout in the slide master view. Logo, background, and footer come with the layout. Layouts which differ only in their color count as one, and the color applies to the whole presentation.");
/// <summary>
/// What the choice of a layout for a kind of slide does, and what it does not.
/// </summary>
public static string WhatALayoutChoiceDoes => TB("Choose which layout of your template the LLM uses for certain kinds of slides. The lists offer the layouts with room for content. Where you have no preference, the LLM decides by the names and placeholders of the layouts. A choice says which layout such a slide uses, not whether the presentation has one: whether there is, e.g., an agenda, follows from your content and your important aspects. Only for the framing slides, e.g., the table of contents or the imprint, does a choice also add such a slide; their help says so. Each chapter becomes a section in PowerPoint, which the slide sorter shows as an outline.");
public static string WhatALayoutChoiceDoes => TA("Choose which layout of your template the LLM uses for certain kinds of slides. The lists offer the layouts with room for content. Where you have no preference, the LLM decides by the names and placeholders of the layouts. A choice says which layout such a slide uses, not whether the presentation has one: whether there is, e.g., an agenda, follows from your content and your important aspects. Only for the framing slides, e.g., the table of contents or the imprint, does a choice also add such a slide; their help says so. Each chapter becomes a section in PowerPoint, which the slide sorter shows as an outline.");
/// <summary>
/// What keeping the empty picture placeholders does.
/// </summary>
public static string WhatKeepingEmptyPicturePlaceholdersDoes => TB("When a new slide has no picture for a picture placeholder of its layout, it leaves the placeholder out, as every other placeholder without content. Keep these placeholders to insert pictures of your own later: PowerPoint shows them while you edit and hides them in the slide show.");
public static string WhatKeepingEmptyPicturePlaceholdersDoes => TA("When a new slide has no picture for a picture placeholder of its layout, it leaves the placeholder out, as every other placeholder without content. Keep these placeholders to insert pictures of your own later: PowerPoint shows them while you edit and hides them in the slide show.");
/// <summary>
/// What a picture library is, which of its files count, and how the LLM uses them.
/// </summary>
public static string WhatAPictureLibraryOffers => string.Format(TB("Choose a folder with pictures and icons, e.g., the picture library of your organization. The LLM reads only their names, never the pictures themselves, and uses a picture where its name fits a slide; it does not have to use any. So clear names such as “rocket-launch.jpg” help. Only PNG and JPEG pictures count: many icons come as SVG, which the assistant leaves out. A picture whose name, or the name of one of its folders, the chosen folder included, starts with “icon” is an icon. An icon may appear on several slides and goes into picture placeholders of any size, except those which cover half the slide or more, as a background picture does. From a folder with more than {0} pictures, the LLM gets a random {0} of them for each planning."), MaxLibraryPictures);
public static string WhatAPictureLibraryOffers => string.Format(TA("Choose a folder with pictures and icons, e.g., the picture library of your organization. The LLM reads only their names, never the pictures themselves, and uses a picture where its name fits a slide; it does not have to use any. So clear names such as “rocket-launch.jpg” help. Only PNG and JPEG pictures count: many icons come as SVG, which the assistant leaves out. A picture whose name, or the name of one of its folders, the chosen folder included, starts with “icon” is an icon. An icon may appear on several slides and goes into picture placeholders of any size, except those which cover half the slide or more, as a background picture does. From a folder with more than {0} pictures, the LLM gets a random {0} of them for each planning."), MaxLibraryPictures);
/// <summary>
/// The most pictures of a library the LLM reads about, as the user reads the number.
@@ -37,5 +37,5 @@ public static class SlideAssistantTexts
/// <summary>
/// What the presentation does with the slides a template carries besides its layouts.
/// </summary>
public static string WhatHappensToTemplateSlides => TB("Besides its layouts, your file contains slides, e.g., samples of the template or the slides of an earlier presentation. By default, the presentation leaves them out. Choose for each slide whether the presentation keeps it unchanged or lets the LLM edit its content. The LLM uses each of these slides exactly once, keeps them in their order, and may add new slides before, between, and after them. It never copies a slide as a pattern: new slides always come from the layouts of your template.");
public static string WhatHappensToTemplateSlides => TA("Besides its layouts, your file contains slides, e.g., samples of the template or the slides of an earlier presentation. By default, the presentation leaves them out. Choose for each slide whether the presentation keeps it unchanged or lets the LLM edit its content. The LLM uses each of these slides exactly once, keeps them in their order, and may add new slides before, between, and after them. It never copies a slide as a pattern: new slides always come from the layouts of your template.");
}