Skip to content

Create I18N data, part 2 #429

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Things we are currently working on:
- [x] ~~Plan & implement the base plugin system ([PR #322](https://github.com/MindWorkAI/AI-Studio/pull/322))~~
- [x] ~~Start the plugin system ([PR #372](https://github.com/MindWorkAI/AI-Studio/pull/372))~~
- [x] ~~Added hot-reload support for plugins ([PR #377](https://github.com/MindWorkAI/AI-Studio/pull/377), [PR #391](https://github.com/MindWorkAI/AI-Studio/pull/391))~~
- [ ] Add support for other languages (I18N) to AI Studio (~~[PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404))~~
- [ ] Add support for other languages (I18N) to AI Studio (~~[PR #381](https://github.com/MindWorkAI/AI-Studio/pull/381), [PR #400](https://github.com/MindWorkAI/AI-Studio/pull/400), [PR #404](https://github.com/MindWorkAI/AI-Studio/pull/404), [PR #429](https://github.com/MindWorkAI/AI-Studio/pull/429))~~
- [x] ~~Add an I18N assistant to translate all AI Studio texts to a certain language & culture ([PR #422](https://github.com/MindWorkAI/AI-Studio/pull/422))~~
- [ ] Provide MindWork AI Studio in German ([#31](https://github.com/MindWorkAI/Planning/issues/31))
- [ ] Add configuration plugins, which allow pre-defining some LLM providers in organizations
Expand Down
11 changes: 10 additions & 1 deletion app/Build/Commands/CollectI18NKeysCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -146,14 +146,23 @@ private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent)
var content = fileContent;
while (startIdx > -1)
{
//
// In some cases, after the initial " there follow more " characters.
// We need to skip them:
//
content = content[(startIdx + START_TAG.Length)..];
while(content[0] == '"')
content = content[1..];

var endIdx = content.IndexOf(END_TAG);
if (endIdx == -1)
break;

var match = content[..endIdx];
matches.Add(match.ToString());
while (match[^1] == '"')
match = match[..^1];

matches.Add(match.ToString());
startIdx = content.IndexOf(START_TAG);
}

Expand Down
42 changes: 4 additions & 38 deletions app/MindWork AI Studio/Assistants/AssistantBase.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@

namespace AIStudio.Assistants;

public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMessageBusReceiver, IDisposable where TSettings : IComponent
public abstract partial class AssistantBase<TSettings> : AssistantLowerBase where TSettings : IComponent
{
[Inject]
protected SettingsManager SettingsManager { get; init; } = null!;

[Inject]
private IDialogService DialogService { get; init; } = null!;

Expand All @@ -42,9 +39,6 @@ public abstract partial class AssistantBase<TSettings> : AssistantLowerBase, IMe
[Inject]
private MudTheme ColorTheme { get; init; } = null!;

[Inject]
private MessageBus MessageBus { get; init; } = null!;

protected abstract string Title { get; }

protected abstract string Description { get; }
Expand Down Expand Up @@ -119,10 +113,6 @@ protected override async Task OnInitializedAsync()
this.MightPreselectValues();
this.providerSettings = this.SettingsManager.GetPreselectedProvider(this.Component);
this.currentProfile = this.SettingsManager.GetPreselectedProfile(this.Component);

this.MessageBus.RegisterComponent(this);
this.MessageBus.ApplyFilters(this, [], [ Event.COLOR_THEME_CHANGED ]);

await base.OnInitializedAsync();
}

Expand All @@ -144,29 +134,6 @@ protected override async Task OnAfterRenderAsync(bool firstRender)
await base.OnAfterRenderAsync(firstRender);
}

#endregion

#region Implementation of IMessageBusReceiver

public string ComponentName => nameof(AssistantBase<TSettings>);

public Task ProcessMessage<T>(ComponentBase? sendingComponent, Event triggeredEvent, T? data)
{
switch (triggeredEvent)
{
case Event.COLOR_THEME_CHANGED:
this.StateHasChanged();
break;
}

return Task.CompletedTask;
}

public Task<TResult?> ProcessMessageWithResult<TPayload, TResult>(ComponentBase? sendingComponent, Event triggeredEvent, TPayload? data)
{
return Task.FromResult<TResult?>(default);
}

#endregion

private string SubmitButtonStyle => this.SettingsManager.ConfigurationData.LLMProviders.ShowProviderConfidence ? this.providerSettings.UsedLLMProvider.GetConfidence(this.SettingsManager).StyleBorder(this.SettingsManager) : string.Empty;
Expand Down Expand Up @@ -226,7 +193,7 @@ protected void CreateChatThread()
SystemPrompt = this.SystemPrompt,
WorkspaceId = Guid.Empty,
ChatId = Guid.NewGuid(),
Name = $"Assistant - {this.Title}",
Name = string.Format(T("Assistant - {0}"), this.Title),
Seed = this.RNG.Next(),
Blocks = [],
};
Expand Down Expand Up @@ -399,11 +366,10 @@ private async Task InnerResetForm()
false => $"background-color: {this.ColorTheme.GetCurrentPalette(this.SettingsManager).InfoLighten}",
};

#region Implementation of IDisposable
#region Overrides of MSGComponentBase

public void Dispose()
protected override void DisposeResources()
{
this.MessageBus.Unregister(this);
this.formChangeTimer.Dispose();
}

Expand Down
4 changes: 2 additions & 2 deletions app/MindWork AI Studio/Assistants/AssistantLowerBase.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using Microsoft.AspNetCore.Components;
using AIStudio.Components;

namespace AIStudio.Assistants;

public abstract class AssistantLowerBase : ComponentBase
public abstract class AssistantLowerBase : MSGComponentBase
{
protected static readonly Dictionary<string, object?> USER_INPUT_ATTRIBUTES = new();

Expand Down
20 changes: 11 additions & 9 deletions app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor
Original file line number Diff line number Diff line change
@@ -1,23 +1,25 @@
@attribute [Route(Routes.ASSISTANT_EMAIL)]
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogWritingEMails>

<MudTextSwitch Label="Is there a history, a previous conversation?" @bind-Value="@this.provideHistory" LabelOn="Yes, I provide the previous conversation" LabelOff="No, I don't provide a previous conversation" />
<MudTextSwitch Label="@T("Is there a history, a previous conversation?")" @bind-Value="@this.provideHistory" LabelOn="@T("Yes, I provide the previous conversation")" LabelOff="@T("No, I don't provide a previous conversation")" />
@if (this.provideHistory)
{
<MudPaper Class="pa-3 mb-8 border-dashed border rounded-lg">
<MudTextField T="string" @bind-Text="@this.inputHistory" Validation="@this.ValidateHistory" Label="Previous conversation" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Provide the previous conversation, e.g., the last e-mail, the last chat, etc." Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DocumentScanner"/>
<MudTextField T="string" @bind-Text="@this.inputHistory" Validation="@this.ValidateHistory" Label="@T("Previous conversation")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Provide the previous conversation, e.g., the last e-mail, the last chat, etc.")" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.DocumentScanner"/>
</MudPaper>
}

<MudTextField T="string" @bind-Text="@this.inputGreeting" Label="(Optional) The greeting phrase to use" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Placeholder="Dear Colleagues" Class="mb-3"/>
<MudTextField T="string" @bind-Text="@this.inputBulletPoints" Validation="@this.ValidateBulletPoints" AdornmentIcon="@Icons.Material.Filled.ListAlt" Adornment="Adornment.Start" Label="Your bullet points" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Bullet list the content of the e-mail roughly. Use dashes (-) to separate the items." Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Placeholder="@PLACEHOLDER_BULLET_POINTS"/>
<MudSelect T="string" Label="(Optional) Are any of your points particularly important?" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
<MudTextField T="string" @bind-Text="@this.inputGreeting" Label="@T("(Optional) The greeting phrase to use")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" Placeholder="@T("Dear Colleagues")" Class="mb-3"/>
<MudTextField T="string" @bind-Text="@this.inputBulletPoints" Validation="@this.ValidateBulletPoints" AdornmentIcon="@Icons.Material.Filled.ListAlt" Adornment="Adornment.Start" Label="@T("Your bullet points")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Bullet list the content of the e-mail roughly. Use dashes (-) to separate the items.")" Immediate="@false" DebounceInterval="1_000" OnDebounceIntervalElapsed="@this.OnContentChanged" Placeholder="@PLACEHOLDER_BULLET_POINTS"/>
<MudSelect T="string" Label="@T("(Optional) Are any of your points particularly important?")" MultiSelection="@true" @bind-SelectedValues="@this.selectedFoci" Variant="Variant.Outlined" Class="mb-3" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.ListAlt">
@foreach (var contentLine in this.bulletPointsLines)
{
<MudSelectItem T="string" Value="@contentLine">@contentLine</MudSelectItem>
<MudSelectItem T="string" Value="@contentLine">
@contentLine
</MudSelectItem>
}
</MudSelect>
<MudTextField T="string" @bind-Text="@this.inputName" Label="(Optional) Your name for the closing salutation" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="Your name for the closing salutation of your e-mail." Class="mb-3"/>
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="Select the writing style" ValidateSelection="@this.ValidateWritingStyle"/>
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Target language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom target language" />
<MudTextField T="string" @bind-Text="@this.inputName" Label="@T("(Optional) Your name for the closing salutation")" Adornment="Adornment.Start" AdornmentIcon="@Icons.Material.Filled.Person" Variant="Variant.Outlined" Margin="Margin.Dense" UserAttributes="@USER_INPUT_ATTRIBUTES" HelperText="@T("Your name for the closing salutation of your e-mail.")" Class="mb-3"/>
<EnumSelection T="WritingStyles" NameFunc="@(style => style.Name())" @bind-Value="@this.selectedWritingStyle" Icon="@Icons.Material.Filled.Edit" Label="@T("Select the writing style")" ValidateSelection="@this.ValidateWritingStyle"/>
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelecting())" @bind-Value="@this.selectedTargetLanguage" ValidateSelection="@this.ValidateTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Target language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom target language")" />
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
21 changes: 9 additions & 12 deletions app/MindWork AI Studio/Assistants/EMail/AssistantEMail.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,9 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa
{
public override Tools.Components Component => Tools.Components.EMAIL_ASSISTANT;

protected override string Title => "E-Mail";
protected override string Title => T("E-Mail");

protected override string Description =>
"""
Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input.
""";
protected override string Description => T("Provide a list of bullet points and some basic information for an e-mail. The assistant will generate an e-mail based on that input.");

protected override string SystemPrompt =>
$"""
Expand All @@ -25,7 +22,7 @@ public partial class AssistantEMail : AssistantBaseCore<SettingsDialogWritingEMa

protected override IReadOnlyList<IButtonData> FooterButtons => [];

protected override string SubmitText => "Create email";
protected override string SubmitText => T("Create email");

protected override Func<Task> SubmitAction => this.CreateMail;

Expand Down Expand Up @@ -100,44 +97,44 @@ protected override async Task OnInitializedAsync()
private string? ValidateBulletPoints(string content)
{
if(string.IsNullOrWhiteSpace(content))
return "Please provide some content for the e-mail.";
return T("Please provide some content for the e-mail.");

var lines = content.Split('\n', StringSplitOptions.RemoveEmptyEntries);
foreach (var line in lines)
if(!line.TrimStart().StartsWith('-'))
return "Please start each line of your content list with a dash (-) to create a bullet point list.";
return T("Please start each line of your content list with a dash (-) to create a bullet point list.");

return null;
}

private string? ValidateTargetLanguage(CommonLanguages language)
{
if(language is CommonLanguages.AS_IS)
return "Please select a target language for the e-mail.";
return T("Please select a target language for the e-mail.");

return null;
}

private string? ValidateCustomLanguage(string language)
{
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
return "Please provide a custom language.";
return T("Please provide a custom language.");

return null;
}

private string? ValidateWritingStyle(WritingStyles style)
{
if(style == WritingStyles.NONE)
return "Please select a writing style for the e-mail.";
return T("Please select a writing style for the e-mail.");

return null;
}

private string? ValidateHistory(string history)
{
if(this.provideHistory && string.IsNullOrWhiteSpace(history))
return "Please provide some history for the e-mail.";
return T("Please provide some history for the e-mail.");

return null;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
@attribute [Route(Routes.ASSISTANT_GRAMMAR_SPELLING)]
@inherits AssistantBaseCore<AIStudio.Dialogs.Settings.SettingsDialogGrammarSpelling>

<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="Your input to check" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="Language" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="Custom language" />
<MudTextField T="string" @bind-Text="@this.inputText" Validation="@this.ValidateText" AdornmentIcon="@Icons.Material.Filled.DocumentScanner" Adornment="Adornment.Start" Label="@T("Your input to check")" Variant="Variant.Outlined" Lines="6" AutoGrow="@true" MaxLines="12" Class="mb-3" UserAttributes="@USER_INPUT_ATTRIBUTES"/>
<EnumSelection T="CommonLanguages" NameFunc="@(language => language.NameSelectingOptional())" @bind-Value="@this.selectedTargetLanguage" Icon="@Icons.Material.Filled.Translate" Label="@T("Language")" AllowOther="@true" OtherValue="CommonLanguages.OTHER" @bind-OtherInput="@this.customTargetLanguage" ValidateOther="@this.ValidateCustomLanguage" LabelOther="@T("Custom language")" />
<ProviderSelection @bind-ProviderSettings="@this.providerSettings" ValidateProvider="@this.ValidatingProvider"/>
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,9 @@ public partial class AssistantGrammarSpelling : AssistantBaseCore<SettingsDialog
{
public override Tools.Components Component => Tools.Components.GRAMMAR_SPELLING_ASSISTANT;

protected override string Title => "Grammar & Spelling Checker";
protected override string Title => T("Grammar & Spelling Checker");

protected override string Description =>
"""
Check the grammar and spelling of a text.
""";
protected override string Description => T("Check the grammar and spelling of a text.");

protected override string SystemPrompt =>
$"""
Expand Down Expand Up @@ -40,7 +37,7 @@ Germany and German in Austria differ. You receive text as input. You check the s
},
];

protected override string SubmitText => "Proofread";
protected override string SubmitText => T("Proofread");

protected override Func<Task> SubmitAction => this.ProofreadText;

Expand Down Expand Up @@ -93,15 +90,15 @@ protected override async Task OnInitializedAsync()
private string? ValidateText(string text)
{
if(string.IsNullOrWhiteSpace(text))
return "Please provide a text as input. You might copy the desired text from a document or a website.";
return T("Please provide a text as input. You might copy the desired text from a document or a website.");

return null;
}

private string? ValidateCustomLanguage(string language)
{
if(this.selectedTargetLanguage == CommonLanguages.OTHER && string.IsNullOrWhiteSpace(language))
return "Please provide a custom language.";
return T("Please provide a custom language.");

return null;
}
Expand Down
5 changes: 5 additions & 0 deletions app/MindWork AI Studio/Assistants/I18N/AssistantI18N.razor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -351,5 +351,10 @@ private void Phase2CreateLuaCode()
this.finalLuaCode.Clear();
var commentContent = this.addedContent.Concat(PluginFactory.BaseLanguage.Content).ToDictionary();
LuaTable.Create(ref this.finalLuaCode, "UI_TEXT_CONTENT", this.localizedContent, commentContent, this.cancellationTokenSource!.Token);

// Next, we must remove the `root::` prefix from the keys:
this.finalLuaCode.Replace("""UI_TEXT_CONTENT["root::""", """
UI_TEXT_CONTENT["
""");
}
}
Loading