using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using LocalizationTool.Services;
namespace LocalizationTool.ViewModels
{
///
/// 翻译设置窗口 ViewModel。
/// 语义:BaseUrl / Model / Keys / AppId / Secret 留空 = 内置默认;填了 = 自定义优先;
/// "恢复默认"清空自定义字段。
///
public class TranslationSettingsViewModel : ViewModelBase
{
private readonly Config.ConfigRoot _cfg;
private bool _isBaidu;
private string _baseUrl;
private string _model;
private bool _isLlm;
private string _reference;
private string _testText = "保存配置";
private string _testResult = "翻译结果将显示在这里...";
private LanguageEntry _testLang;
private bool _isBusy;
private List _allModels = new List();
public TranslationSettingsViewModel(Config.ConfigRoot cfg)
{
_cfg = cfg ?? new Config.ConfigRoot();
// 掩码回显
var firstKey = Config.GetOpenAiKeys(_cfg.Translation).FirstOrDefault() ?? "";
OpenAiKeyMask = Config.MaskKey(firstKey);
var (appId, secret) = Config.GetBaiduCredentials(_cfg.Baidu);
BaiduAppIdMask = Config.MaskKey(appId);
BaiduSecretMask = Config.MaskKey(secret);
BaseUrl = Config.ResolveBaseUrl(_cfg.Translation);
Model = Config.ResolveModel(_cfg.Translation);
IsBaidu = string.Equals(_cfg.Translation.Provider, "baidu", StringComparison.OrdinalIgnoreCase);
IsLlm = string.Equals(_cfg.Baidu.ModelType, "llm", StringComparison.OrdinalIgnoreCase);
Reference = _cfg.Baidu.Reference ?? "";
// 输入框初始显示掩码(内容 == 掩码 = 未修改,保存时保持原值)
OpenAiKeyInput = OpenAiKeyMask;
BaiduAppIdInput = BaiduAppIdMask;
BaiduSecretInput = BaiduSecretMask;
TestCommand = new RelayCommand(() => { var _ = TestTranslateAsync(); });
FetchModelsCommand = new RelayCommand(() => { var _ = FetchModelsAsync(); });
ResetOpenAICommand = new RelayCommand(ResetOpenAiToDefault);
ResetBaiduCommand = new RelayCommand(ResetBaiduToDefault);
SaveCommand = new RelayCommand(Save);
CancelCommand = new RelayCommand(Cancel);
SwitchToOpenAiCommand = new RelayCommand(() => IsOpenAi = true);
SwitchToBaiduCommand = new RelayCommand(() => IsBaidu = true);
}
// ---- 引擎选择 ----
public bool IsOpenAi
{
get => !_isBaidu;
set { if (value) IsBaidu = false; }
}
public bool IsBaidu
{
get => _isBaidu;
set { if (Set(ref _isBaidu, value)) { OnPropertyChanged(nameof(IsOpenAi)); UpdateStatus(); } }
}
// ---- OpenAI ----
public string BaseUrl
{
get => _baseUrl;
set => Set(ref _baseUrl, value);
}
public string Model
{
get => _model;
set
{
if (Set(ref _model, value)) OnPropertyChanged(nameof(FilteredModels));
}
}
/// 模型最大上下文(token)输入;空/非法 = 用内置默认(agnes-2.5-flash 512K)。
public string MaxContextTokensText
{
get => _cfg.Translation.MaxContextTokens > 0 ? _cfg.Translation.MaxContextTokens.ToString() : "";
set
{
_cfg.Translation.MaxContextTokens =
int.TryParse(value?.Trim(), out var n) && n > 0 ? n : 0;
}
}
/// 当前生效的最大上下文(含内置默认值说明)。
public string MaxContextHint =>
$"留空 = 内置默认 {Config.BuiltInMaxContextTokens:N0} token(agnes-2.5-flash 规格)";
/// 已存 Key 的掩码(输入框内容 == 掩码 = 未修改)。
public string OpenAiKeyMask
{
get => _openAiKeyMask;
set => Set(ref _openAiKeyMask, value);
}
private string _openAiKeyMask = "";
/// 输入框当前内容(由 View 在 PasswordBox 变化时更新)。
public string OpenAiKeyInput
{
get => _openAiKeyInput;
set => Set(ref _openAiKeyInput, value);
}
private string _openAiKeyInput = "";
// ---- 百度 ----
public string BaiduAppIdMask
{
get => _baiduAppIdMask;
set => Set(ref _baiduAppIdMask, value);
}
private string _baiduAppIdMask = "";
public string BaiduSecretMask
{
get => _baiduSecretMask;
set => Set(ref _baiduSecretMask, value);
}
private string _baiduSecretMask = "";
public string BaiduAppIdInput
{
get => _baiduAppIdInput;
set => Set(ref _baiduAppIdInput, value);
}
private string _baiduAppIdInput = "";
public string BaiduSecretInput
{
get => _baiduSecretInput;
set => Set(ref _baiduSecretInput, value);
}
private string _baiduSecretInput = "";
public bool IsLlm
{
get => _isLlm;
set
{
if (Set(ref _isLlm, value)) OnPropertyChanged(nameof(IsNmt));
}
}
/// NMT 模式(与 IsLlm 互斥)。
public bool IsNmt
{
get => !_isLlm;
set
{
var newLlm = !value;
if (_isLlm == newLlm) return;
_isLlm = newLlm;
OnPropertyChanged(nameof(IsLlm));
OnPropertyChanged(nameof(IsNmt));
}
}
public string Reference
{
get => _reference;
set => Set(ref _reference, value);
}
// ---- 测试 ----
/// 测试目标语言候选(全量语言目录)。
public List TestLanguages => LanguageCatalog.All;
public LanguageEntry TestLang
{
get => _testLang;
set => Set(ref _testLang, value);
}
public string TestText
{
get => _testText;
set => Set(ref _testText, value);
}
public string TestResult
{
get => _testResult;
set => Set(ref _testResult, value);
}
public bool IsBusy
{
get => _isBusy;
set => Set(ref _isBusy, value);
}
// ---- 状态 ----
private string _openAiStatus = "";
private string _baiduStatus = "";
public string OpenAiStatus
{
get => _openAiStatus;
private set => Set(ref _openAiStatus, value);
}
public string BaiduStatus
{
get => _baiduStatus;
private set => Set(ref _baiduStatus, value);
}
/// 请求关闭窗口(true = 保存成功)。
public event Action RequestClose;
// ---- 命令 ----
public RelayCommand TestCommand { get; }
public RelayCommand FetchModelsCommand { get; }
public RelayCommand ResetOpenAICommand { get; }
public RelayCommand ResetBaiduCommand { get; }
public RelayCommand SaveCommand { get; }
public RelayCommand CancelCommand { get; }
public RelayCommand SwitchToOpenAiCommand { get; }
public RelayCommand SwitchToBaiduCommand { get; }
// ---- 初始化 ----
public void Initialize(LanguageEntry defaultLang)
{
TestLang = defaultLang;
UpdateStatus();
}
public void UpdateStatus()
{
OpenAiStatus = Config.IsDefaultOpenAi(_cfg.Translation)
? "✅ 当前使用内置默认 AI(留空即内置默认)"
: "✏️ 当前使用自定义 AI 配置";
BaiduStatus = Config.IsDefaultBaidu(_cfg.Baidu)
? "✅ 当前使用内置默认凭证(留空即内置默认)"
: "✏️ 当前使用自定义凭证";
}
// ---- 恢复默认 ----
private void ResetOpenAiToDefault()
{
Config.ResetOpenAiToDefault(_cfg.Translation);
BaseUrl = Config.BuiltInBaseUrl;
Model = Config.BuiltInModel;
OpenAiKeyMask = Config.MaskKey(Config.GetOpenAiKeys(_cfg.Translation).FirstOrDefault() ?? "");
OpenAiKeyInput = OpenAiKeyMask;
UpdateStatus();
}
private void ResetBaiduToDefault()
{
Config.ResetBaiduToDefault(_cfg.Baidu);
BaiduAppIdMask = Config.MaskKey(Config.GetBaiduCredentials(_cfg.Baidu).appId);
BaiduSecretMask = Config.MaskKey(Config.GetBaiduCredentials(_cfg.Baidu).secret);
BaiduAppIdInput = BaiduAppIdMask;
BaiduSecretInput = BaiduSecretMask;
UpdateStatus();
}
// ---- 保存 ----
private void Save()
{
if (IsOpenAi)
{
_cfg.Translation.Provider = "openai";
_cfg.Translation.ApiKey = null;
var url = BaseUrl?.Trim();
_cfg.Translation.BaseUrl =
(string.IsNullOrEmpty(url) || url == Config.BuiltInBaseUrl) ? "" : url;
var model = Model?.Trim();
_cfg.Translation.Model =
(string.IsNullOrEmpty(model) || model == Config.BuiltInModel) ? "" : model;
if (OpenAiKeyInput.Length == 0)
_cfg.Translation.Keys = new List();
else if (OpenAiKeyInput != OpenAiKeyMask)
_cfg.Translation.Keys = new List { Config.EncryptKey(OpenAiKeyInput) };
}
else
{
_cfg.Translation.Provider = "baidu";
_cfg.Baidu.ModelType = IsLlm ? "llm" : "nmt";
_cfg.Baidu.Reference = Reference?.Trim();
if (BaiduAppIdInput.Length == 0)
_cfg.Baidu.AppId = "";
else if (BaiduAppIdInput != BaiduAppIdMask)
_cfg.Baidu.AppId = Config.EncryptKey(BaiduAppIdInput);
if (BaiduSecretInput.Length == 0)
_cfg.Baidu.Secret = "";
else if (BaiduSecretInput != BaiduSecretMask)
_cfg.Baidu.Secret = Config.EncryptKey(BaiduSecretInput);
}
Config.Save(_cfg);
Translation.Reset();
RequestClose?.Invoke(true);
}
private void Cancel() => RequestClose?.Invoke(false);
// ---- 测试翻译 ----
private async Task TestTranslateAsync()
{
var text = TestText?.Trim();
if (string.IsNullOrEmpty(text))
{
MessageBox.Show("请输入测试文本", "提示");
return;
}
var langEntry = TestLang;
var targetLang = langEntry?.Code ?? "en-US";
var targetDisplay = langEntry?.Display ?? targetLang;
try
{
IsBusy = true;
TestResult = $"翻译中...(目标:{targetDisplay})";
var tempConfig = new Config.ConfigRoot
{
Translation = new Config.TranslationConfig
{
Provider = IsOpenAi ? "openai" : "baidu",
BaseUrl = string.IsNullOrWhiteSpace(BaseUrl) ? Config.BuiltInBaseUrl : BaseUrl.Trim(),
Model = string.IsNullOrWhiteSpace(Model) ? Config.BuiltInModel : Model.Trim(),
Keys = OpenAiKeyInput.Length > 0 && OpenAiKeyInput != OpenAiKeyMask
? new List { Config.EncryptKey(OpenAiKeyInput) }
: (_cfg.Translation.Keys ?? new List()),
},
Baidu = new Config.BaiduConfig
{
AppId = BaiduAppIdInput.Length > 0 && BaiduAppIdInput != BaiduAppIdMask
? Config.EncryptKey(BaiduAppIdInput) : _cfg.Baidu.AppId,
Secret = BaiduSecretInput.Length > 0 && BaiduSecretInput != BaiduSecretMask
? Config.EncryptKey(BaiduSecretInput) : _cfg.Baidu.Secret,
ModelType = IsLlm ? "llm" : "nmt",
Reference = Reference?.Trim(),
}
};
Translation.Reset();
var provider = Translation.Create(tempConfig);
var result = await provider.TranslateAsync(text, "zh", targetLang);
TestResult = $"✅ 翻译成功(引擎:{provider.Name},目标:{targetDisplay}):\n{result}";
}
catch (Exception ex)
{
TestResult = $"❌ 翻译失败:\n{ex.Message}";
}
finally
{
IsBusy = false;
}
}
// ---- 获取模型列表 ----
private async Task FetchModelsAsync()
{
var baseUrl = string.IsNullOrWhiteSpace(BaseUrl)
? Config.BuiltInBaseUrl
: BaseUrl.Trim().TrimEnd('/');
var key = ResolveOpenAiKey();
if (string.IsNullOrWhiteSpace(baseUrl) || string.IsNullOrWhiteSpace(key))
{
MessageBox.Show("请先填写 Base URL 和 API Key", "提示");
return;
}
try
{
IsBusy = true;
using (var client = new System.Net.Http.HttpClient { Timeout = TimeSpan.FromSeconds(20) })
{
client.DefaultRequestHeaders.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", key);
var resp = await client.GetAsync(baseUrl + "/models");
var body = await resp.Content.ReadAsStringAsync();
var j = Newtonsoft.Json.Linq.JObject.Parse(body);
var arr = j["data"] as Newtonsoft.Json.Linq.JArray;
var models = arr?.Select(t => (string)t["id"])
.Where(s => !string.IsNullOrEmpty(s))
.Distinct()
.OrderBy(s => s)
.ToList() ?? new List();
if (models.Count == 0)
{
MessageBox.Show("接口返回为空,未获取到模型", "提示");
return;
}
_allModels = models;
Model = models.First();
OnPropertyChanged(nameof(FilteredModels));
}
}
catch (Exception ex)
{
MessageBox.Show($"获取模型失败:{ex.Message}", "错误");
}
finally
{
IsBusy = false;
}
}
private string ResolveOpenAiKey()
{
var input = OpenAiKeyInput?.Trim() ?? "";
if (!string.IsNullOrEmpty(input) && input != OpenAiKeyMask) return input;
return Config.GetOpenAiKeys(_cfg.Translation).FirstOrDefault() ?? "";
}
/// 模型下拉候选(按 Model 输入过滤)。
public List FilteredModels
{
get
{
if (_allModels.Count == 0) return null;
var kw = Model?.Trim() ?? "";
return string.IsNullOrEmpty(kw)
? _allModels
: _allModels.Where(m => m.IndexOf(kw, StringComparison.OrdinalIgnoreCase) >= 0).ToList();
}
}
public void RefreshModelFilter() => OnPropertyChanged(nameof(FilteredModels));
}
}