mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-05 01:07:21 +00:00
Fixed and guarded the I18N key collection against missing keys (#936)
This commit is contained in:
1 parent
415ed35422
commit
99089f853e
11 files changed
+160
-30
No files matched your search
@@ -22,9 +22,13 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
T(@"
|
T(@"
|
||||||
""";
|
""";
|
||||||
|
|
||||||
private const string END_TAG = """
|
private const string END_TAG1 = """
|
||||||
")
|
")
|
||||||
""";
|
""";
|
||||||
|
|
||||||
|
private const string END_TAG2 = """
|
||||||
|
",
|
||||||
|
""";
|
||||||
|
|
||||||
private static readonly (string Tag, int Length)[] START_TAGS =
|
private static readonly (string Tag, int Length)[] START_TAGS =
|
||||||
[
|
[
|
||||||
@@ -33,6 +37,12 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
(START_TAG3, START_TAG3.Length)
|
(START_TAG3, START_TAG3.Length)
|
||||||
];
|
];
|
||||||
|
|
||||||
|
private static readonly string[] END_TAGS =
|
||||||
|
[
|
||||||
|
END_TAG1,
|
||||||
|
END_TAG2
|
||||||
|
];
|
||||||
|
|
||||||
[Command("collect-i18n", Description = "Collect I18N keys")]
|
[Command("collect-i18n", Description = "Collect I18N keys")]
|
||||||
public async Task CollectI18NKeys()
|
public async Task CollectI18NKeys()
|
||||||
{
|
{
|
||||||
@@ -49,6 +59,7 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
var allFiles = Directory.EnumerateFiles(cwd, "*", SearchOption.AllDirectories);
|
var allFiles = Directory.EnumerateFiles(cwd, "*", SearchOption.AllDirectories);
|
||||||
var counter = 0;
|
var counter = 0;
|
||||||
|
|
||||||
|
var warnings = new List<string>();
|
||||||
var allI18NContent = new Dictionary<string, string>();
|
var allI18NContent = new Dictionary<string, string>();
|
||||||
foreach (var filePath in allFiles)
|
foreach (var filePath in allFiles)
|
||||||
{
|
{
|
||||||
@@ -66,7 +77,7 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var content = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
var content = await File.ReadAllTextAsync(filePath, Encoding.UTF8);
|
||||||
var matches = this.FindAllTextTags(content);
|
var matches = this.FindAllTextTags(content, filePath, warnings);
|
||||||
if (matches.Count == 0)
|
if (matches.Count == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
@@ -89,6 +100,8 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
Console.WriteLine($" {counter:###,###} files processed, {allI18NContent.Count:###,###} keys found.");
|
Console.WriteLine($" {counter:###,###} files processed, {allI18NContent.Count:###,###} keys found.");
|
||||||
|
foreach (var warning in warnings)
|
||||||
|
Console.WriteLine(warning);
|
||||||
|
|
||||||
Console.Write("- Creating Lua code ...");
|
Console.Write("- Creating Lua code ...");
|
||||||
var luaCode = this.ExportToLuaAssignments(allI18NContent);
|
var luaCode = this.ExportToLuaAssignments(allI18NContent);
|
||||||
@@ -163,7 +176,7 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
return sb.ToString();
|
return sb.ToString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent)
|
private List<string> FindAllTextTags(ReadOnlySpan<char> fileContent, string filePath, List<string> warnings)
|
||||||
{
|
{
|
||||||
(int Index, int Len) FindNextStart(ReadOnlySpan<char> content)
|
(int Index, int Len) FindNextStart(ReadOnlySpan<char> content)
|
||||||
{
|
{
|
||||||
@@ -183,6 +196,19 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
return (bestIndex, bestLength);
|
return (bestIndex, bestLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int FindNextEnd(ReadOnlySpan<char> content)
|
||||||
|
{
|
||||||
|
var bestIndex = -1;
|
||||||
|
foreach (var tag in END_TAGS)
|
||||||
|
{
|
||||||
|
var index = content.IndexOf(tag);
|
||||||
|
if (index != -1 && (bestIndex == -1 || index < bestIndex))
|
||||||
|
bestIndex = index;
|
||||||
|
}
|
||||||
|
|
||||||
|
return bestIndex;
|
||||||
|
}
|
||||||
|
|
||||||
var matches = new List<string>();
|
var matches = new List<string>();
|
||||||
var startIdx = FindNextStart(fileContent);
|
var startIdx = FindNextStart(fileContent);
|
||||||
var content = fileContent;
|
var content = fileContent;
|
||||||
@@ -196,7 +222,7 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
while(content[0] == '"')
|
while(content[0] == '"')
|
||||||
content = content[1..];
|
content = content[1..];
|
||||||
|
|
||||||
var endIdx = content.IndexOf(END_TAG);
|
var endIdx = FindNextEnd(content);
|
||||||
if (endIdx == -1)
|
if (endIdx == -1)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -204,7 +230,18 @@ public sealed partial class CollectI18NKeysCommand
|
|||||||
while (match[^1] == '"')
|
while (match[^1] == '"')
|
||||||
match = match[..^1];
|
match = match[..^1];
|
||||||
|
|
||||||
matches.Add(match.ToString());
|
var text = match.ToString();
|
||||||
|
|
||||||
|
//
|
||||||
|
// We read the raw source text, whereas the app hashes the unescaped string at
|
||||||
|
// runtime. Thus, any escape sequence makes both hashes differ, so that the text
|
||||||
|
// never finds its translation. Since we cannot detect this at runtime, we warn
|
||||||
|
// about it here:
|
||||||
|
//
|
||||||
|
if(text.Contains('\\'))
|
||||||
|
warnings.Add($"- Warning: The text '{text}' in the file '{filePath}' contains an escape sequence. Its key does not match the key the app looks up at runtime, so this text stays untranslated. Please use a raw string literal instead.");
|
||||||
|
|
||||||
|
matches.Add(text);
|
||||||
startIdx = FindNextStart(content);
|
startIdx = FindNextStart(content);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6238,6 +6238,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
|
|||||||
-- Additional API parameters
|
-- Additional API parameters
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
||||||
|
|
||||||
|
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2765821959"] = "Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
||||||
|
|
||||||
-- No models loaded or available.
|
-- No models loaded or available.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
||||||
|
|
||||||
@@ -6271,9 +6274,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert
|
|||||||
-- Audio input
|
-- Audio input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audio input"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audio input"
|
||||||
|
|
||||||
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Invalid JSON: Add the parameters in proper JSON formatting, e.g., \\\"temperature\\\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
|
||||||
|
|
||||||
-- Reasoning (thinking) behavior
|
-- Reasoning (thinking) behavior
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Reasoning (thinking) behavior"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Reasoning (thinking) behavior"
|
||||||
|
|
||||||
@@ -8851,6 +8851,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins"
|
|||||||
-- Edit Assistant Plugin
|
-- Edit Assistant Plugin
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Edit Assistant Plugin"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Edit Assistant Plugin"
|
||||||
|
|
||||||
|
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2608443050"] = "The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"
|
||||||
|
|
||||||
-- Enabled Plugins
|
-- Enabled Plugins
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins"
|
||||||
|
|
||||||
@@ -8905,9 +8908,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4157246824"] = "The assistant plugin
|
|||||||
-- Open website
|
-- Open website
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Open website"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Open website"
|
||||||
|
|
||||||
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T448946658"] = "The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level \\\"{2}\\\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"
|
|
||||||
|
|
||||||
-- The plugin archive was exported to '{0}'.
|
-- The plugin archive was exported to '{0}'.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "The plugin archive was exported to '{0}'."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "The plugin archive was exported to '{0}'."
|
||||||
|
|
||||||
@@ -10567,9 +10567,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T1204510649"]
|
|||||||
-- The global shortcut could not be registered. The previous shortcut remains active.
|
-- The global shortcut could not be registered. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "The global shortcut could not be registered. The previous shortcut remains active."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "The global shortcut could not be registered. The previous shortcut remains active."
|
||||||
|
|
||||||
|
-- Global shortcut
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2637055764"] = "Global shortcut"
|
||||||
|
|
||||||
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "The global shortcut change was cancelled. The previous shortcut remains active."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "The global shortcut change was cancelled. The previous shortcut remains active."
|
||||||
|
|
||||||
|
-- Toggle voice recording
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T40517664"] = "Toggle voice recording"
|
||||||
|
|
||||||
-- The configured transcription provider could not be created.
|
-- The configured transcription provider could not be created.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created."
|
||||||
|
|
||||||
|
|||||||
@@ -631,7 +631,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
|||||||
}
|
}
|
||||||
catch (JsonException)
|
catch (JsonException)
|
||||||
{
|
{
|
||||||
return T("Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.");
|
return T("""Invalid JSON: Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -764,7 +764,7 @@ public partial class ProviderDialog : MSGComponentBase, ISecretId
|
|||||||
|
|
||||||
if (objectStack.Count != 0)
|
if (objectStack.Count != 0)
|
||||||
{
|
{
|
||||||
errorMessage = T("Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.");
|
errorMessage = T("""Invalid JSON: Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.""");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -175,7 +175,7 @@ public partial class Plugins : MSGComponentBase
|
|||||||
{
|
{
|
||||||
x => x.Message,
|
x => x.Message,
|
||||||
string.Format(
|
string.Format(
|
||||||
this.T("The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level \"{2}\". Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"),
|
this.T("The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"),
|
||||||
pluginName,
|
pluginName,
|
||||||
actualLevel.GetName(),
|
actualLevel.GetName(),
|
||||||
this.AssistantPluginAuditSettings.MinimumLevel.GetName())
|
this.AssistantPluginAuditSettings.MinimumLevel.GetName())
|
||||||
|
|||||||
+12
-6
@@ -6240,6 +6240,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Hinzufügen
|
|||||||
-- Additional API parameters
|
-- Additional API parameters
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Zusätzliche API-Parameter"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Zusätzliche API-Parameter"
|
||||||
|
|
||||||
|
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2765821959"] = "Ungültiges JSON: Fügen Sie die Parameter in korrektem JSON-Format hinzu, z. B. "temperature": 0.5. Entfernen Sie abschließende Kommas. Die üblichen umgebenden geschweiften Klammern {} dürfen jedoch nicht verwendet werden."
|
||||||
|
|
||||||
-- No models loaded or available.
|
-- No models loaded or available.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "Keine Modelle geladen oder verfügbar."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "Keine Modelle geladen oder verfügbar."
|
||||||
|
|
||||||
@@ -6273,9 +6276,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Experten-Ei
|
|||||||
-- Audio input
|
-- Audio input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audioeingabe"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audioeingabe"
|
||||||
|
|
||||||
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Ungültiges JSON: Fügen Sie die Parameter in korrektem JSON-Format hinzu, z. B. \"temperature\": 0.5. Entfernen Sie abschließende Kommas. Die üblichen umgebenden geschweiften Klammern {} dürfen jedoch nicht verwendet werden."
|
|
||||||
|
|
||||||
-- Reasoning (thinking) behavior
|
-- Reasoning (thinking) behavior
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Verhalten bezüglich Schlussfolgerungen (Denken)"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Verhalten bezüglich Schlussfolgerungen (Denken)"
|
||||||
|
|
||||||
@@ -8853,6 +8853,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins"
|
|||||||
-- Edit Assistant Plugin
|
-- Edit Assistant Plugin
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Plugin für „Assistent bearbeiten“"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Plugin für „Assistent bearbeiten“"
|
||||||
|
|
||||||
|
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2608443050"] = "Das Assistenten-Plugin „{0}“ wurde mit der Stufe „{1}“ geprüft, die unter der erforderlichen Mindeststufe „{2}“ liegt. Ihre aktuellen Einstellungen erlauben die Aktivierung dennoch, dies kann jedoch potenziell gefährlich sein. Möchten Sie dieses Plugin wirklich aktivieren?"
|
||||||
|
|
||||||
-- Enabled Plugins
|
-- Enabled Plugins
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Aktivierte Plugins"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Aktivierte Plugins"
|
||||||
|
|
||||||
@@ -8907,9 +8910,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4157246824"] = "Das Assistenten-Plug
|
|||||||
-- Open website
|
-- Open website
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Website öffnen"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Website öffnen"
|
||||||
|
|
||||||
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T448946658"] = "Das Assistenten-Plugin „{0}“ wurde mit der Stufe „{1}“ geprüft, die unter der erforderlichen Mindeststufe „{2}“ liegt. Ihre aktuellen Einstellungen erlauben die Aktivierung dennoch, dies kann jedoch potenziell gefährlich sein. Möchten Sie dieses Plugin wirklich aktivieren?"
|
|
||||||
|
|
||||||
-- The plugin archive was exported to '{0}'.
|
-- The plugin archive was exported to '{0}'.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "Das Plugin-Archiv wurde nach „{0}“ exportiert."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "Das Plugin-Archiv wurde nach „{0}“ exportiert."
|
||||||
|
|
||||||
@@ -10569,9 +10569,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T1204510649"]
|
|||||||
-- The global shortcut could not be registered. The previous shortcut remains active.
|
-- The global shortcut could not be registered. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "Die globale Tastenkombination konnte nicht registriert werden. Die vorherige Tastenkombination bleibt aktiv."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "Die globale Tastenkombination konnte nicht registriert werden. Die vorherige Tastenkombination bleibt aktiv."
|
||||||
|
|
||||||
|
-- Global shortcut
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2637055764"] = "Globale Tastenkombination"
|
||||||
|
|
||||||
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "Die Änderung der globalen Tastenkombination wurde abgebrochen. Die vorherige Tastenkombination bleibt aktiv."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "Die Änderung der globalen Tastenkombination wurde abgebrochen. Die vorherige Tastenkombination bleibt aktiv."
|
||||||
|
|
||||||
|
-- Toggle voice recording
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T40517664"] = "Sprachaufnahme umschalten"
|
||||||
|
|
||||||
-- The configured transcription provider could not be created.
|
-- The configured transcription provider could not be created.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "Der konfigurierte Transkriptionsanbieter konnte nicht erstellt werden."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "Der konfigurierte Transkriptionsanbieter konnte nicht erstellt werden."
|
||||||
|
|
||||||
|
|||||||
+12
-6
@@ -6240,6 +6240,9 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2646845972"] = "Add"
|
|||||||
-- Additional API parameters
|
-- Additional API parameters
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2728244552"] = "Additional API parameters"
|
||||||
|
|
||||||
|
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., "temperature": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2765821959"] = "Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
||||||
|
|
||||||
-- No models loaded or available.
|
-- No models loaded or available.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T2810182573"] = "No models loaded or available."
|
||||||
|
|
||||||
@@ -6273,9 +6276,6 @@ UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3361153305"] = "Show Expert
|
|||||||
-- Audio input
|
-- Audio input
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audio input"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3404621481"] = "Audio input"
|
||||||
|
|
||||||
-- Invalid JSON: Add the parameters in proper JSON formatting, e.g., \"temperature\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though.
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3502745319"] = "Invalid JSON: Add the parameters in proper JSON formatting, e.g., \\\"temperature\\\": 0.5. Remove trailing commas. The usual surrounding curly brackets {} must not be used, though."
|
|
||||||
|
|
||||||
-- Reasoning (thinking) behavior
|
-- Reasoning (thinking) behavior
|
||||||
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Reasoning (thinking) behavior"
|
UI_TEXT_CONTENT["AISTUDIO::DIALOGS::PROVIDERDIALOG::T3546126752"] = "Reasoning (thinking) behavior"
|
||||||
|
|
||||||
@@ -8853,6 +8853,9 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2222816203"] = "Plugins"
|
|||||||
-- Edit Assistant Plugin
|
-- Edit Assistant Plugin
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Edit Assistant Plugin"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2477579768"] = "Edit Assistant Plugin"
|
||||||
|
|
||||||
|
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2608443050"] = "The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"
|
||||||
|
|
||||||
-- Enabled Plugins
|
-- Enabled Plugins
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T2738444034"] = "Enabled Plugins"
|
||||||
|
|
||||||
@@ -8907,9 +8910,6 @@ UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4157246824"] = "The assistant plugin
|
|||||||
-- Open website
|
-- Open website
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Open website"
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T4239378936"] = "Open website"
|
||||||
|
|
||||||
-- The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?
|
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T448946658"] = "The assistant plugin '{0}' was audited with the level '{1}', which is below the required minimum level '{2}'. Your current settings allow activation anyway, but this may be potentially dangerous. Do you really want to enable this plugin?"
|
|
||||||
|
|
||||||
-- The plugin archive was exported to '{0}'.
|
-- The plugin archive was exported to '{0}'.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "The plugin archive was exported to '{0}'."
|
UI_TEXT_CONTENT["AISTUDIO::PAGES::PLUGINS::T659549952"] = "The plugin archive was exported to '{0}'."
|
||||||
|
|
||||||
@@ -10569,9 +10569,15 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T1204510649"]
|
|||||||
-- The global shortcut could not be registered. The previous shortcut remains active.
|
-- The global shortcut could not be registered. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "The global shortcut could not be registered. The previous shortcut remains active."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2266307101"] = "The global shortcut could not be registered. The previous shortcut remains active."
|
||||||
|
|
||||||
|
-- Global shortcut
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T2637055764"] = "Global shortcut"
|
||||||
|
|
||||||
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
-- The global shortcut change was cancelled. The previous shortcut remains active.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "The global shortcut change was cancelled. The previous shortcut remains active."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T3299913860"] = "The global shortcut change was cancelled. The previous shortcut remains active."
|
||||||
|
|
||||||
|
-- Toggle voice recording
|
||||||
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::GLOBALSHORTCUTSERVICE::T40517664"] = "Toggle voice recording"
|
||||||
|
|
||||||
-- The configured transcription provider could not be created.
|
-- The configured transcription provider could not be created.
|
||||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created."
|
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::MEDIATRANSCRIPTIONSERVICE::T1235984176"] = "The configured transcription provider could not be created."
|
||||||
|
|
||||||
|
|||||||
@@ -269,11 +269,14 @@ public sealed class GlobalShortcutService : BackgroundService, IMessageBusReceiv
|
|||||||
|
|
||||||
private async Task<string> GetShortcutDescription(Shortcut shortcutId)
|
private async Task<string> GetShortcutDescription(Shortcut shortcutId)
|
||||||
{
|
{
|
||||||
|
// Message bus receivers run without being awaited, so I18N.Init in MainLayout may still be
|
||||||
|
// pending during startup or a plugin reload. Resolve the active language directly to avoid
|
||||||
|
// using no language or the previously active language for the shortcut description:
|
||||||
var language = await this.settingsManager.GetActiveLanguagePlugin();
|
var language = await this.settingsManager.GetActiveLanguagePlugin();
|
||||||
return shortcutId switch
|
return shortcutId switch
|
||||||
{
|
{
|
||||||
Shortcut.VOICE_RECORDING_TOGGLE => I18N.I.GetText(language, "Toggle voice recording", typeof(GlobalShortcutService).Namespace, nameof(GlobalShortcutService)),
|
Shortcut.VOICE_RECORDING_TOGGLE => TB("Toggle voice recording", language),
|
||||||
_ => I18N.I.GetText(language, "Global shortcut", typeof(GlobalShortcutService).Namespace, nameof(GlobalShortcutService)),
|
_ => TB("Global shortcut", language),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -320,6 +323,8 @@ public sealed class GlobalShortcutService : BackgroundService, IMessageBusReceiv
|
|||||||
|
|
||||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(GlobalShortcutService).Namespace, nameof(GlobalShortcutService));
|
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(GlobalShortcutService).Namespace, nameof(GlobalShortcutService));
|
||||||
|
|
||||||
|
private static string TB(string fallbackEN, ILanguagePlugin language) => I18N.I.GetText(language, fallbackEN, typeof(GlobalShortcutService).Namespace, nameof(GlobalShortcutService));
|
||||||
|
|
||||||
private async Task<ShortcutState> GetShortcutState(Shortcut shortcutId, ShortcutSyncSource source)
|
private async Task<ShortcutState> GetShortcutState(Shortcut shortcutId, ShortcutSyncSource source)
|
||||||
{
|
{
|
||||||
var shortcut = this.GetShortcutValue(shortcutId);
|
var shortcut = this.GetShortcutValue(shortcutId);
|
||||||
|
|||||||
@@ -8,3 +8,4 @@
|
|||||||
- Fixed AI Studio holding on to finished chats, presentation images, and plugin data. It releases them now, so memory no longer grows the longer you keep the app running.
|
- Fixed AI Studio holding on to finished chats, presentation images, and plugin data. It releases them now, so memory no longer grows the longer you keep the app running.
|
||||||
- Fixed assistants handing an outdated result to the chat. When you sent several results without opening the chat in between, you now receive the one you sent last.
|
- Fixed assistants handing an outdated result to the chat. When you sent several results without opening the chat in between, you now receive the one you sent last.
|
||||||
- Fixed rare issues when multiple configurations provided introduction texts or mandatory information under the same ID.
|
- Fixed rare issues when multiple configurations provided introduction texts or mandatory information under the same ID.
|
||||||
|
- Fixed the description of the global voice recording shortcut always appearing in English. On Linux, your desktop asks you to confirm such a shortcut and shows this description; it now appears in your language.
|
||||||
@@ -15,3 +15,4 @@
|
|||||||
MWAIS0009 | Usage | Error | StaticServiceProviderCacheAnalyzer
|
MWAIS0009 | Usage | Error | StaticServiceProviderCacheAnalyzer
|
||||||
MWAIS0010 | Usage | Error | CanonicalJsonConfigurationAnalyzer
|
MWAIS0010 | Usage | Error | CanonicalJsonConfigurationAnalyzer
|
||||||
MWAIS0011 | Usage | Error | CanonicalJsonShapeAnalyzer
|
MWAIS0011 | Usage | Error | CanonicalJsonShapeAnalyzer
|
||||||
|
MWAIS0012 | Usage | Error | DirectI18NGetTextAnalyzer
|
||||||
@@ -13,4 +13,5 @@ public static class Identifier
|
|||||||
public const string STATIC_SERVICE_PROVIDER_CACHE_ANALYZER = $"{Tools.ID_PREFIX}0009";
|
public const string STATIC_SERVICE_PROVIDER_CACHE_ANALYZER = $"{Tools.ID_PREFIX}0009";
|
||||||
public const string CANONICAL_JSON_CONFIGURATION_ANALYZER = $"{Tools.ID_PREFIX}0010";
|
public const string CANONICAL_JSON_CONFIGURATION_ANALYZER = $"{Tools.ID_PREFIX}0010";
|
||||||
public const string CANONICAL_JSON_SHAPE_ANALYZER = $"{Tools.ID_PREFIX}0011";
|
public const string CANONICAL_JSON_SHAPE_ANALYZER = $"{Tools.ID_PREFIX}0011";
|
||||||
|
public const string DIRECT_I18N_GET_TEXT_ANALYZER = $"{Tools.ID_PREFIX}0012";
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
using Microsoft.CodeAnalysis;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp;
|
||||||
|
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||||
|
using Microsoft.CodeAnalysis.Diagnostics;
|
||||||
|
using Microsoft.CodeAnalysis.Operations;
|
||||||
|
|
||||||
|
namespace SourceCodeRules.UsageAnalyzers;
|
||||||
|
|
||||||
|
#pragma warning disable RS1038
|
||||||
|
[DiagnosticAnalyzer(LanguageNames.CSharp)]
|
||||||
|
#pragma warning restore RS1038
|
||||||
|
public sealed class DirectI18NGetTextAnalyzer : DiagnosticAnalyzer
|
||||||
|
{
|
||||||
|
private const string DIAGNOSTIC_ID = Identifier.DIRECT_I18N_GET_TEXT_ANALYZER;
|
||||||
|
|
||||||
|
private const string TITLE = "Direct translation lookup is not allowed";
|
||||||
|
|
||||||
|
private const string MESSAGE_FORMAT = "Call GetText only from a T or TB wrapper whose first string parameter is forwarded as the fallback text";
|
||||||
|
|
||||||
|
private const string DESCRIPTION = "Translation calls must use collector-compatible T or TB wrappers so that every fallback text is included in the generated I18N resources.";
|
||||||
|
|
||||||
|
private const string CATEGORY = "Usage";
|
||||||
|
|
||||||
|
private static readonly DiagnosticDescriptor RULE = new(DIAGNOSTIC_ID, TITLE, MESSAGE_FORMAT, CATEGORY, DiagnosticSeverity.Error, isEnabledByDefault: true, description: DESCRIPTION);
|
||||||
|
|
||||||
|
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => [RULE];
|
||||||
|
|
||||||
|
public override void Initialize(AnalysisContext context)
|
||||||
|
{
|
||||||
|
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
|
||||||
|
context.EnableConcurrentExecution();
|
||||||
|
context.RegisterSyntaxNodeAction(AnalyzeInvocation, SyntaxKind.InvocationExpression);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void AnalyzeInvocation(SyntaxNodeAnalysisContext context)
|
||||||
|
{
|
||||||
|
var invocation = (InvocationExpressionSyntax)context.Node;
|
||||||
|
if (context.SemanticModel.GetSymbolInfo(invocation).Symbol is not IMethodSymbol method)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var targetMethod = method.ReducedFrom ?? method;
|
||||||
|
if (targetMethod.Name != "GetText" || targetMethod.ContainingType.Name != "ILangExtensions" || targetMethod.ContainingNamespace.ToDisplayString() != "AIStudio.Tools.PluginSystem")
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (context.SemanticModel.GetOperation(invocation) is IInvocationOperation operation
|
||||||
|
&& IsCollectorCompatibleWrapper(context.ContainingSymbol as IMethodSymbol, operation))
|
||||||
|
return;
|
||||||
|
|
||||||
|
context.ReportDiagnostic(Diagnostic.Create(RULE, invocation.GetLocation()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static bool IsCollectorCompatibleWrapper(IMethodSymbol? containingMethod, IInvocationOperation invocation)
|
||||||
|
{
|
||||||
|
if (containingMethod?.Name is not ("T" or "TB")
|
||||||
|
|| containingMethod.ReturnType.SpecialType != SpecialType.System_String
|
||||||
|
|| containingMethod.Parameters.Length == 0
|
||||||
|
|| containingMethod.Parameters[0].Type.SpecialType != SpecialType.System_String)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var fallbackArgument = invocation.Arguments.FirstOrDefault(argument => argument.Parameter?.Name == "fallbackEN");
|
||||||
|
return fallbackArgument?.Value is IParameterReferenceOperation parameterReference
|
||||||
|
&& SymbolEqualityComparer.Default.Equals(parameterReference.Parameter, containingMethod.Parameters[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in new issue
Block a user