消息渠道的消息支持多语言
This commit is contained in:
@@ -3,12 +3,14 @@ use tokio::sync::mpsc;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use super::command_handlers;
|
||||
use super::i18n::{self, Lang};
|
||||
use super::manager::ChatChannelManager;
|
||||
use super::types::IncomingCommand;
|
||||
use crate::db::service::{app_metadata_service, chat_channel_message_log_service};
|
||||
|
||||
const COMMAND_PREFIX_KEY: &str = "chat_command_prefix";
|
||||
const DEFAULT_COMMAND_PREFIX: &str = "/";
|
||||
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
||||
|
||||
pub fn spawn_command_dispatcher(
|
||||
mut command_rx: mpsc::Receiver<IncomingCommand>,
|
||||
@@ -37,7 +39,9 @@ pub fn spawn_command_dispatcher(
|
||||
.flatten()
|
||||
.unwrap_or_else(|| DEFAULT_COMMAND_PREFIX.to_string());
|
||||
|
||||
let response = dispatch_command(text, &prefix, &db_conn, &manager).await;
|
||||
let lang = load_lang(&db_conn).await;
|
||||
|
||||
let response = dispatch_command(text, &prefix, &db_conn, &manager, lang).await;
|
||||
|
||||
// Send response back via the same channel
|
||||
let send_result = manager.send_to_channel(cmd.channel_id, &response).await;
|
||||
@@ -66,15 +70,25 @@ pub fn spawn_command_dispatcher(
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_lang(db: &DatabaseConnection) -> Lang {
|
||||
app_metadata_service::get_value(db, MESSAGE_LANGUAGE_KEY)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|v| Lang::from_str_lossy(&v))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
async fn dispatch_command(
|
||||
text: &str,
|
||||
prefix: &str,
|
||||
db: &DatabaseConnection,
|
||||
manager: &ChatChannelManager,
|
||||
lang: Lang,
|
||||
) -> super::types::RichMessage {
|
||||
// Check if text starts with the configured prefix
|
||||
if !text.starts_with(prefix) {
|
||||
return command_handlers::handle_help(prefix);
|
||||
return command_handlers::handle_help(prefix, lang);
|
||||
}
|
||||
|
||||
// Strip prefix and parse command + args
|
||||
@@ -84,31 +98,27 @@ async fn dispatch_command(
|
||||
let args = parts.get(1).map(|s| s.trim()).unwrap_or("");
|
||||
|
||||
match command.as_str() {
|
||||
"recent" => command_handlers::handle_recent(db).await,
|
||||
"recent" => command_handlers::handle_recent(db, lang).await,
|
||||
"search" => {
|
||||
if args.is_empty() {
|
||||
super::types::RichMessage::info(format!("用法: {prefix}search <关键词>"))
|
||||
.with_title("参数错误")
|
||||
super::types::RichMessage::info(i18n::search_usage(lang, prefix))
|
||||
.with_title(i18n::invalid_args_title(lang))
|
||||
} else {
|
||||
command_handlers::handle_search(db, args).await
|
||||
command_handlers::handle_search(db, args, lang).await
|
||||
}
|
||||
}
|
||||
"detail" => {
|
||||
if let Ok(id) = args.parse::<i32>() {
|
||||
command_handlers::handle_detail(db, id).await
|
||||
command_handlers::handle_detail(db, id, lang).await
|
||||
} else {
|
||||
super::types::RichMessage::info(format!("用法: {prefix}detail <会话ID>"))
|
||||
.with_title("参数错误")
|
||||
super::types::RichMessage::info(i18n::detail_usage(lang, prefix))
|
||||
.with_title(i18n::invalid_args_title(lang))
|
||||
}
|
||||
}
|
||||
"today" => command_handlers::handle_today(db).await,
|
||||
"status" => command_handlers::handle_status(manager).await,
|
||||
"help" | "start" => command_handlers::handle_help(prefix),
|
||||
_ => {
|
||||
super::types::RichMessage::info(format!(
|
||||
"未知命令: {prefix}{command}\n输入 {prefix}help 查看可用命令",
|
||||
))
|
||||
.with_title("未知命令")
|
||||
}
|
||||
"today" => command_handlers::handle_today(db, lang).await,
|
||||
"status" => command_handlers::handle_status(manager, lang).await,
|
||||
"help" | "start" => command_handlers::handle_help(prefix, lang),
|
||||
_ => super::types::RichMessage::info(i18n::unknown_command(lang, prefix, &command))
|
||||
.with_title(i18n::unknown_command_title(lang)),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
use chrono::Utc;
|
||||
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder};
|
||||
|
||||
use super::i18n::{self, Lang};
|
||||
use super::manager::ChatChannelManager;
|
||||
use super::types::{MessageLevel, RichMessage};
|
||||
use crate::db::entities::conversation;
|
||||
|
||||
pub async fn handle_recent(db: &DatabaseConnection) -> RichMessage {
|
||||
pub async fn handle_recent(db: &DatabaseConnection, lang: Lang) -> RichMessage {
|
||||
let rows = match conversation::Entity::find()
|
||||
.filter(conversation::Column::DeletedAt.is_null())
|
||||
.order_by_desc(conversation::Column::CreatedAt)
|
||||
@@ -15,7 +16,7 @@ pub async fn handle_recent(db: &DatabaseConnection) -> RichMessage {
|
||||
Ok(rows) => rows,
|
||||
Err(e) => {
|
||||
return RichMessage {
|
||||
title: Some("查询失败".to_string()),
|
||||
title: Some(i18n::query_failed_title(lang).to_string()),
|
||||
body: e.to_string(),
|
||||
fields: Vec::new(),
|
||||
level: MessageLevel::Error,
|
||||
@@ -25,12 +26,13 @@ pub async fn handle_recent(db: &DatabaseConnection) -> RichMessage {
|
||||
|
||||
let recent: Vec<_> = rows.into_iter().take(5).collect();
|
||||
if recent.is_empty() {
|
||||
return RichMessage::info("暂无会话记录").with_title("最近会话");
|
||||
return RichMessage::info(i18n::no_conversations(lang))
|
||||
.with_title(i18n::recent_conversations_title(lang));
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
for (i, conv) in recent.iter().enumerate() {
|
||||
let title = conv.title.as_deref().unwrap_or("(无标题)");
|
||||
let title = conv.title.as_deref().unwrap_or(i18n::untitled(lang));
|
||||
let agent = &conv.agent_type;
|
||||
let time = conv.created_at.format("%m-%d %H:%M");
|
||||
body.push_str(&format!(
|
||||
@@ -42,10 +44,15 @@ pub async fn handle_recent(db: &DatabaseConnection) -> RichMessage {
|
||||
));
|
||||
}
|
||||
|
||||
RichMessage::info(body.trim_end()).with_title("最近 5 条会话")
|
||||
RichMessage::info(body.trim_end())
|
||||
.with_title(i18n::recent_n_conversations_title(lang, recent.len()))
|
||||
}
|
||||
|
||||
pub async fn handle_search(db: &DatabaseConnection, keyword: &str) -> RichMessage {
|
||||
pub async fn handle_search(
|
||||
db: &DatabaseConnection,
|
||||
keyword: &str,
|
||||
lang: Lang,
|
||||
) -> RichMessage {
|
||||
let rows = match conversation::Entity::find()
|
||||
.filter(conversation::Column::DeletedAt.is_null())
|
||||
.order_by_desc(conversation::Column::CreatedAt)
|
||||
@@ -55,7 +62,7 @@ pub async fn handle_search(db: &DatabaseConnection, keyword: &str) -> RichMessag
|
||||
Ok(rows) => rows,
|
||||
Err(e) => {
|
||||
return RichMessage {
|
||||
title: Some("查询失败".to_string()),
|
||||
title: Some(i18n::query_failed_title(lang).to_string()),
|
||||
body: e.to_string(),
|
||||
fields: Vec::new(),
|
||||
level: MessageLevel::Error,
|
||||
@@ -76,22 +83,35 @@ pub async fn handle_search(db: &DatabaseConnection, keyword: &str) -> RichMessag
|
||||
.collect();
|
||||
|
||||
if matched.is_empty() {
|
||||
return RichMessage::info(format!("未找到包含 \"{keyword}\" 的会话"))
|
||||
.with_title("搜索结果");
|
||||
return RichMessage::info(i18n::search_no_results(lang, keyword))
|
||||
.with_title(i18n::search_results_title(lang));
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
for (i, conv) in matched.iter().enumerate() {
|
||||
let title = conv.title.as_deref().unwrap_or("(无标题)");
|
||||
let title = conv.title.as_deref().unwrap_or(i18n::untitled(lang));
|
||||
let agent = &conv.agent_type;
|
||||
body.push_str(&format!("{}. [{}] {} (ID:{})\n", i + 1, agent, title, conv.id));
|
||||
body.push_str(&format!(
|
||||
"{}. [{}] {} (ID:{})\n",
|
||||
i + 1,
|
||||
agent,
|
||||
title,
|
||||
conv.id
|
||||
));
|
||||
}
|
||||
|
||||
RichMessage::info(body.trim_end())
|
||||
.with_title(format!("搜索 \"{}\" - {} 条结果", keyword, matched.len()))
|
||||
RichMessage::info(body.trim_end()).with_title(i18n::search_results_count_title(
|
||||
lang,
|
||||
keyword,
|
||||
matched.len(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn handle_detail(db: &DatabaseConnection, conversation_id: i32) -> RichMessage {
|
||||
pub async fn handle_detail(
|
||||
db: &DatabaseConnection,
|
||||
conversation_id: i32,
|
||||
lang: Lang,
|
||||
) -> RichMessage {
|
||||
let conv = match conversation::Entity::find_by_id(conversation_id)
|
||||
.filter(conversation::Column::DeletedAt.is_null())
|
||||
.one(db)
|
||||
@@ -99,12 +119,12 @@ pub async fn handle_detail(db: &DatabaseConnection, conversation_id: i32) -> Ric
|
||||
{
|
||||
Ok(Some(c)) => c,
|
||||
Ok(None) => {
|
||||
return RichMessage::info(format!("会话 {conversation_id} 不存在"))
|
||||
.with_title("未找到");
|
||||
return RichMessage::info(i18n::conversation_not_found(lang, conversation_id))
|
||||
.with_title(i18n::not_found_title(lang));
|
||||
}
|
||||
Err(e) => {
|
||||
return RichMessage {
|
||||
title: Some("查询失败".to_string()),
|
||||
title: Some(i18n::query_failed_title(lang).to_string()),
|
||||
body: e.to_string(),
|
||||
fields: Vec::new(),
|
||||
level: MessageLevel::Error,
|
||||
@@ -112,16 +132,22 @@ pub async fn handle_detail(db: &DatabaseConnection, conversation_id: i32) -> Ric
|
||||
}
|
||||
};
|
||||
|
||||
let title = conv.title.as_deref().unwrap_or("(无标题)");
|
||||
let title = conv.title.as_deref().unwrap_or(i18n::untitled(lang));
|
||||
RichMessage::info(title)
|
||||
.with_title(format!("会话详情 #{}", conv.id))
|
||||
.with_field("代理", &conv.agent_type)
|
||||
.with_field("状态", format!("{:?}", conv.status))
|
||||
.with_field("消息数", conv.message_count.to_string())
|
||||
.with_field("创建时间", conv.created_at.format("%Y-%m-%d %H:%M").to_string())
|
||||
.with_title(i18n::conversation_detail_title(lang, conv.id))
|
||||
.with_field(i18n::field_agent(lang), &conv.agent_type)
|
||||
.with_field(i18n::field_status(lang), format!("{:?}", conv.status))
|
||||
.with_field(
|
||||
i18n::field_message_count(lang),
|
||||
conv.message_count.to_string(),
|
||||
)
|
||||
.with_field(
|
||||
i18n::field_created_at(lang),
|
||||
conv.created_at.format("%Y-%m-%d %H:%M").to_string(),
|
||||
)
|
||||
}
|
||||
|
||||
pub async fn handle_today(db: &DatabaseConnection) -> RichMessage {
|
||||
pub async fn handle_today(db: &DatabaseConnection, lang: Lang) -> RichMessage {
|
||||
let now = Utc::now();
|
||||
let today_start = now
|
||||
.date_naive()
|
||||
@@ -139,7 +165,7 @@ pub async fn handle_today(db: &DatabaseConnection) -> RichMessage {
|
||||
Ok(rows) => rows,
|
||||
Err(e) => {
|
||||
return RichMessage {
|
||||
title: Some("查询失败".to_string()),
|
||||
title: Some(i18n::query_failed_title(lang).to_string()),
|
||||
body: e.to_string(),
|
||||
fields: Vec::new(),
|
||||
level: MessageLevel::Error,
|
||||
@@ -148,7 +174,8 @@ pub async fn handle_today(db: &DatabaseConnection) -> RichMessage {
|
||||
};
|
||||
|
||||
if rows.is_empty() {
|
||||
return RichMessage::info("今日暂无编码活动").with_title("今日活动");
|
||||
return RichMessage::info(i18n::no_activity_today(lang))
|
||||
.with_title(i18n::today_activity_title(lang));
|
||||
}
|
||||
|
||||
// Group by agent_type
|
||||
@@ -163,29 +190,33 @@ pub async fn handle_today(db: &DatabaseConnection) -> RichMessage {
|
||||
}
|
||||
}
|
||||
|
||||
let mut body = format!("会话总数: {}", rows.len());
|
||||
body.push_str("\n\n按代理:");
|
||||
let mut body = i18n::total_sessions(lang, rows.len() as u32);
|
||||
body.push_str(&format!("\n\n{}", i18n::by_agent_label(lang)));
|
||||
for (agent, count) in &by_agent {
|
||||
body.push_str(&format!("\n {agent} - {count} 个"));
|
||||
body.push_str(&format!(
|
||||
"\n {}",
|
||||
i18n::agent_count(lang, agent, *count)
|
||||
));
|
||||
}
|
||||
|
||||
if !titles.is_empty() {
|
||||
body.push_str("\n\n最近活动:");
|
||||
body.push_str(&format!("\n\n{}", i18n::recent_activity_label(lang)));
|
||||
for t in &titles {
|
||||
body.push_str(&format!("\n • {t}"));
|
||||
}
|
||||
}
|
||||
|
||||
RichMessage::info(body).with_title(format!(
|
||||
"今日活动 ({})",
|
||||
now.format("%Y-%m-%d")
|
||||
RichMessage::info(body).with_title(i18n::today_activity_date_title(
|
||||
lang,
|
||||
&now.format("%Y-%m-%d").to_string(),
|
||||
))
|
||||
}
|
||||
|
||||
pub async fn handle_status(manager: &ChatChannelManager) -> RichMessage {
|
||||
pub async fn handle_status(manager: &ChatChannelManager, lang: Lang) -> RichMessage {
|
||||
let statuses = manager.get_status().await;
|
||||
if statuses.is_empty() {
|
||||
return RichMessage::info("暂无活跃渠道").with_title("渠道状态");
|
||||
return RichMessage::info(i18n::no_active_channels(lang))
|
||||
.with_title(i18n::channel_status_title(lang));
|
||||
}
|
||||
|
||||
let mut body = String::new();
|
||||
@@ -202,17 +233,9 @@ pub async fn handle_status(manager: &ChatChannelManager) -> RichMessage {
|
||||
));
|
||||
}
|
||||
|
||||
RichMessage::info(body.trim_end()).with_title("渠道状态")
|
||||
RichMessage::info(body.trim_end()).with_title(i18n::channel_status_title(lang))
|
||||
}
|
||||
|
||||
pub fn handle_help(prefix: &str) -> RichMessage {
|
||||
RichMessage::info(format!(
|
||||
"{prefix}recent - 最近 5 条会话\n\
|
||||
{prefix}search <关键词> - 搜索会话\n\
|
||||
{prefix}detail <ID> - 会话详情\n\
|
||||
{prefix}today - 今日活动汇总\n\
|
||||
{prefix}status - 渠道连接状态\n\
|
||||
{prefix}help - 显示帮助",
|
||||
))
|
||||
.with_title("Codeg Bot 帮助")
|
||||
pub fn handle_help(prefix: &str, lang: Lang) -> RichMessage {
|
||||
RichMessage::info(i18n::help_body(lang, prefix)).with_title(i18n::help_title(lang))
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::time::{Duration, Instant};
|
||||
use sea_orm::DatabaseConnection;
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use super::i18n::Lang;
|
||||
use super::manager::ChatChannelManager;
|
||||
use super::message_formatter;
|
||||
use super::types::RichMessage;
|
||||
@@ -13,6 +14,7 @@ use crate::web::event_bridge::WebEventBroadcaster;
|
||||
|
||||
/// Minimum interval between pushes for the same event type per channel (debounce).
|
||||
const DEBOUNCE_SECS: u64 = 5;
|
||||
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
||||
|
||||
pub fn spawn_event_subscriber(
|
||||
broadcaster: Arc<WebEventBroadcaster>,
|
||||
@@ -36,7 +38,9 @@ pub fn spawn_event_subscriber(
|
||||
}
|
||||
};
|
||||
|
||||
let message = match parse_event(&event.channel, &event.payload) {
|
||||
let lang = load_lang(&db_conn).await;
|
||||
|
||||
let message = match parse_event(&event.channel, &event.payload, lang) {
|
||||
Some((event_type, msg)) => {
|
||||
// Global event filter check
|
||||
let global_filter = app_metadata_service::get_value(&db_conn, "chat_event_filter")
|
||||
@@ -100,14 +104,23 @@ pub fn spawn_event_subscriber(
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_event(channel: &str, payload: &serde_json::Value) -> Option<(String, RichMessage)> {
|
||||
async fn load_lang(db: &DatabaseConnection) -> Lang {
|
||||
app_metadata_service::get_value(db, MESSAGE_LANGUAGE_KEY)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|v| Lang::from_str_lossy(&v))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
fn parse_event(channel: &str, payload: &serde_json::Value, lang: Lang) -> Option<(String, RichMessage)> {
|
||||
match channel {
|
||||
"acp://event" => parse_acp_event(payload),
|
||||
"acp://event" => parse_acp_event(payload, lang),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_acp_event(payload: &serde_json::Value) -> Option<(String, RichMessage)> {
|
||||
fn parse_acp_event(payload: &serde_json::Value, lang: Lang) -> Option<(String, RichMessage)> {
|
||||
let event_type = payload.get("type")?.as_str()?;
|
||||
|
||||
match event_type {
|
||||
@@ -126,7 +139,7 @@ fn parse_acp_event(payload: &serde_json::Value) -> Option<(String, RichMessage)>
|
||||
.unwrap_or("Unknown Agent");
|
||||
Some((
|
||||
"turn_complete".to_string(),
|
||||
message_formatter::format_turn_complete(agent_type, stop_reason),
|
||||
message_formatter::format_turn_complete(agent_type, stop_reason, lang),
|
||||
))
|
||||
}
|
||||
"error" => {
|
||||
@@ -140,10 +153,9 @@ fn parse_acp_event(payload: &serde_json::Value) -> Option<(String, RichMessage)>
|
||||
.unwrap_or("Unknown error");
|
||||
Some((
|
||||
"error".to_string(),
|
||||
message_formatter::format_agent_error(agent_type, message),
|
||||
message_formatter::format_agent_error(agent_type, message, lang),
|
||||
))
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
795
src-tauri/src/chat_channel/i18n.rs
Normal file
795
src-tauri/src/chat_channel/i18n.rs
Normal file
@@ -0,0 +1,795 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
|
||||
#[serde(rename_all = "kebab-case")]
|
||||
pub enum Lang {
|
||||
En,
|
||||
ZhCn,
|
||||
ZhTw,
|
||||
Ja,
|
||||
Ko,
|
||||
Es,
|
||||
De,
|
||||
Fr,
|
||||
Pt,
|
||||
Ar,
|
||||
}
|
||||
|
||||
impl Default for Lang {
|
||||
fn default() -> Self {
|
||||
Lang::En
|
||||
}
|
||||
}
|
||||
|
||||
impl Lang {
|
||||
pub fn from_str_lossy(s: &str) -> Self {
|
||||
match s {
|
||||
"en" => Lang::En,
|
||||
"zh-cn" | "zh-CN" | "zh_CN" => Lang::ZhCn,
|
||||
"zh-tw" | "zh-TW" | "zh_TW" => Lang::ZhTw,
|
||||
"ja" => Lang::Ja,
|
||||
"ko" => Lang::Ko,
|
||||
"es" => Lang::Es,
|
||||
"de" => Lang::De,
|
||||
"fr" => Lang::Fr,
|
||||
"pt" => Lang::Pt,
|
||||
"ar" => Lang::Ar,
|
||||
_ => Lang::En,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ── Event messages ──
|
||||
|
||||
pub fn turn_complete_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "会话完成",
|
||||
Lang::ZhTw => "對話完成",
|
||||
Lang::Ja => "セッション完了",
|
||||
Lang::Ko => "세션 완료",
|
||||
Lang::Es => "Sesión completada",
|
||||
Lang::De => "Sitzung abgeschlossen",
|
||||
Lang::Fr => "Session terminée",
|
||||
Lang::Pt => "Sessão concluída",
|
||||
Lang::Ar => "اكتملت الجلسة",
|
||||
Lang::En => "Turn Complete",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn turn_complete_body(lang: Lang, agent_type: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("{agent_type} 会话已完成"),
|
||||
Lang::ZhTw => format!("{agent_type} 對話已完成"),
|
||||
Lang::Ja => format!("{agent_type} セッションが完了しました"),
|
||||
Lang::Ko => format!("{agent_type} 세션이 완료되었습니다"),
|
||||
Lang::Es => format!("{agent_type} sesión completada"),
|
||||
Lang::De => format!("{agent_type} Sitzung abgeschlossen"),
|
||||
Lang::Fr => format!("Session {agent_type} terminée"),
|
||||
Lang::Pt => format!("Sessão {agent_type} concluída"),
|
||||
Lang::Ar => format!("اكتملت جلسة {agent_type}"),
|
||||
Lang::En => format!("{agent_type} session completed"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_reason_label(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "结束原因",
|
||||
Lang::ZhTw => "結束原因",
|
||||
Lang::Ja => "終了理由",
|
||||
Lang::Ko => "종료 사유",
|
||||
Lang::Es => "Motivo de fin",
|
||||
Lang::De => "Beendigungsgrund",
|
||||
Lang::Fr => "Raison de fin",
|
||||
Lang::Pt => "Motivo do término",
|
||||
Lang::Ar => "سبب الانتهاء",
|
||||
Lang::En => "Stop Reason",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_reason_end_turn(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "正常结束",
|
||||
Lang::ZhTw => "正常結束",
|
||||
Lang::Ja => "正常終了",
|
||||
Lang::Ko => "정상 종료",
|
||||
Lang::Es => "Finalizado",
|
||||
Lang::De => "Normal beendet",
|
||||
Lang::Fr => "Terminé normalement",
|
||||
Lang::Pt => "Finalizado",
|
||||
Lang::Ar => "انتهى بشكل طبيعي",
|
||||
Lang::En => "Completed",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stop_reason_cancelled(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "已取消",
|
||||
Lang::ZhTw => "已取消",
|
||||
Lang::Ja => "キャンセル",
|
||||
Lang::Ko => "취소됨",
|
||||
Lang::Es => "Cancelado",
|
||||
Lang::De => "Abgebrochen",
|
||||
Lang::Fr => "Annulé",
|
||||
Lang::Pt => "Cancelado",
|
||||
Lang::Ar => "تم الإلغاء",
|
||||
Lang::En => "Cancelled",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_error_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "代理错误",
|
||||
Lang::ZhTw => "代理錯誤",
|
||||
Lang::Ja => "エージェントエラー",
|
||||
Lang::Ko => "에이전트 오류",
|
||||
Lang::Es => "Error del agente",
|
||||
Lang::De => "Agent-Fehler",
|
||||
Lang::Fr => "Erreur de l'agent",
|
||||
Lang::Pt => "Erro do agente",
|
||||
Lang::Ar => "خطأ في الوكيل",
|
||||
Lang::En => "Agent Error",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_error_body(lang: Lang, agent_type: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("{agent_type} 发生错误"),
|
||||
Lang::ZhTw => format!("{agent_type} 發生錯誤"),
|
||||
Lang::Ja => format!("{agent_type} でエラーが発生しました"),
|
||||
Lang::Ko => format!("{agent_type}에서 오류 발생"),
|
||||
Lang::Es => format!("{agent_type} encontró un error"),
|
||||
Lang::De => format!("{agent_type} hat einen Fehler"),
|
||||
Lang::Fr => format!("{agent_type} a rencontré une erreur"),
|
||||
Lang::Pt => format!("{agent_type} encontrou um erro"),
|
||||
Lang::Ar => format!("حدث خطأ في {agent_type}"),
|
||||
Lang::En => format!("{agent_type} encountered an error"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn error_message_label(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "错误信息",
|
||||
Lang::ZhTw => "錯誤訊息",
|
||||
Lang::Ja => "エラーメッセージ",
|
||||
Lang::Ko => "오류 메시지",
|
||||
Lang::Es => "Mensaje de error",
|
||||
Lang::De => "Fehlermeldung",
|
||||
Lang::Fr => "Message d'erreur",
|
||||
Lang::Pt => "Mensagem de erro",
|
||||
Lang::Ar => "رسالة الخطأ",
|
||||
Lang::En => "Error Message",
|
||||
}
|
||||
}
|
||||
|
||||
// ── Daily report ──
|
||||
|
||||
pub fn daily_report_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "每日编码报告",
|
||||
Lang::ZhTw => "每日編碼報告",
|
||||
Lang::Ja => "日次コーディングレポート",
|
||||
Lang::Ko => "일일 코딩 보고서",
|
||||
Lang::Es => "Informe diario de codificación",
|
||||
Lang::De => "Täglicher Coding-Bericht",
|
||||
Lang::Fr => "Rapport de codage quotidien",
|
||||
Lang::Pt => "Relatório diário de codificação",
|
||||
Lang::Ar => "تقرير البرمجة اليومي",
|
||||
Lang::En => "Daily Coding Report",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn daily_report_summary(lang: Lang, date: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("今日编码活动汇总 ({date})"),
|
||||
Lang::ZhTw => format!("今日編碼活動匯總 ({date})"),
|
||||
Lang::Ja => format!("本日のコーディング活動まとめ ({date})"),
|
||||
Lang::Ko => format!("오늘의 코딩 활동 요약 ({date})"),
|
||||
Lang::Es => format!("Resumen de actividad de codificación ({date})"),
|
||||
Lang::De => format!("Coding-Aktivitätszusammenfassung ({date})"),
|
||||
Lang::Fr => format!("Résumé de l'activité de codage ({date})"),
|
||||
Lang::Pt => format!("Resumo da atividade de codificação ({date})"),
|
||||
Lang::Ar => format!("ملخص نشاط البرمجة ({date})"),
|
||||
Lang::En => format!("Daily coding activity summary ({date})"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn total_sessions(lang: Lang, count: u32) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("会话总数: {count}"),
|
||||
Lang::ZhTw => format!("對話總數: {count}"),
|
||||
Lang::Ja => format!("セッション合計: {count}"),
|
||||
Lang::Ko => format!("총 세션: {count}"),
|
||||
Lang::Es => format!("Total de sesiones: {count}"),
|
||||
Lang::De => format!("Sitzungen gesamt: {count}"),
|
||||
Lang::Fr => format!("Sessions totales : {count}"),
|
||||
Lang::Pt => format!("Total de sessões: {count}"),
|
||||
Lang::Ar => format!("إجمالي الجلسات: {count}"),
|
||||
Lang::En => format!("Total sessions: {count}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn by_agent_label(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "按代理分布:",
|
||||
Lang::ZhTw => "按代理分佈:",
|
||||
Lang::Ja => "エージェント別:",
|
||||
Lang::Ko => "에이전트별:",
|
||||
Lang::Es => "Por agente:",
|
||||
Lang::De => "Nach Agent:",
|
||||
Lang::Fr => "Par agent :",
|
||||
Lang::Pt => "Por agente:",
|
||||
Lang::Ar => "حسب الوكيل:",
|
||||
Lang::En => "By agent:",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_session_count(lang: Lang, agent: &str, count: u32) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("{agent} - {count} 个会话"),
|
||||
Lang::ZhTw => format!("{agent} - {count} 個對話"),
|
||||
Lang::Ja => format!("{agent} - {count} セッション"),
|
||||
Lang::Ko => format!("{agent} - {count}개 세션"),
|
||||
Lang::Es => format!("{agent} - {count} sesiones"),
|
||||
Lang::De => format!("{agent} - {count} Sitzungen"),
|
||||
Lang::Fr => format!("{agent} - {count} sessions"),
|
||||
Lang::Pt => format!("{agent} - {count} sessões"),
|
||||
Lang::Ar => format!("{agent} - {count} جلسات"),
|
||||
Lang::En => format!("{agent} - {count} sessions"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn projects_label(lang: Lang, projects: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("涉及项目: {projects}"),
|
||||
Lang::ZhTw => format!("涉及專案: {projects}"),
|
||||
Lang::Ja => format!("関連プロジェクト: {projects}"),
|
||||
Lang::Ko => format!("관련 프로젝트: {projects}"),
|
||||
Lang::Es => format!("Proyectos: {projects}"),
|
||||
Lang::De => format!("Projekte: {projects}"),
|
||||
Lang::Fr => format!("Projets : {projects}"),
|
||||
Lang::Pt => format!("Projetos: {projects}"),
|
||||
Lang::Ar => format!("المشاريع: {projects}"),
|
||||
Lang::En => format!("Projects: {projects}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn key_activities_label(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "主要活动:",
|
||||
Lang::ZhTw => "主要活動:",
|
||||
Lang::Ja => "主な活動:",
|
||||
Lang::Ko => "주요 활동:",
|
||||
Lang::Es => "Actividades clave:",
|
||||
Lang::De => "Wichtige Aktivitäten:",
|
||||
Lang::Fr => "Activités principales :",
|
||||
Lang::Pt => "Atividades principais:",
|
||||
Lang::Ar => "الأنشطة الرئيسية:",
|
||||
Lang::En => "Key activities:",
|
||||
}
|
||||
}
|
||||
|
||||
// ── Command responses ──
|
||||
|
||||
pub fn query_failed_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "查询失败",
|
||||
Lang::ZhTw => "查詢失敗",
|
||||
Lang::Ja => "クエリ失敗",
|
||||
Lang::Ko => "조회 실패",
|
||||
Lang::Es => "Error de consulta",
|
||||
Lang::De => "Abfrage fehlgeschlagen",
|
||||
Lang::Fr => "Échec de la requête",
|
||||
Lang::Pt => "Falha na consulta",
|
||||
Lang::Ar => "فشل الاستعلام",
|
||||
Lang::En => "Query Failed",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no_conversations(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "暂无会话记录",
|
||||
Lang::ZhTw => "暫無對話記錄",
|
||||
Lang::Ja => "セッション履歴なし",
|
||||
Lang::Ko => "대화 기록 없음",
|
||||
Lang::Es => "Sin conversaciones",
|
||||
Lang::De => "Keine Sitzungen",
|
||||
Lang::Fr => "Aucune session",
|
||||
Lang::Pt => "Nenhuma sessão",
|
||||
Lang::Ar => "لا توجد جلسات",
|
||||
Lang::En => "No conversations found",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recent_conversations_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "最近会话",
|
||||
Lang::ZhTw => "最近對話",
|
||||
Lang::Ja => "最近のセッション",
|
||||
Lang::Ko => "최근 대화",
|
||||
Lang::Es => "Conversaciones recientes",
|
||||
Lang::De => "Letzte Sitzungen",
|
||||
Lang::Fr => "Sessions récentes",
|
||||
Lang::Pt => "Sessões recentes",
|
||||
Lang::Ar => "الجلسات الأخيرة",
|
||||
Lang::En => "Recent Conversations",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn untitled(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "(无标题)",
|
||||
Lang::ZhTw => "(無標題)",
|
||||
Lang::Ja => "(無題)",
|
||||
Lang::Ko => "(제목 없음)",
|
||||
Lang::Es => "(Sin título)",
|
||||
Lang::De => "(Ohne Titel)",
|
||||
Lang::Fr => "(Sans titre)",
|
||||
Lang::Pt => "(Sem título)",
|
||||
Lang::Ar => "(بدون عنوان)",
|
||||
Lang::En => "(Untitled)",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recent_n_conversations_title(lang: Lang, n: usize) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("最近 {n} 条会话"),
|
||||
Lang::ZhTw => format!("最近 {n} 條對話"),
|
||||
Lang::Ja => format!("最新 {n} セッション"),
|
||||
Lang::Ko => format!("최근 {n}개 대화"),
|
||||
Lang::Es => format!("{n} conversaciones más recientes"),
|
||||
Lang::De => format!("Letzte {n} Sitzungen"),
|
||||
Lang::Fr => format!("{n} dernières sessions"),
|
||||
Lang::Pt => format!("{n} sessões mais recentes"),
|
||||
Lang::Ar => format!("أحدث {n} جلسات"),
|
||||
Lang::En => format!("{n} Most Recent Conversations"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_no_results(lang: Lang, keyword: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("未找到包含 \"{keyword}\" 的会话"),
|
||||
Lang::ZhTw => format!("未找到包含 \"{keyword}\" 的對話"),
|
||||
Lang::Ja => format!("\"{keyword}\" を含むセッションが見つかりません"),
|
||||
Lang::Ko => format!("\"{keyword}\"을(를) 포함하는 대화를 찾을 수 없습니다"),
|
||||
Lang::Es => format!("No se encontraron conversaciones con \"{keyword}\""),
|
||||
Lang::De => format!("Keine Sitzungen mit \"{keyword}\" gefunden"),
|
||||
Lang::Fr => format!("Aucune session trouvée avec \"{keyword}\""),
|
||||
Lang::Pt => format!("Nenhuma sessão encontrada com \"{keyword}\""),
|
||||
Lang::Ar => format!("لم يتم العثور على جلسات تحتوي على \"{keyword}\""),
|
||||
Lang::En => format!("No conversations found matching \"{keyword}\""),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_results_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "搜索结果",
|
||||
Lang::ZhTw => "搜尋結果",
|
||||
Lang::Ja => "検索結果",
|
||||
Lang::Ko => "검색 결과",
|
||||
Lang::Es => "Resultados",
|
||||
Lang::De => "Suchergebnisse",
|
||||
Lang::Fr => "Résultats",
|
||||
Lang::Pt => "Resultados",
|
||||
Lang::Ar => "نتائج البحث",
|
||||
Lang::En => "Search Results",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_results_count_title(lang: Lang, keyword: &str, count: usize) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("搜索 \"{keyword}\" - {count} 条结果"),
|
||||
Lang::ZhTw => format!("搜尋 \"{keyword}\" - {count} 條結果"),
|
||||
Lang::Ja => format!("\"{keyword}\" の検索 - {count} 件"),
|
||||
Lang::Ko => format!("\"{keyword}\" 검색 - {count}건"),
|
||||
Lang::Es => format!("Buscar \"{keyword}\" - {count} resultados"),
|
||||
Lang::De => format!("Suche \"{keyword}\" - {count} Ergebnisse"),
|
||||
Lang::Fr => format!("Recherche \"{keyword}\" - {count} résultats"),
|
||||
Lang::Pt => format!("Busca \"{keyword}\" - {count} resultados"),
|
||||
Lang::Ar => format!("بحث \"{keyword}\" - {count} نتائج"),
|
||||
Lang::En => format!("Search \"{keyword}\" - {count} results"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn conversation_not_found(lang: Lang, id: i32) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("会话 {id} 不存在"),
|
||||
Lang::ZhTw => format!("對話 {id} 不存在"),
|
||||
Lang::Ja => format!("セッション {id} が見つかりません"),
|
||||
Lang::Ko => format!("대화 {id}를 찾을 수 없습니다"),
|
||||
Lang::Es => format!("Conversación {id} no encontrada"),
|
||||
Lang::De => format!("Sitzung {id} nicht gefunden"),
|
||||
Lang::Fr => format!("Session {id} introuvable"),
|
||||
Lang::Pt => format!("Sessão {id} não encontrada"),
|
||||
Lang::Ar => format!("الجلسة {id} غير موجودة"),
|
||||
Lang::En => format!("Conversation {id} not found"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn not_found_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "未找到",
|
||||
Lang::ZhTw => "未找到",
|
||||
Lang::Ja => "見つかりません",
|
||||
Lang::Ko => "찾을 수 없음",
|
||||
Lang::Es => "No encontrado",
|
||||
Lang::De => "Nicht gefunden",
|
||||
Lang::Fr => "Introuvable",
|
||||
Lang::Pt => "Não encontrado",
|
||||
Lang::Ar => "غير موجود",
|
||||
Lang::En => "Not Found",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn conversation_detail_title(lang: Lang, id: i32) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("会话详情 #{id}"),
|
||||
Lang::ZhTw => format!("對話詳情 #{id}"),
|
||||
Lang::Ja => format!("セッション詳細 #{id}"),
|
||||
Lang::Ko => format!("대화 상세 #{id}"),
|
||||
Lang::Es => format!("Detalles #{id}"),
|
||||
Lang::De => format!("Sitzungsdetails #{id}"),
|
||||
Lang::Fr => format!("Détails #{id}"),
|
||||
Lang::Pt => format!("Detalhes #{id}"),
|
||||
Lang::Ar => format!("تفاصيل الجلسة #{id}"),
|
||||
Lang::En => format!("Conversation Details #{id}"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_agent(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "代理",
|
||||
Lang::ZhTw => "代理",
|
||||
Lang::Ja => "エージェント",
|
||||
Lang::Ko => "에이전트",
|
||||
Lang::Es => "Agente",
|
||||
Lang::De => "Agent",
|
||||
Lang::Fr => "Agent",
|
||||
Lang::Pt => "Agente",
|
||||
Lang::Ar => "الوكيل",
|
||||
Lang::En => "Agent",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_status(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "状态",
|
||||
Lang::ZhTw => "狀態",
|
||||
Lang::Ja => "ステータス",
|
||||
Lang::Ko => "상태",
|
||||
Lang::Es => "Estado",
|
||||
Lang::De => "Status",
|
||||
Lang::Fr => "Statut",
|
||||
Lang::Pt => "Status",
|
||||
Lang::Ar => "الحالة",
|
||||
Lang::En => "Status",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_message_count(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "消息数",
|
||||
Lang::ZhTw => "訊息數",
|
||||
Lang::Ja => "メッセージ数",
|
||||
Lang::Ko => "메시지 수",
|
||||
Lang::Es => "Mensajes",
|
||||
Lang::De => "Nachrichten",
|
||||
Lang::Fr => "Messages",
|
||||
Lang::Pt => "Mensagens",
|
||||
Lang::Ar => "عدد الرسائل",
|
||||
Lang::En => "Messages",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn field_created_at(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "创建时间",
|
||||
Lang::ZhTw => "建立時間",
|
||||
Lang::Ja => "作成日時",
|
||||
Lang::Ko => "생성 시간",
|
||||
Lang::Es => "Creado",
|
||||
Lang::De => "Erstellt",
|
||||
Lang::Fr => "Créé",
|
||||
Lang::Pt => "Criado",
|
||||
Lang::Ar => "تاريخ الإنشاء",
|
||||
Lang::En => "Created",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no_activity_today(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "今日暂无编码活动",
|
||||
Lang::ZhTw => "今日暫無編碼活動",
|
||||
Lang::Ja => "本日のコーディング活動はありません",
|
||||
Lang::Ko => "오늘 코딩 활동이 없습니다",
|
||||
Lang::Es => "Sin actividad de codificación hoy",
|
||||
Lang::De => "Heute keine Coding-Aktivität",
|
||||
Lang::Fr => "Aucune activité de codage aujourd'hui",
|
||||
Lang::Pt => "Nenhuma atividade de codificação hoje",
|
||||
Lang::Ar => "لا يوجد نشاط برمجة اليوم",
|
||||
Lang::En => "No coding activity today",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn today_activity_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "今日活动",
|
||||
Lang::ZhTw => "今日活動",
|
||||
Lang::Ja => "本日の活動",
|
||||
Lang::Ko => "오늘의 활동",
|
||||
Lang::Es => "Actividad de hoy",
|
||||
Lang::De => "Heutige Aktivität",
|
||||
Lang::Fr => "Activité du jour",
|
||||
Lang::Pt => "Atividade de hoje",
|
||||
Lang::Ar => "نشاط اليوم",
|
||||
Lang::En => "Today's Activity",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn today_activity_date_title(lang: Lang, date: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("今日活动 ({date})"),
|
||||
Lang::ZhTw => format!("今日活動 ({date})"),
|
||||
Lang::Ja => format!("本日の活動 ({date})"),
|
||||
Lang::Ko => format!("오늘의 활동 ({date})"),
|
||||
Lang::Es => format!("Actividad de hoy ({date})"),
|
||||
Lang::De => format!("Heutige Aktivität ({date})"),
|
||||
Lang::Fr => format!("Activité du jour ({date})"),
|
||||
Lang::Pt => format!("Atividade de hoje ({date})"),
|
||||
Lang::Ar => format!("نشاط اليوم ({date})"),
|
||||
Lang::En => format!("Today's Activity ({date})"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn agent_count(lang: Lang, agent: &str, count: u32) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("{agent} - {count} 个"),
|
||||
Lang::ZhTw => format!("{agent} - {count} 個"),
|
||||
Lang::Ja => format!("{agent} - {count} 件"),
|
||||
Lang::Ko => format!("{agent} - {count}개"),
|
||||
Lang::Es | Lang::De | Lang::Fr | Lang::Pt | Lang::Ar | Lang::En => {
|
||||
format!("{agent} - {count}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn recent_activity_label(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "最近活动:",
|
||||
Lang::ZhTw => "最近活動:",
|
||||
Lang::Ja => "最近の活動:",
|
||||
Lang::Ko => "최근 활동:",
|
||||
Lang::Es => "Actividad reciente:",
|
||||
Lang::De => "Letzte Aktivität:",
|
||||
Lang::Fr => "Activité récente :",
|
||||
Lang::Pt => "Atividade recente:",
|
||||
Lang::Ar => "النشاط الأخير:",
|
||||
Lang::En => "Recent activity:",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn no_active_channels(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "暂无活跃渠道",
|
||||
Lang::ZhTw => "暫無活躍頻道",
|
||||
Lang::Ja => "アクティブなチャンネルなし",
|
||||
Lang::Ko => "활성 채널 없음",
|
||||
Lang::Es => "Sin canales activos",
|
||||
Lang::De => "Keine aktiven Kanäle",
|
||||
Lang::Fr => "Aucun canal actif",
|
||||
Lang::Pt => "Nenhum canal ativo",
|
||||
Lang::Ar => "لا توجد قنوات نشطة",
|
||||
Lang::En => "No active channels",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn channel_status_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "渠道状态",
|
||||
Lang::ZhTw => "頻道狀態",
|
||||
Lang::Ja => "チャンネル状況",
|
||||
Lang::Ko => "채널 상태",
|
||||
Lang::Es => "Estado de canales",
|
||||
Lang::De => "Kanalstatus",
|
||||
Lang::Fr => "Statut des canaux",
|
||||
Lang::Pt => "Status dos canais",
|
||||
Lang::Ar => "حالة القنوات",
|
||||
Lang::En => "Channel Status",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn help_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "Codeg Bot 帮助",
|
||||
Lang::ZhTw => "Codeg Bot 幫助",
|
||||
Lang::Ja => "Codeg Bot ヘルプ",
|
||||
Lang::Ko => "Codeg Bot 도움말",
|
||||
Lang::Es => "Ayuda de Codeg Bot",
|
||||
Lang::De => "Codeg Bot Hilfe",
|
||||
Lang::Fr => "Aide Codeg Bot",
|
||||
Lang::Pt => "Ajuda do Codeg Bot",
|
||||
Lang::Ar => "مساعدة Codeg Bot",
|
||||
Lang::En => "Codeg Bot Help",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn help_body(lang: Lang, prefix: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!(
|
||||
"{prefix}recent - 最近 5 条会话\n\
|
||||
{prefix}search <关键词> - 搜索会话\n\
|
||||
{prefix}detail <ID> - 会话详情\n\
|
||||
{prefix}today - 今日活动汇总\n\
|
||||
{prefix}status - 渠道连接状态\n\
|
||||
{prefix}help - 显示帮助"
|
||||
),
|
||||
Lang::ZhTw => format!(
|
||||
"{prefix}recent - 最近 5 條對話\n\
|
||||
{prefix}search <關鍵字> - 搜尋對話\n\
|
||||
{prefix}detail <ID> - 對話詳情\n\
|
||||
{prefix}today - 今日活動匯總\n\
|
||||
{prefix}status - 頻道連線狀態\n\
|
||||
{prefix}help - 顯示幫助"
|
||||
),
|
||||
Lang::Ja => format!(
|
||||
"{prefix}recent - 最新5件のセッション\n\
|
||||
{prefix}search <キーワード> - セッション検索\n\
|
||||
{prefix}detail <ID> - セッション詳細\n\
|
||||
{prefix}today - 本日の活動まとめ\n\
|
||||
{prefix}status - チャンネル接続状況\n\
|
||||
{prefix}help - ヘルプを表示"
|
||||
),
|
||||
Lang::Ko => format!(
|
||||
"{prefix}recent - 최근 5개 대화\n\
|
||||
{prefix}search <키워드> - 대화 검색\n\
|
||||
{prefix}detail <ID> - 대화 상세\n\
|
||||
{prefix}today - 오늘의 활동 요약\n\
|
||||
{prefix}status - 채널 연결 상태\n\
|
||||
{prefix}help - 도움말 표시"
|
||||
),
|
||||
Lang::Es => format!(
|
||||
"{prefix}recent - 5 conversaciones más recientes\n\
|
||||
{prefix}search <palabra> - Buscar conversaciones\n\
|
||||
{prefix}detail <ID> - Detalles de conversación\n\
|
||||
{prefix}today - Resumen de hoy\n\
|
||||
{prefix}status - Estado de canales\n\
|
||||
{prefix}help - Mostrar ayuda"
|
||||
),
|
||||
Lang::De => format!(
|
||||
"{prefix}recent - 5 neueste Sitzungen\n\
|
||||
{prefix}search <Stichwort> - Sitzungen suchen\n\
|
||||
{prefix}detail <ID> - Sitzungsdetails\n\
|
||||
{prefix}today - Heutige Zusammenfassung\n\
|
||||
{prefix}status - Kanalstatus\n\
|
||||
{prefix}help - Hilfe anzeigen"
|
||||
),
|
||||
Lang::Fr => format!(
|
||||
"{prefix}recent - 5 dernières sessions\n\
|
||||
{prefix}search <mot-clé> - Rechercher des sessions\n\
|
||||
{prefix}detail <ID> - Détails de la session\n\
|
||||
{prefix}today - Résumé du jour\n\
|
||||
{prefix}status - Statut des canaux\n\
|
||||
{prefix}help - Afficher l'aide"
|
||||
),
|
||||
Lang::Pt => format!(
|
||||
"{prefix}recent - 5 sessões mais recentes\n\
|
||||
{prefix}search <palavra> - Buscar sessões\n\
|
||||
{prefix}detail <ID> - Detalhes da sessão\n\
|
||||
{prefix}today - Resumo de hoje\n\
|
||||
{prefix}status - Status dos canais\n\
|
||||
{prefix}help - Mostrar ajuda"
|
||||
),
|
||||
Lang::Ar => format!(
|
||||
"{prefix}recent - أحدث 5 جلسات\n\
|
||||
{prefix}search <كلمة> - البحث في الجلسات\n\
|
||||
{prefix}detail <ID> - تفاصيل الجلسة\n\
|
||||
{prefix}today - ملخص اليوم\n\
|
||||
{prefix}status - حالة القنوات\n\
|
||||
{prefix}help - عرض المساعدة"
|
||||
),
|
||||
Lang::En => format!(
|
||||
"{prefix}recent - 5 most recent conversations\n\
|
||||
{prefix}search <keyword> - Search conversations\n\
|
||||
{prefix}detail <ID> - Conversation details\n\
|
||||
{prefix}today - Today's activity summary\n\
|
||||
{prefix}status - Channel connection status\n\
|
||||
{prefix}help - Show help"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
// ── Command dispatcher messages ──
|
||||
|
||||
pub fn invalid_args_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "参数错误",
|
||||
Lang::ZhTw => "參數錯誤",
|
||||
Lang::Ja => "引数エラー",
|
||||
Lang::Ko => "인수 오류",
|
||||
Lang::Es => "Argumentos inválidos",
|
||||
Lang::De => "Ungültige Argumente",
|
||||
Lang::Fr => "Arguments invalides",
|
||||
Lang::Pt => "Argumentos inválidos",
|
||||
Lang::Ar => "وسيطات غير صالحة",
|
||||
Lang::En => "Invalid Arguments",
|
||||
}
|
||||
}
|
||||
|
||||
pub fn search_usage(lang: Lang, prefix: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("用法: {prefix}search <关键词>"),
|
||||
Lang::ZhTw => format!("用法: {prefix}search <關鍵字>"),
|
||||
Lang::Ja => format!("使い方: {prefix}search <キーワード>"),
|
||||
Lang::Ko => format!("사용법: {prefix}search <키워드>"),
|
||||
Lang::Es => format!("Uso: {prefix}search <palabra>"),
|
||||
Lang::De => format!("Verwendung: {prefix}search <Stichwort>"),
|
||||
Lang::Fr => format!("Utilisation : {prefix}search <mot-clé>"),
|
||||
Lang::Pt => format!("Uso: {prefix}search <palavra>"),
|
||||
Lang::Ar => format!("الاستخدام: {prefix}search <كلمة>"),
|
||||
Lang::En => format!("Usage: {prefix}search <keyword>"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn detail_usage(lang: Lang, prefix: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!("用法: {prefix}detail <会话ID>"),
|
||||
Lang::ZhTw => format!("用法: {prefix}detail <對話ID>"),
|
||||
Lang::Ja => format!("使い方: {prefix}detail <セッションID>"),
|
||||
Lang::Ko => format!("사용법: {prefix}detail <대화ID>"),
|
||||
Lang::Es => format!("Uso: {prefix}detail <ID>"),
|
||||
Lang::De => format!("Verwendung: {prefix}detail <ID>"),
|
||||
Lang::Fr => format!("Utilisation : {prefix}detail <ID>"),
|
||||
Lang::Pt => format!("Uso: {prefix}detail <ID>"),
|
||||
Lang::Ar => format!("الاستخدام: {prefix}detail <ID>"),
|
||||
Lang::En => format!("Usage: {prefix}detail <ID>"),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unknown_command(lang: Lang, prefix: &str, command: &str) -> String {
|
||||
match lang {
|
||||
Lang::ZhCn => format!(
|
||||
"未知命令: {prefix}{command}\n输入 {prefix}help 查看可用命令"
|
||||
),
|
||||
Lang::ZhTw => format!(
|
||||
"未知命令: {prefix}{command}\n輸入 {prefix}help 查看可用命令"
|
||||
),
|
||||
Lang::Ja => format!(
|
||||
"不明なコマンド: {prefix}{command}\n{prefix}help でヘルプを表示"
|
||||
),
|
||||
Lang::Ko => format!(
|
||||
"알 수 없는 명령: {prefix}{command}\n{prefix}help 로 도움말 보기"
|
||||
),
|
||||
Lang::Es => format!(
|
||||
"Comando desconocido: {prefix}{command}\nEscriba {prefix}help para ver los comandos"
|
||||
),
|
||||
Lang::De => format!(
|
||||
"Unbekannter Befehl: {prefix}{command}\n{prefix}help für Hilfe eingeben"
|
||||
),
|
||||
Lang::Fr => format!(
|
||||
"Commande inconnue : {prefix}{command}\nTapez {prefix}help pour l'aide"
|
||||
),
|
||||
Lang::Pt => format!(
|
||||
"Comando desconhecido: {prefix}{command}\nDigite {prefix}help para ajuda"
|
||||
),
|
||||
Lang::Ar => format!(
|
||||
"أمر غير معروف: {prefix}{command}\nاكتب {prefix}help لعرض المساعدة"
|
||||
),
|
||||
Lang::En => format!(
|
||||
"Unknown command: {prefix}{command}\nType {prefix}help for available commands"
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn unknown_command_title(lang: Lang) -> &'static str {
|
||||
match lang {
|
||||
Lang::ZhCn => "未知命令",
|
||||
Lang::ZhTw => "未知命令",
|
||||
Lang::Ja => "不明なコマンド",
|
||||
Lang::Ko => "알 수 없는 명령",
|
||||
Lang::Es => "Comando desconocido",
|
||||
Lang::De => "Unbekannter Befehl",
|
||||
Lang::Fr => "Commande inconnue",
|
||||
Lang::Pt => "Comando desconhecido",
|
||||
Lang::Ar => "أمر غير معروف",
|
||||
Lang::En => "Unknown Command",
|
||||
}
|
||||
}
|
||||
@@ -1,21 +1,25 @@
|
||||
use super::i18n::{self, Lang};
|
||||
use super::types::{MessageLevel, RichMessage};
|
||||
|
||||
pub fn format_turn_complete(agent_type: &str, stop_reason: &str) -> RichMessage {
|
||||
pub fn format_turn_complete(agent_type: &str, stop_reason: &str, lang: Lang) -> RichMessage {
|
||||
let reason = match stop_reason {
|
||||
"end_turn" => "正常结束",
|
||||
"cancelled" => "已取消",
|
||||
"end_turn" => i18n::stop_reason_end_turn(lang),
|
||||
"cancelled" => i18n::stop_reason_cancelled(lang),
|
||||
_ => stop_reason,
|
||||
};
|
||||
RichMessage::info(format!("{agent_type} 会话已完成"))
|
||||
.with_title("会话完成")
|
||||
.with_field("结束原因", reason)
|
||||
RichMessage::info(i18n::turn_complete_body(lang, agent_type))
|
||||
.with_title(i18n::turn_complete_title(lang))
|
||||
.with_field(i18n::stop_reason_label(lang), reason)
|
||||
}
|
||||
|
||||
pub fn format_agent_error(agent_type: &str, message: &str) -> RichMessage {
|
||||
pub fn format_agent_error(agent_type: &str, message: &str, lang: Lang) -> RichMessage {
|
||||
RichMessage {
|
||||
title: Some("代理错误".to_string()),
|
||||
body: format!("{agent_type} 发生错误"),
|
||||
fields: vec![("错误信息".to_string(), message.to_string())],
|
||||
title: Some(i18n::agent_error_title(lang).to_string()),
|
||||
body: i18n::agent_error_body(lang, agent_type),
|
||||
fields: vec![(
|
||||
i18n::error_message_label(lang).to_string(),
|
||||
message.to_string(),
|
||||
)],
|
||||
level: MessageLevel::Error,
|
||||
}
|
||||
}
|
||||
@@ -28,31 +32,37 @@ pub struct DailyReportData {
|
||||
pub key_activities: Vec<String>,
|
||||
}
|
||||
|
||||
pub fn format_daily_report(report: &DailyReportData) -> RichMessage {
|
||||
let mut body = format!("今日编码活动汇总 ({})", report.date);
|
||||
pub fn format_daily_report(report: &DailyReportData, lang: Lang) -> RichMessage {
|
||||
let mut body = i18n::daily_report_summary(lang, &report.date);
|
||||
|
||||
body.push_str(&format!("\n\n会话总数: {}", report.total_conversations));
|
||||
body.push_str(&format!(
|
||||
"\n\n{}",
|
||||
i18n::total_sessions(lang, report.total_conversations)
|
||||
));
|
||||
|
||||
if !report.conversations_by_agent.is_empty() {
|
||||
body.push_str("\n\n按代理分布:");
|
||||
body.push_str(&format!("\n\n{}", i18n::by_agent_label(lang)));
|
||||
for (agent, count) in &report.conversations_by_agent {
|
||||
body.push_str(&format!("\n {} - {} 个会话", agent, count));
|
||||
body.push_str(&format!(
|
||||
"\n {}",
|
||||
i18n::agent_session_count(lang, agent, *count)
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if !report.projects_involved.is_empty() {
|
||||
body.push_str(&format!(
|
||||
"\n\n涉及项目: {}",
|
||||
report.projects_involved.join(", ")
|
||||
"\n\n{}",
|
||||
i18n::projects_label(lang, &report.projects_involved.join(", "))
|
||||
));
|
||||
}
|
||||
|
||||
if !report.key_activities.is_empty() {
|
||||
body.push_str("\n\n主要活动:");
|
||||
body.push_str(&format!("\n\n{}", i18n::key_activities_label(lang)));
|
||||
for activity in &report.key_activities {
|
||||
body.push_str(&format!("\n • {}", activity));
|
||||
}
|
||||
}
|
||||
|
||||
RichMessage::info(body).with_title("每日编码报告")
|
||||
RichMessage::info(body).with_title(i18n::daily_report_title(lang))
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@ pub mod command_dispatcher;
|
||||
pub mod command_handlers;
|
||||
pub mod error;
|
||||
pub mod event_subscriber;
|
||||
pub mod i18n;
|
||||
pub mod manager;
|
||||
pub mod message_formatter;
|
||||
pub mod scheduler;
|
||||
|
||||
@@ -4,10 +4,13 @@ use chrono::{Local, NaiveDate, Timelike, Utc};
|
||||
use sea_orm::{ColumnTrait, DatabaseConnection, EntityTrait, QueryFilter, QueryOrder};
|
||||
use tokio::task::JoinHandle;
|
||||
|
||||
use super::i18n::Lang;
|
||||
use super::manager::ChatChannelManager;
|
||||
use super::message_formatter::{self, DailyReportData};
|
||||
use crate::db::entities::conversation;
|
||||
use crate::db::service::{chat_channel_message_log_service, chat_channel_service};
|
||||
use crate::db::service::{app_metadata_service, chat_channel_message_log_service, chat_channel_service};
|
||||
|
||||
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
||||
|
||||
pub fn spawn_daily_report_scheduler(
|
||||
manager: ChatChannelManager,
|
||||
@@ -53,9 +56,11 @@ pub fn spawn_daily_report_scheduler(
|
||||
continue;
|
||||
}
|
||||
|
||||
let lang = load_lang(&db_conn).await;
|
||||
|
||||
// Generate and send report
|
||||
let report = generate_daily_report(&db_conn).await;
|
||||
let message = message_formatter::format_daily_report(&report);
|
||||
let message = message_formatter::format_daily_report(&report, lang);
|
||||
|
||||
let send_result = manager.send_to_channel(ch.id, &message).await;
|
||||
let (status, error_detail) = match &send_result {
|
||||
@@ -80,6 +85,15 @@ pub fn spawn_daily_report_scheduler(
|
||||
})
|
||||
}
|
||||
|
||||
async fn load_lang(db: &DatabaseConnection) -> Lang {
|
||||
app_metadata_service::get_value(db, MESSAGE_LANGUAGE_KEY)
|
||||
.await
|
||||
.ok()
|
||||
.flatten()
|
||||
.map(|v| Lang::from_str_lossy(&v))
|
||||
.unwrap_or_default()
|
||||
}
|
||||
|
||||
async fn generate_daily_report(db: &DatabaseConnection) -> DailyReportData {
|
||||
let now = Utc::now();
|
||||
let today_start = now.date_naive().and_hms_opt(0, 0, 0).unwrap().and_utc();
|
||||
|
||||
@@ -296,6 +296,42 @@ pub async fn set_chat_command_prefix_core(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const MESSAGE_LANGUAGE_KEY: &str = "chat_message_language";
|
||||
|
||||
pub async fn get_chat_message_language_core(
|
||||
db: &AppDatabase,
|
||||
) -> Result<String, AppCommandError> {
|
||||
let val = crate::db::service::app_metadata_service::get_value(&db.conn, MESSAGE_LANGUAGE_KEY)
|
||||
.await
|
||||
.map_err(AppCommandError::from)?;
|
||||
Ok(val.unwrap_or_else(|| "en".to_string()))
|
||||
}
|
||||
|
||||
pub async fn set_chat_message_language_core(
|
||||
db: &AppDatabase,
|
||||
language: String,
|
||||
) -> Result<(), AppCommandError> {
|
||||
// Validate language code
|
||||
let valid = [
|
||||
"en", "zh-cn", "zh-tw", "ja", "ko", "es", "de", "fr", "pt", "ar",
|
||||
];
|
||||
let lang_lower = language.to_lowercase();
|
||||
if !valid.contains(&lang_lower.as_str()) {
|
||||
return Err(AppCommandError::invalid_input(format!(
|
||||
"Unsupported language: {language}. Supported: {}",
|
||||
valid.join(", ")
|
||||
)));
|
||||
}
|
||||
crate::db::service::app_metadata_service::upsert_value(
|
||||
&db.conn,
|
||||
MESSAGE_LANGUAGE_KEY,
|
||||
&lang_lower,
|
||||
)
|
||||
.await
|
||||
.map_err(AppCommandError::from)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
const EVENT_FILTER_KEY: &str = "chat_event_filter";
|
||||
|
||||
pub async fn get_chat_event_filter_core(
|
||||
@@ -493,3 +529,20 @@ pub async fn set_chat_event_filter(
|
||||
) -> Result<(), AppCommandError> {
|
||||
set_chat_event_filter_core(&db, filter).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
#[tauri::command]
|
||||
pub async fn get_chat_message_language(
|
||||
db: tauri::State<'_, AppDatabase>,
|
||||
) -> Result<String, AppCommandError> {
|
||||
get_chat_message_language_core(&db).await
|
||||
}
|
||||
|
||||
#[cfg(feature = "tauri-runtime")]
|
||||
#[tauri::command]
|
||||
pub async fn set_chat_message_language(
|
||||
db: tauri::State<'_, AppDatabase>,
|
||||
language: String,
|
||||
) -> Result<(), AppCommandError> {
|
||||
set_chat_message_language_core(&db, language).await
|
||||
}
|
||||
|
||||
@@ -383,6 +383,8 @@ mod tauri_app {
|
||||
chat_channel_commands::set_chat_command_prefix,
|
||||
chat_channel_commands::get_chat_event_filter,
|
||||
chat_channel_commands::set_chat_event_filter,
|
||||
chat_channel_commands::get_chat_message_language,
|
||||
chat_channel_commands::set_chat_message_language,
|
||||
web::start_web_server,
|
||||
web::stop_web_server,
|
||||
web::get_web_server_status,
|
||||
|
||||
@@ -225,3 +225,24 @@ pub async fn set_chat_event_filter(
|
||||
cc_commands::set_chat_event_filter_core(&state.db, params.filter).await?;
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
pub async fn get_chat_message_language(
|
||||
Extension(state): Extension<Arc<AppState>>,
|
||||
) -> Result<Json<String>, AppCommandError> {
|
||||
let result = cc_commands::get_chat_message_language_core(&state.db).await?;
|
||||
Ok(Json(result))
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SetMessageLanguageParams {
|
||||
pub language: String,
|
||||
}
|
||||
|
||||
pub async fn set_chat_message_language(
|
||||
Extension(state): Extension<Arc<AppState>>,
|
||||
Json(params): Json<SetMessageLanguageParams>,
|
||||
) -> Result<Json<()>, AppCommandError> {
|
||||
cc_commands::set_chat_message_language_core(&state.db, params.language).await?;
|
||||
Ok(Json(()))
|
||||
}
|
||||
|
||||
@@ -197,6 +197,8 @@ pub fn build_router(state: Arc<AppState>, token: String, static_dir: std::path::
|
||||
.route("/set_chat_command_prefix", post(handlers::chat_channel::set_chat_command_prefix))
|
||||
.route("/get_chat_event_filter", post(handlers::chat_channel::get_chat_event_filter))
|
||||
.route("/set_chat_event_filter", post(handlers::chat_channel::set_chat_event_filter))
|
||||
.route("/get_chat_message_language", post(handlers::chat_channel::get_chat_message_language))
|
||||
.route("/set_chat_message_language", post(handlers::chat_channel::set_chat_message_language))
|
||||
// ─── Terminal ───
|
||||
.route("/terminal_spawn", post(handlers::terminal::terminal_spawn))
|
||||
.route("/terminal_write", post(handlers::terminal::terminal_write))
|
||||
|
||||
91
src/components/settings/channel-other-tab.tsx
Normal file
91
src/components/settings/channel-other-tab.tsx
Normal file
@@ -0,0 +1,91 @@
|
||||
"use client"
|
||||
|
||||
import { useCallback, useEffect, useState } from "react"
|
||||
import { Loader2 } from "lucide-react"
|
||||
import { useTranslations } from "next-intl"
|
||||
import { toast } from "sonner"
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/components/ui/select"
|
||||
import { getChatMessageLanguage, setChatMessageLanguage } from "@/lib/api"
|
||||
|
||||
const SUPPORTED_LANGUAGES = [
|
||||
"en",
|
||||
"zh-cn",
|
||||
"zh-tw",
|
||||
"ja",
|
||||
"ko",
|
||||
"es",
|
||||
"de",
|
||||
"fr",
|
||||
"pt",
|
||||
"ar",
|
||||
] as const
|
||||
|
||||
export function ChannelOtherTab() {
|
||||
const t = useTranslations("ChatChannelSettings.language")
|
||||
const [language, setLanguage] = useState("en")
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
getChatMessageLanguage()
|
||||
.then((lang) => setLanguage(lang))
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false))
|
||||
}, [])
|
||||
|
||||
const handleLanguageChange = useCallback(
|
||||
async (value: string) => {
|
||||
setSaving(true)
|
||||
try {
|
||||
await setChatMessageLanguage(value)
|
||||
setLanguage(value)
|
||||
toast.success(t("saved"))
|
||||
} catch {
|
||||
toast.error(t("saveFailed"))
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
},
|
||||
[t]
|
||||
)
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="h-full flex items-center justify-center text-sm text-muted-foreground gap-2">
|
||||
<Loader2 className="h-4 w-4 animate-spin" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<section className="space-y-2">
|
||||
<h3 className="text-sm font-medium">{t("title")}</h3>
|
||||
<p className="text-xs text-muted-foreground">{t("description")}</p>
|
||||
<Select
|
||||
value={language}
|
||||
onValueChange={handleLanguageChange}
|
||||
disabled={saving}
|
||||
>
|
||||
<SelectTrigger className="w-56">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{SUPPORTED_LANGUAGES.map((lang) => (
|
||||
<SelectItem key={lang} value={lang}>
|
||||
{t(lang)}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"
|
||||
import { ChannelListTab } from "./channel-list-tab"
|
||||
import { ChannelCommandsTab } from "./channel-commands-tab"
|
||||
import { ChannelEventsTab } from "./channel-events-tab"
|
||||
import { ChannelOtherTab } from "./channel-other-tab"
|
||||
|
||||
export function ChatChannelSettings() {
|
||||
const t = useTranslations("ChatChannelSettings")
|
||||
@@ -24,6 +25,7 @@ export function ChatChannelSettings() {
|
||||
<TabsTrigger value="channels">{t("tabs.channels")}</TabsTrigger>
|
||||
<TabsTrigger value="commands">{t("tabs.commands")}</TabsTrigger>
|
||||
<TabsTrigger value="events">{t("tabs.events")}</TabsTrigger>
|
||||
<TabsTrigger value="other">{t("tabs.other")}</TabsTrigger>
|
||||
</TabsList>
|
||||
</section>
|
||||
|
||||
@@ -36,6 +38,9 @@ export function ChatChannelSettings() {
|
||||
<TabsContent value="events" className="mt-0">
|
||||
<ChannelEventsTab />
|
||||
</TabsContent>
|
||||
<TabsContent value="other" className="mt-0">
|
||||
<ChannelOtherTab />
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "القنوات",
|
||||
"commands": "الأوامر",
|
||||
"events": "الأحداث"
|
||||
"events": "الأحداث",
|
||||
"other": "أخرى"
|
||||
},
|
||||
"commands": {
|
||||
"title": "الأوامر المدمجة",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "عندما يواجه الوكيل خطأ",
|
||||
"saved": "تم تحديث فلتر الأحداث.",
|
||||
"saveFailed": "فشل حفظ فلتر الأحداث."
|
||||
},
|
||||
"language": {
|
||||
"title": "لغة الرسائل",
|
||||
"description": "اللغة المستخدمة لإشعارات الأحداث واستجابات الأوامر والتقارير اليومية المرسلة إلى قنوات الدردشة.",
|
||||
"saved": "تم حفظ لغة الرسائل.",
|
||||
"saveFailed": "فشل حفظ لغة الرسائل.",
|
||||
"en": "الإنجليزية",
|
||||
"zh-cn": "الصينية المبسطة",
|
||||
"zh-tw": "الصينية التقليدية",
|
||||
"ja": "اليابانية",
|
||||
"ko": "الكورية",
|
||||
"es": "الإسبانية",
|
||||
"de": "الألمانية",
|
||||
"fr": "الفرنسية",
|
||||
"pt": "البرتغالية",
|
||||
"ar": "العربية"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "Kanäle",
|
||||
"commands": "Befehle",
|
||||
"events": "Ereignisse"
|
||||
"events": "Ereignisse",
|
||||
"other": "Sonstiges"
|
||||
},
|
||||
"commands": {
|
||||
"title": "Integrierte Befehle",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "Wenn ein Agent einen Fehler feststellt",
|
||||
"saved": "Ereignisfilter aktualisiert.",
|
||||
"saveFailed": "Fehler beim Speichern des Ereignisfilters."
|
||||
},
|
||||
"language": {
|
||||
"title": "Nachrichtensprache",
|
||||
"description": "Sprache für Ereignisbenachrichtigungen, Befehlsantworten und tägliche Berichte, die an Chat-Kanäle gesendet werden.",
|
||||
"saved": "Nachrichtensprache gespeichert.",
|
||||
"saveFailed": "Fehler beim Speichern der Nachrichtensprache.",
|
||||
"en": "Englisch",
|
||||
"zh-cn": "Vereinfachtes Chinesisch",
|
||||
"zh-tw": "Traditionelles Chinesisch",
|
||||
"ja": "Japanisch",
|
||||
"ko": "Koreanisch",
|
||||
"es": "Spanisch",
|
||||
"de": "Deutsch",
|
||||
"fr": "Französisch",
|
||||
"pt": "Portugiesisch",
|
||||
"ar": "Arabisch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1714,7 +1714,8 @@
|
||||
"tabs": {
|
||||
"channels": "Channels",
|
||||
"commands": "Commands",
|
||||
"events": "Events"
|
||||
"events": "Events",
|
||||
"other": "Other"
|
||||
},
|
||||
"commands": {
|
||||
"title": "Built-in Commands",
|
||||
@@ -1741,6 +1742,22 @@
|
||||
"errorDesc": "When an agent encounters an error",
|
||||
"saved": "Event filter updated.",
|
||||
"saveFailed": "Failed to save event filter."
|
||||
},
|
||||
"language": {
|
||||
"title": "Message Language",
|
||||
"description": "Language used for event notifications, command responses, and daily reports sent to chat channels.",
|
||||
"saved": "Message language saved.",
|
||||
"saveFailed": "Failed to save message language.",
|
||||
"en": "English",
|
||||
"zh-cn": "Simplified Chinese",
|
||||
"zh-tw": "Traditional Chinese",
|
||||
"ja": "Japanese",
|
||||
"ko": "Korean",
|
||||
"es": "Spanish",
|
||||
"de": "German",
|
||||
"fr": "French",
|
||||
"pt": "Portuguese",
|
||||
"ar": "Arabic"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "Canales",
|
||||
"commands": "Comandos",
|
||||
"events": "Eventos"
|
||||
"events": "Eventos",
|
||||
"other": "Otros"
|
||||
},
|
||||
"commands": {
|
||||
"title": "Comandos integrados",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "Cuando un agente encuentra un error",
|
||||
"saved": "Filtro de eventos actualizado.",
|
||||
"saveFailed": "Error al guardar el filtro de eventos."
|
||||
},
|
||||
"language": {
|
||||
"title": "Idioma de mensajes",
|
||||
"description": "Idioma utilizado para las notificaciones de eventos, respuestas de comandos e informes diarios enviados a los canales de chat.",
|
||||
"saved": "Idioma de mensajes guardado.",
|
||||
"saveFailed": "Error al guardar el idioma de mensajes.",
|
||||
"en": "Inglés",
|
||||
"zh-cn": "Chino simplificado",
|
||||
"zh-tw": "Chino tradicional",
|
||||
"ja": "Japonés",
|
||||
"ko": "Coreano",
|
||||
"es": "Español",
|
||||
"de": "Alemán",
|
||||
"fr": "Francés",
|
||||
"pt": "Portugués",
|
||||
"ar": "Árabe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "Canaux",
|
||||
"commands": "Commandes",
|
||||
"events": "Événements"
|
||||
"events": "Événements",
|
||||
"other": "Autres"
|
||||
},
|
||||
"commands": {
|
||||
"title": "Commandes intégrées",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "Lorsqu'un agent rencontre une erreur",
|
||||
"saved": "Filtre d'événements mis à jour.",
|
||||
"saveFailed": "Échec de l'enregistrement du filtre d'événements."
|
||||
},
|
||||
"language": {
|
||||
"title": "Langue des messages",
|
||||
"description": "Langue utilisée pour les notifications d'événements, les réponses aux commandes et les rapports quotidiens envoyés aux canaux de chat.",
|
||||
"saved": "Langue des messages enregistrée.",
|
||||
"saveFailed": "Échec de l'enregistrement de la langue des messages.",
|
||||
"en": "Anglais",
|
||||
"zh-cn": "Chinois simplifié",
|
||||
"zh-tw": "Chinois traditionnel",
|
||||
"ja": "Japonais",
|
||||
"ko": "Coréen",
|
||||
"es": "Espagnol",
|
||||
"de": "Allemand",
|
||||
"fr": "Français",
|
||||
"pt": "Portugais",
|
||||
"ar": "Arabe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "チャンネル",
|
||||
"commands": "コマンド",
|
||||
"events": "イベント"
|
||||
"events": "イベント",
|
||||
"other": "その他"
|
||||
},
|
||||
"commands": {
|
||||
"title": "組み込みコマンド",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "エージェントがエラーに遭遇した時",
|
||||
"saved": "イベントフィルターを更新しました。",
|
||||
"saveFailed": "イベントフィルターの保存に失敗しました。"
|
||||
},
|
||||
"language": {
|
||||
"title": "メッセージ言語",
|
||||
"description": "イベント通知、コマンド応答、日次レポートをチャットチャンネルに送信する際に使用する言語。",
|
||||
"saved": "メッセージ言語を保存しました。",
|
||||
"saveFailed": "メッセージ言語の保存に失敗しました。",
|
||||
"en": "英語",
|
||||
"zh-cn": "簡体字中国語",
|
||||
"zh-tw": "繁体字中国語",
|
||||
"ja": "日本語",
|
||||
"ko": "韓国語",
|
||||
"es": "スペイン語",
|
||||
"de": "ドイツ語",
|
||||
"fr": "フランス語",
|
||||
"pt": "ポルトガル語",
|
||||
"ar": "アラビア語"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "채널",
|
||||
"commands": "명령어",
|
||||
"events": "이벤트"
|
||||
"events": "이벤트",
|
||||
"other": "기타"
|
||||
},
|
||||
"commands": {
|
||||
"title": "내장 명령어",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "에이전트에 오류가 발생했을 때",
|
||||
"saved": "이벤트 필터가 업데이트되었습니다.",
|
||||
"saveFailed": "이벤트 필터 저장에 실패했습니다."
|
||||
},
|
||||
"language": {
|
||||
"title": "메시지 언어",
|
||||
"description": "이벤트 알림, 명령 응답, 일일 보고서를 채팅 채널로 전송할 때 사용하는 언어입니다.",
|
||||
"saved": "메시지 언어가 저장되었습니다.",
|
||||
"saveFailed": "메시지 언어 저장에 실패했습니다.",
|
||||
"en": "영어",
|
||||
"zh-cn": "중국어 간체",
|
||||
"zh-tw": "중국어 번체",
|
||||
"ja": "일본어",
|
||||
"ko": "한국어",
|
||||
"es": "스페인어",
|
||||
"de": "독일어",
|
||||
"fr": "프랑스어",
|
||||
"pt": "포르투갈어",
|
||||
"ar": "아랍어"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "Canais",
|
||||
"commands": "Comandos",
|
||||
"events": "Eventos"
|
||||
"events": "Eventos",
|
||||
"other": "Outros"
|
||||
},
|
||||
"commands": {
|
||||
"title": "Comandos integrados",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "Quando um agente encontra um erro",
|
||||
"saved": "Filtro de eventos atualizado.",
|
||||
"saveFailed": "Falha ao salvar o filtro de eventos."
|
||||
},
|
||||
"language": {
|
||||
"title": "Idioma das mensagens",
|
||||
"description": "Idioma utilizado para notificações de eventos, respostas de comandos e relatórios diários enviados aos canais de chat.",
|
||||
"saved": "Idioma das mensagens salvo.",
|
||||
"saveFailed": "Falha ao salvar o idioma das mensagens.",
|
||||
"en": "Inglês",
|
||||
"zh-cn": "Chinês simplificado",
|
||||
"zh-tw": "Chinês tradicional",
|
||||
"ja": "Japonês",
|
||||
"ko": "Coreano",
|
||||
"es": "Espanhol",
|
||||
"de": "Alemão",
|
||||
"fr": "Francês",
|
||||
"pt": "Português",
|
||||
"ar": "Árabe"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1714,7 +1714,8 @@
|
||||
"tabs": {
|
||||
"channels": "渠道",
|
||||
"commands": "指令",
|
||||
"events": "事件"
|
||||
"events": "事件",
|
||||
"other": "其他"
|
||||
},
|
||||
"commands": {
|
||||
"title": "内置指令",
|
||||
@@ -1741,6 +1742,22 @@
|
||||
"errorDesc": "代理遇到错误时",
|
||||
"saved": "事件过滤已更新。",
|
||||
"saveFailed": "保存事件过滤失败。"
|
||||
},
|
||||
"language": {
|
||||
"title": "消息语言",
|
||||
"description": "事件通知、指令响应和每日报告推送到消息渠道时使用的语言。",
|
||||
"saved": "消息语言已保存。",
|
||||
"saveFailed": "保存消息语言失败。",
|
||||
"en": "英语",
|
||||
"zh-cn": "简体中文",
|
||||
"zh-tw": "繁体中文",
|
||||
"ja": "日语",
|
||||
"ko": "韩语",
|
||||
"es": "西班牙语",
|
||||
"de": "德语",
|
||||
"fr": "法语",
|
||||
"pt": "葡萄牙语",
|
||||
"ar": "阿拉伯语"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1713,7 +1713,8 @@
|
||||
"tabs": {
|
||||
"channels": "頻道",
|
||||
"commands": "指令",
|
||||
"events": "事件"
|
||||
"events": "事件",
|
||||
"other": "其他"
|
||||
},
|
||||
"commands": {
|
||||
"title": "內建指令",
|
||||
@@ -1740,6 +1741,22 @@
|
||||
"errorDesc": "代理遇到錯誤時",
|
||||
"saved": "事件篩選已更新。",
|
||||
"saveFailed": "儲存事件篩選失敗。"
|
||||
},
|
||||
"language": {
|
||||
"title": "訊息語言",
|
||||
"description": "事件通知、指令回應和每日報告推送到訊息頻道時使用的語言。",
|
||||
"saved": "訊息語言已儲存。",
|
||||
"saveFailed": "儲存訊息語言失敗。",
|
||||
"en": "英語",
|
||||
"zh-cn": "簡體中文",
|
||||
"zh-tw": "繁體中文",
|
||||
"ja": "日語",
|
||||
"ko": "韓語",
|
||||
"es": "西班牙語",
|
||||
"de": "德語",
|
||||
"fr": "法語",
|
||||
"pt": "葡萄牙語",
|
||||
"ar": "阿拉伯語"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1418,3 +1418,11 @@ export async function setChatEventFilter(
|
||||
): Promise<void> {
|
||||
return getTransport().call("set_chat_event_filter", { filter })
|
||||
}
|
||||
|
||||
export async function getChatMessageLanguage(): Promise<string> {
|
||||
return getTransport().call("get_chat_message_language")
|
||||
}
|
||||
|
||||
export async function setChatMessageLanguage(language: string): Promise<void> {
|
||||
return getTransport().call("set_chat_message_language", { language })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user