mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-04-27 15:39:47 +00:00
Read the user's preferred language from the OS (#382)
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (linux-arm64) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
Some checks are pending
Build and Release / Read metadata (push) Waiting to run
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-apple-darwin, osx-arm64, macos-latest, aarch64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-aarch64-pc-windows-msvc.exe, win-arm64, windows-latest, aarch64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-apple-darwin, osx-x64, macos-latest, x86_64-apple-darwin, dmg updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-pc-windows-msvc.exe, win-x64, windows-latest, x86_64-pc-windows-msvc, nsis updater) (push) Blocked by required conditions
Build and Release / Build app (${{ matrix.dotnet_runtime }}) (-x86_64-unknown-linux-gnu, linux-x64, ubuntu-22.04, x86_64-unknown-linux-gnu, appimage deb updater) (push) Blocked by required conditions
Build and Release / Build app (linux-arm64) (push) Blocked by required conditions
Build and Release / Prepare & create release (push) Blocked by required conditions
Build and Release / Publish release (push) Blocked by required conditions
This commit is contained in:
parent
ceefc0114b
commit
b456319434
@ -79,6 +79,12 @@ public partial class MainLayout : LayoutComponentBase, IMessageBusReceiver, IDis
|
|||||||
SettingsManager.DataDirectory = dataDir;
|
SettingsManager.DataDirectory = dataDir;
|
||||||
Directory.CreateDirectory(SettingsManager.DataDirectory);
|
Directory.CreateDirectory(SettingsManager.DataDirectory);
|
||||||
|
|
||||||
|
//
|
||||||
|
// Read the user language from Rust:
|
||||||
|
//
|
||||||
|
var userLanguage = await this.RustService.ReadUserLanguage();
|
||||||
|
this.Logger.LogInformation($"The user language is: '{userLanguage}'");
|
||||||
|
|
||||||
// Ensure that all settings are loaded:
|
// Ensure that all settings are loaded:
|
||||||
await this.SettingsManager.LoadSettings();
|
await this.SettingsManager.LoadSettings();
|
||||||
|
|
||||||
|
@ -113,6 +113,7 @@
|
|||||||
<ThirdPartyComponent Name="file-format" Developer="Mickaël Malécot & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mmalecot/file-format/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/mmalecot/file-format" UseCase="This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."/>
|
<ThirdPartyComponent Name="file-format" Developer="Mickaël Malécot & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mmalecot/file-format/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/mmalecot/file-format" UseCase="This library is used to determine the file type of a file. This is necessary, e.g., when we want to stream a file."/>
|
||||||
<ThirdPartyComponent Name="calamine" Developer="Johann Tuffe, Joel Natividad, Eric Jolibois, Dmitriy & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tafia/calamine/blob/master/LICENSE-MIT.md" RepositoryUrl="https://github.com/tafia/calamine" UseCase="This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."/>
|
<ThirdPartyComponent Name="calamine" Developer="Johann Tuffe, Joel Natividad, Eric Jolibois, Dmitriy & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/tafia/calamine/blob/master/LICENSE-MIT.md" RepositoryUrl="https://github.com/tafia/calamine" UseCase="This library is used to read Excel and OpenDocument spreadsheet files. This is necessary, e.g., for using spreadsheets as a data source for a chat."/>
|
||||||
<ThirdPartyComponent Name="pdfium-render" Developer="Alastair Carey, Dorian Rudolph & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/ajrcarey/pdfium-render/blob/master/LICENSE.md" RepositoryUrl="https://github.com/ajrcarey/pdfium-render" UseCase="This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."/>
|
<ThirdPartyComponent Name="pdfium-render" Developer="Alastair Carey, Dorian Rudolph & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/ajrcarey/pdfium-render/blob/master/LICENSE.md" RepositoryUrl="https://github.com/ajrcarey/pdfium-render" UseCase="This library is used to read PDF files. This is necessary, e.g., for using PDFs as a data source for a chat."/>
|
||||||
|
<ThirdPartyComponent Name="sys-locale" Developer="1Password Team, ComplexSpaces & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/1Password/sys-locale/blob/main/LICENSE-MIT" RepositoryUrl="https://github.com/1Password/sys-locale" UseCase="This library is used to determine the language of the operating system. This is necessary to set the language of the user interface."/>
|
||||||
<ThirdPartyComponent Name="Lua-CSharp" Developer="Yusuke Nakada & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/nuskey8/Lua-CSharp/blob/main/LICENSE" RepositoryUrl="https://github.com/nuskey8/Lua-CSharp" UseCase="We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library." />
|
<ThirdPartyComponent Name="Lua-CSharp" Developer="Yusuke Nakada & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/nuskey8/Lua-CSharp/blob/main/LICENSE" RepositoryUrl="https://github.com/nuskey8/Lua-CSharp" UseCase="We use Lua as the language for plugins. Lua-CSharp lets Lua scripts communicate with AI Studio and vice versa. Thank you, Yusuke Nakada, for this great library." />
|
||||||
<ThirdPartyComponent Name="HtmlAgilityPack" Developer="ZZZ Projects & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE" RepositoryUrl="https://github.com/zzzprojects/html-agility-pack" UseCase="We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
<ThirdPartyComponent Name="HtmlAgilityPack" Developer="ZZZ Projects & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/zzzprojects/html-agility-pack/blob/master/LICENSE" RepositoryUrl="https://github.com/zzzprojects/html-agility-pack" UseCase="We use the HtmlAgilityPack to extract content from the web. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
||||||
<ThirdPartyComponent Name="ReverseMarkdown" Developer="Babu Annamalai & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE" RepositoryUrl="https://github.com/mysticmind/reversemarkdown-net" UseCase="This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
<ThirdPartyComponent Name="ReverseMarkdown" Developer="Babu Annamalai & Open Source Community" LicenseName="MIT" LicenseUrl="https://github.com/mysticmind/reversemarkdown-net/blob/master/LICENSE" RepositoryUrl="https://github.com/mysticmind/reversemarkdown-net" UseCase="This library is used to convert HTML to Markdown. This is necessary, e.g., when you provide a URL as input for an assistant."/>
|
||||||
|
@ -40,6 +40,10 @@ IS_MAINTAINED = true
|
|||||||
-- When the plugin is deprecated, this message will be shown to users:
|
-- When the plugin is deprecated, this message will be shown to users:
|
||||||
DEPRECATION_MESSAGE = ""
|
DEPRECATION_MESSAGE = ""
|
||||||
|
|
||||||
|
-- The IETF BCP 47 tag for the language. It's the ISO 639 language
|
||||||
|
-- code followed by the ISO 3166-1 country code:
|
||||||
|
IETF_TAG = "de-DE"
|
||||||
|
|
||||||
UI_TEXT_CONTENT = {
|
UI_TEXT_CONTENT = {
|
||||||
HOME = CONTENT_HOME,
|
HOME = CONTENT_HOME,
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,10 @@ IS_MAINTAINED = true
|
|||||||
-- When the plugin is deprecated, this message will be shown to users:
|
-- When the plugin is deprecated, this message will be shown to users:
|
||||||
DEPRECATION_MESSAGE = ""
|
DEPRECATION_MESSAGE = ""
|
||||||
|
|
||||||
|
-- The IETF BCP 47 tag for the language. It's the ISO 639 language
|
||||||
|
-- code followed by the ISO 3166-1 country code:
|
||||||
|
IETF_TAG = "en-US"
|
||||||
|
|
||||||
UI_TEXT_CONTENT = {
|
UI_TEXT_CONTENT = {
|
||||||
HOME = CONTENT_HOME,
|
HOME = CONTENT_HOME,
|
||||||
}
|
}
|
||||||
|
@ -6,12 +6,16 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
|||||||
{
|
{
|
||||||
private readonly Dictionary<string, string> content = [];
|
private readonly Dictionary<string, string> content = [];
|
||||||
private readonly List<ILanguagePlugin> otherLanguagePlugins = [];
|
private readonly List<ILanguagePlugin> otherLanguagePlugins = [];
|
||||||
|
private readonly string langCultureTag;
|
||||||
|
|
||||||
private ILanguagePlugin? baseLanguage;
|
private ILanguagePlugin? baseLanguage;
|
||||||
|
|
||||||
public PluginLanguage(bool isInternal, LuaState state, PluginType type) : base(isInternal, state, type)
|
public PluginLanguage(bool isInternal, LuaState state, PluginType type) : base(isInternal, state, type)
|
||||||
{
|
{
|
||||||
if (this.TryInitUITextContent(out var issue, out var readContent))
|
if(!this.TryInitIETFTag(out var issue, out this.langCultureTag))
|
||||||
|
this.pluginIssues.Add(issue);
|
||||||
|
|
||||||
|
if (this.TryInitUITextContent(out issue, out var readContent))
|
||||||
this.content = readContent;
|
this.content = readContent;
|
||||||
else
|
else
|
||||||
this.pluginIssues.Add(issue);
|
this.pluginIssues.Add(issue);
|
||||||
@ -65,4 +69,62 @@ public sealed class PluginLanguage : PluginBase, ILanguagePlugin
|
|||||||
value = string.Empty;
|
value = string.Empty;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to initialize the IETF tag.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="message">The error message, when the IETF tag could not be read.</param>
|
||||||
|
/// <param name="readLangCultureTag">The read IETF tag.</param>
|
||||||
|
/// <returns>True, when the IETF tag could be read, false otherwise.</returns>
|
||||||
|
private bool TryInitIETFTag(out string message, out string readLangCultureTag)
|
||||||
|
{
|
||||||
|
if (!this.state.Environment["IETF_TAG"].TryRead(out readLangCultureTag))
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG does not exist or is not a valid string.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(readLangCultureTag))
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG is empty. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readLangCultureTag.Length != 5)
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readLangCultureTag[2] != '-')
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the first part consists of only lower case letters:
|
||||||
|
for (var i = 0; i < 2; i++)
|
||||||
|
if (!char.IsLower(readLangCultureTag[i]))
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check the second part consists of only upper case letters:
|
||||||
|
for (var i = 3; i < 5; i++)
|
||||||
|
if (!char.IsUpper(readLangCultureTag[i]))
|
||||||
|
{
|
||||||
|
message = "The field IETF_TAG is not a valid IETF tag. Use a valid IETF tag like 'en-US'. The first part is the language, the second part is the country code.";
|
||||||
|
readLangCultureTag = string.Empty;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
message = string.Empty;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
16
app/MindWork AI Studio/Tools/Services/RustService.OS.cs
Normal file
16
app/MindWork AI Studio/Tools/Services/RustService.OS.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace AIStudio.Tools.Services;
|
||||||
|
|
||||||
|
public sealed partial class RustService
|
||||||
|
{
|
||||||
|
public async Task<string> ReadUserLanguage()
|
||||||
|
{
|
||||||
|
var response = await this.http.GetAsync("/system/language");
|
||||||
|
if (!response.IsSuccessStatusCode)
|
||||||
|
{
|
||||||
|
this.logger!.LogError($"Failed to read the user language from Rust: '{response.StatusCode}'");
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
return await response.Content.ReadAsStringAsync();
|
||||||
|
}
|
||||||
|
}
|
10
runtime/Cargo.lock
generated
10
runtime/Cargo.lock
generated
@ -2684,6 +2684,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
|
"sys-locale",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-build",
|
"tauri-build",
|
||||||
"tauri-plugin-window-state",
|
"tauri-plugin-window-state",
|
||||||
@ -4759,6 +4760,15 @@ dependencies = [
|
|||||||
"syn 2.0.93",
|
"syn 2.0.93",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "sys-locale"
|
||||||
|
version = "0.3.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "system-configuration"
|
name = "system-configuration"
|
||||||
version = "0.5.1"
|
version = "0.5.1"
|
||||||
|
@ -36,6 +36,7 @@ rcgen = { version = "0.13.2", features = ["pem"] }
|
|||||||
file-format = "0.26.0"
|
file-format = "0.26.0"
|
||||||
calamine = "0.26.1"
|
calamine = "0.26.1"
|
||||||
pdfium-render = "0.8.29"
|
pdfium-render = "0.8.29"
|
||||||
|
sys-locale = "0.3.2"
|
||||||
|
|
||||||
# Fixes security vulnerability downstream, where the upstream is not fixed yet:
|
# Fixes security vulnerability downstream, where the upstream is not fixed yet:
|
||||||
url = "2.5"
|
url = "2.5"
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
use rocket::get;
|
use rocket::get;
|
||||||
|
use sys_locale::get_locale;
|
||||||
use crate::api_token::APIToken;
|
use crate::api_token::APIToken;
|
||||||
|
|
||||||
/// The data directory where the application stores its data.
|
/// The data directory where the application stores its data.
|
||||||
@ -34,4 +35,12 @@ pub fn is_dev() -> bool {
|
|||||||
/// Returns true if the application is running in production mode.
|
/// Returns true if the application is running in production mode.
|
||||||
pub fn is_prod() -> bool {
|
pub fn is_prod() -> bool {
|
||||||
!is_dev()
|
!is_dev()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[get("/system/language")]
|
||||||
|
pub fn read_user_language(_token: APIToken) -> String {
|
||||||
|
get_locale().unwrap_or_else(|| {
|
||||||
|
log::warn!("Could not determine the system language. Use default 'en-US'.");
|
||||||
|
String::from("en-US")
|
||||||
|
})
|
||||||
}
|
}
|
@ -77,6 +77,7 @@ pub fn start_runtime_api() {
|
|||||||
crate::secret::delete_secret,
|
crate::secret::delete_secret,
|
||||||
crate::environment::get_data_directory,
|
crate::environment::get_data_directory,
|
||||||
crate::environment::get_config_directory,
|
crate::environment::get_config_directory,
|
||||||
|
crate::environment::read_user_language,
|
||||||
crate::file_data::extract_data,
|
crate::file_data::extract_data,
|
||||||
crate::log::get_log_paths,
|
crate::log::get_log_paths,
|
||||||
])
|
])
|
||||||
|
Loading…
Reference in New Issue
Block a user