using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.Versioning;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Xml.Linq;
using Autolocalization.Localization;
using Autolocalization.Services;
using Autolocalization.Utils;
using BepInEx;
using BepInEx.Configuration;
using BepInEx.Logging;
using HarmonyLib;
using Microsoft.CodeAnalysis;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
[assembly: CompilationRelaxations(8)]
[assembly: RuntimeCompatibility(WrapNonExceptionThrows = true)]
[assembly: Debuggable(DebuggableAttribute.DebuggingModes.IgnoreSymbolStoreSequencePoints)]
[assembly: TargetFramework(".NETFramework,Version=v4.8.1", FrameworkDisplayName = ".NET Framework 4.8.1")]
[assembly: AssemblyCompany("Autolocalization")]
[assembly: AssemblyConfiguration("Release")]
[assembly: AssemblyFileVersion("1.5.3.0")]
[assembly: AssemblyInformationalVersion("1.5.3+bb6808fdaad04e590f02d08a83c95397ff7c8f12")]
[assembly: AssemblyProduct("Autolocalization")]
[assembly: AssemblyTitle("Autolocalization")]
[assembly: AssemblyVersion("1.5.3.0")]
[module: RefSafetyRules(11)]
namespace Microsoft.CodeAnalysis
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
internal sealed class EmbeddedAttribute : Attribute
{
}
}
namespace System.Runtime.CompilerServices
{
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Event | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, AllowMultiple = false, Inherited = false)]
internal sealed class NullableAttribute : Attribute
{
public readonly byte[] NullableFlags;
public NullableAttribute(byte P_0)
{
NullableFlags = new byte[1] { P_0 };
}
public NullableAttribute(byte[] P_0)
{
NullableFlags = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Interface | AttributeTargets.Delegate, AllowMultiple = false, Inherited = false)]
internal sealed class NullableContextAttribute : Attribute
{
public readonly byte Flag;
public NullableContextAttribute(byte P_0)
{
Flag = P_0;
}
}
[CompilerGenerated]
[Microsoft.CodeAnalysis.Embedded]
[AttributeUsage(AttributeTargets.Module, AllowMultiple = false, Inherited = false)]
internal sealed class RefSafetyRulesAttribute : Attribute
{
public readonly int Version;
public RefSafetyRulesAttribute(int P_0)
{
Version = P_0;
}
}
}
namespace Autolocalization
{
[BepInPlugin(".com.HsgtLgt.autolocalization", "智能本地化引擎", "1.5.3")]
public class Autolocalization : BaseUnityPlugin
{
[HarmonyPatch(typeof(ConfigDescription))]
public static class ConfigDescriptionPatch
{
private static readonly ConcurrentDictionary<string, string> _descriptionCache = new ConcurrentDictionary<string, string>();
private static readonly ConcurrentDictionary<string, bool> _processedKeys = new ConcurrentDictionary<string, bool>();
private static readonly object _exportLock = new object();
private static readonly List<string> _exportBuffer = new List<string>(1000);
private static long _processedCount = 0L;
private static long _cacheHits = 0L;
public static long _translatedCount = 0L;
public static long _unchangedCount = 0L;
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyPrefix]
public static void Prefix(ref string description)
{
if (string.IsNullOrWhiteSpace(description))
{
return;
}
if (_descriptionCache.TryGetValue(description, out string value))
{
Interlocked.Increment(ref _cacheHits);
description = value;
PerformanceMonitor.RecordProcessing(0L, 1L);
return;
}
string text = ProcessDescription(description);
if (text != description)
{
Interlocked.Increment(ref _translatedCount);
}
else
{
Interlocked.Increment(ref _unchangedCount);
}
if (_descriptionCache.Count < 50000)
{
_descriptionCache.TryAdd(description, text);
}
description = text;
Interlocked.Increment(ref _processedCount);
PerformanceMonitor.RecordProcessing(1L, 0L);
AddToExportBuffer(description);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ProcessDescription(string original)
{
string text = _smartLocalizationManager?.GetTranslation(original);
if (!string.IsNullOrEmpty(text) && text != original && text != null)
{
return text;
}
return original;
}
private static void AddToExportBuffer(string description)
{
lock (_exportLock)
{
_exportBuffer.Add(description);
if (_exportBuffer.Count >= 1000)
{
ExportBatch();
}
}
}
private static void ExportBatch()
{
if (_exportBuffer.Count != 0)
{
string[] array = _exportBuffer.ToArray();
_exportBuffer.Clear();
ManualLogSource? slog = Slog;
if (slog != null)
{
slog.LogInfo((object)$"批量导出 {array.Length} 条描述数据");
}
}
}
public static void FlushExportBuffer()
{
lock (_exportLock)
{
ExportBatch();
}
}
}
[HarmonyPatch(typeof(ConfigDefinition))]
public static class ConfigDefinitionPatch
{
private static readonly ConcurrentDictionary<string, string> _keyCache = new ConcurrentDictionary<string, string>();
private static readonly ConcurrentDictionary<string, string> _sectionCache = new ConcurrentDictionary<string, string>();
[HarmonyPatch(/*Could not decode attribute arguments.*/)]
[HarmonyPrefix]
public static void Prefix(ref string section, ref string key)
{
if (!string.IsNullOrWhiteSpace(section))
{
if (_sectionCache.TryGetValue(section, out string value))
{
section = value;
}
else
{
string text = ProcessSection(section);
if (_sectionCache.Count < 10000)
{
_sectionCache.TryAdd(section, text);
}
section = text;
}
}
if (string.IsNullOrWhiteSpace(key))
{
return;
}
if (_keyCache.TryGetValue(key, out string value2))
{
key = value2;
return;
}
string text2 = ProcessKey(key);
if (_keyCache.Count < 10000)
{
_keyCache.TryAdd(key, text2);
}
key = text2;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ProcessSection(string original)
{
string text = _smartLocalizationManager?.GetTranslation(original);
if (!string.IsNullOrEmpty(text) && text != original && text != null)
{
return text;
}
return original;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private static string ProcessKey(string original)
{
string text = _smartLocalizationManager?.GetTranslation(original);
if (!string.IsNullOrEmpty(text) && text != original && text != null)
{
return text;
}
return original;
}
}
public const string PluginGuid = ".com.HsgtLgt.autolocalization";
public const string PluginName = "智能本地化引擎";
public const string PluginVersion = "1.5.3";
private static readonly Harmony Harmony = new Harmony(".com.HsgtLgt.autolocalization");
public static ManualLogSource? Slog;
private static SmartLocalizationManager? _smartLocalizationManager;
private static LocalizationFileManager? _fileManager;
public static Autolocalization? Instance { get; private set; }
public static string LogLevel { get; private set; } = "Basic";
private void Awake()
{
Slog = ((BaseUnityPlugin)this).Logger;
Instance = this;
LoadConfigurations();
PerformanceMonitor.SetLogSource(Slog);
PerformanceMonitor.StartMonitoring();
PerformanceMonitor.StartPeriodicReporting(30, LogLevel);
InitializeTranslationServicesSync();
Slog.LogDebug((object)FormatLog("INFO", "Main", "版本1.5.3启动成功"));
Harmony.PatchAll();
Task.Delay(5000).ContinueWith(delegate
{
if (ConfigDescriptionPatch._translatedCount > 0 || ConfigDescriptionPatch._unchangedCount > 0)
{
ManualLogSource? slog = Slog;
if (slog != null)
{
slog.LogInfo((object)FormatLog("INFO", "Config", $"已处理配置: {ConfigDescriptionPatch._translatedCount}项已翻译, {ConfigDescriptionPatch._unchangedCount}项未变化"));
}
}
});
}
private void LoadConfigurations()
{
LogLevel = ((BaseUnityPlugin)this).Config.Bind<string>("General", "LogLevel", "None", "日志详细程度。可选: None(仅错误)/Basic(基本进度)/Detailed(详细统计)/Debug(调试信息)").Value;
}
private void InitializeTranslationServicesSync()
{
try
{
_fileManager = new LocalizationFileManager();
_smartLocalizationManager = new SmartLocalizationManager(_fileManager);
_smartLocalizationManager.SetLogSource(Slog);
Slog.LogDebug((object)"智能本地化管理器初始化完成");
Task<bool> task = _smartLocalizationManager.InitializeAsync();
task.Wait();
if (task.Result)
{
Slog.LogDebug((object)"✅ 智能本地化系统同步初始化成功");
Slog.LogDebug((object)$"当前阶段: {_smartLocalizationManager.CurrentPhase}");
}
else
{
Slog.LogError((object)"❌ 智能本地化系统同步初始化失败");
}
Slog.LogDebug((object)"翻译服务同步初始化完成");
}
catch (Exception ex)
{
Slog.LogError((object)("同步初始化翻译服务失败: " + ex.Message));
}
}
public static string FormatLog(string level, string module, string message)
{
return LogFormatter.FormatLog(level, module, message);
}
public void OnDestroy()
{
_smartLocalizationManager?.Dispose();
PerformanceMonitor.StopMonitoring();
}
}
public static class PerformanceMonitor
{
private static readonly Stopwatch _stopwatch = new Stopwatch();
private static long _totalProcessed = 0L;
private static long _totalCacheHits = 0L;
private static readonly object _lock = new object();
private static ManualLogSource? _logSource;
public static void SetLogSource(ManualLogSource logSource)
{
_logSource = logSource;
}
public static void StartMonitoring()
{
_stopwatch.Start();
}
public static void StopMonitoring()
{
_stopwatch.Stop();
}
public static void RecordProcessing(long processed, long cacheHits)
{
lock (_lock)
{
_totalProcessed += processed;
_totalCacheHits += cacheHits;
}
}
public static PerformanceStats GetCurrentStats()
{
lock (_lock)
{
long elapsedMilliseconds = _stopwatch.ElapsedMilliseconds;
long totalProcessed = _totalProcessed;
long totalCacheHits = _totalCacheHits;
double cacheHitRate = ((totalProcessed > 0) ? ((double)totalCacheHits / (double)totalProcessed * 100.0) : 0.0);
double processingRate = ((elapsedMilliseconds > 0) ? ((double)totalProcessed / (double)elapsedMilliseconds * 1000.0) : 0.0);
PerformanceStats result = default(PerformanceStats);
result.TotalProcessed = totalProcessed;
result.TotalCacheHits = totalCacheHits;
result.CacheHitRate = cacheHitRate;
result.ProcessingRate = processingRate;
result.ElapsedMilliseconds = elapsedMilliseconds;
result.MemoryUsage = GC.GetTotalMemory(forceFullCollection: false);
return result;
}
}
private static void LogPerformanceReport()
{
PerformanceStats currentStats = GetCurrentStats();
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)"=== 性能监控报告 ===");
}
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogInfo((object)$"总处理数量: {currentStats.TotalProcessed:N0}");
}
ManualLogSource? logSource3 = _logSource;
if (logSource3 != null)
{
logSource3.LogInfo((object)$"缓存命中数: {currentStats.TotalCacheHits:N0}");
}
ManualLogSource? logSource4 = _logSource;
if (logSource4 != null)
{
logSource4.LogInfo((object)$"缓存命中率: {currentStats.CacheHitRate:F2}%");
}
ManualLogSource? logSource5 = _logSource;
if (logSource5 != null)
{
logSource5.LogInfo((object)$"处理速率: {currentStats.ProcessingRate:F0} 条/秒");
}
ManualLogSource? logSource6 = _logSource;
if (logSource6 != null)
{
logSource6.LogInfo((object)$"总耗时: {currentStats.ElapsedMilliseconds:N0} 毫秒");
}
ManualLogSource? logSource7 = _logSource;
if (logSource7 != null)
{
logSource7.LogInfo((object)$"内存使用: {currentStats.MemoryUsage / 1024 / 1024:F2} MB");
}
ManualLogSource? logSource8 = _logSource;
if (logSource8 != null)
{
logSource8.LogInfo((object)"==================");
}
}
public static void StartPeriodicReporting(int intervalSeconds = 30, string logLevel = "Basic")
{
if (intervalSeconds > 0 && !(logLevel == "None"))
{
Timer timer = new Timer(ReportPeriodicStats, logLevel, TimeSpan.FromSeconds(intervalSeconds), TimeSpan.FromSeconds(intervalSeconds));
}
}
private static void ReportPeriodicStats(object state)
{
string text = (state as string) ?? "Basic";
PerformanceStats currentStats = GetCurrentStats();
if (currentStats.TotalProcessed <= 0 || !(text != "None"))
{
return;
}
if (text == "Debug" || text == "Detailed")
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)$"[拦截统计] 配置拦截: {currentStats.TotalProcessed:N0}次, 拦截速率: {currentStats.ProcessingRate:F0}次/秒, 拦截缓存命中: {currentStats.CacheHitRate:F2}%");
}
}
else
{
_ = text == "Basic";
}
}
}
public struct PerformanceStats
{
public long TotalProcessed { get; set; }
public long TotalCacheHits { get; set; }
public double CacheHitRate { get; set; }
public double ProcessingRate { get; set; }
public long ElapsedMilliseconds { get; set; }
public long MemoryUsage { get; set; }
}
}
namespace Autolocalization.Utils
{
public static class LogFormatter
{
public static string FormatLog(string level, string module, string message)
{
string text = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.fff");
string text2 = level switch
{
"ERROR" => "\ud83d\udd34",
"WARN" => "\ud83d\udfe1",
"INFO" => "\ud83d\udd35",
"DEBUG" => "⚪",
_ => "⚪",
};
return $"{text2} [{text}] [{level,-5}] [{module,-20}] {message}";
}
public static string FormatSection(string title)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("");
stringBuilder.AppendLine("\ud83d\udccb " + new string('═', 60));
stringBuilder.AppendLine(" " + title);
stringBuilder.AppendLine(" " + new string('═', 60));
return stringBuilder.ToString();
}
public static string FormatConfigTable(string title, params (string key, string value)[] configs)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("⚙\ufe0f " + title);
stringBuilder.AppendLine(" " + new string('─', 50));
for (int i = 0; i < configs.Length; i++)
{
var (arg, arg2) = configs[i];
stringBuilder.AppendLine($" {arg,-25}: {arg2}");
}
stringBuilder.AppendLine(" " + new string('─', 50));
return stringBuilder.ToString();
}
public static string FormatProgressBar(int current, int total, string description = "")
{
double num = ((total > 0) ? ((double)current / (double)total) : 0.0);
int num2 = (int)(30.0 * num);
int count = 30 - num2;
string text = "[" + new string('█', num2) + new string('░', count) + "]";
string text2 = $"{num * 100.0:F1}%";
return $"\ud83d\udcca {description} {text} {current}/{total} ({text2})";
}
public static string FormatPerformanceStats(string operation, TimeSpan elapsed, int itemsProcessed)
{
double num = ((itemsProcessed > 0) ? ((double)itemsProcessed / elapsed.TotalSeconds) : 0.0);
double num2 = ((itemsProcessed > 0) ? (elapsed.TotalMilliseconds / (double)itemsProcessed) : 0.0);
return $"⚡ {operation} - 耗时: {elapsed.TotalSeconds:F1}s, 处理: {itemsProcessed}项, " + $"吞吐量: {num:F1}项/s, 平均: {num2:F1}ms/项";
}
public static string FormatErrorDetails(string context, Exception ex)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("❌ " + context);
stringBuilder.AppendLine(" 错误类型: " + ex.GetType().Name);
stringBuilder.AppendLine(" 错误消息: " + ex.Message);
if (!string.IsNullOrEmpty(ex.StackTrace))
{
stringBuilder.AppendLine(" 堆栈跟踪:");
string[] array = ex.StackTrace.Split(new char[1] { '\n' });
string[] array2 = array;
foreach (string text in array2)
{
if (!string.IsNullOrWhiteSpace(text))
{
stringBuilder.AppendLine(" " + text.Trim());
}
}
}
return stringBuilder.ToString();
}
public static string FormatBatchSummary(string operation, int total, int success, int failed, int cached = 0)
{
double num = ((total > 0) ? ((double)success / (double)total * 100.0) : 0.0);
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.AppendLine("\ud83d\udcc8 " + operation + " 摘要");
stringBuilder.AppendLine($" 总计: {total} 项");
stringBuilder.AppendLine($" 成功: {success} 项 ({num:F1}%)");
if (failed > 0)
{
stringBuilder.AppendLine($" 失败: {failed} 项");
}
if (cached > 0)
{
stringBuilder.AppendLine($" 缓存: {cached} 项");
}
return stringBuilder.ToString();
}
}
}
namespace Autolocalization.Services
{
public static class PathManager
{
public static string GetPluginDirectory()
{
string location = typeof(PathManager).Assembly.Location;
return Path.GetDirectoryName(location) ?? "";
}
public static string GetProjectDirectory()
{
return Path.Combine(Paths.ConfigPath, "Autolocalization");
}
public static string GetTranslationsFilePath()
{
return Path.Combine(GetProjectDirectory(), "translations.yaml");
}
public static string GetCollectedItemsFilePath()
{
return Path.Combine(GetProjectDirectory(), "collected_items.yaml");
}
public static void EnsureDirectoryExists(string directoryPath)
{
if (!Directory.Exists(directoryPath))
{
Directory.CreateDirectory(directoryPath);
}
}
}
public class SmartLocalizationManager : IDisposable
{
private readonly LocalizationFileManager _fileManager = fileManager ?? throw new ArgumentNullException("fileManager");
private readonly string _translationFilePath = PathManager.GetTranslationsFilePath();
private readonly string _collectionFilePath = PathManager.GetCollectedItemsFilePath();
private readonly string? _configFilePath;
private readonly string? _cacheFilePath;
private readonly ConcurrentDictionary<string, string> _translations = new ConcurrentDictionary<string, string>();
private readonly ConcurrentDictionary<string, string> _collectedItems = new ConcurrentDictionary<string, string>();
private readonly ConcurrentDictionary<string, DateTime> _translationTimestamps = new ConcurrentDictionary<string, DateTime>();
private readonly object _stateLock = new object();
private LocalizationPhase _currentPhase;
private bool _isInitialized;
private bool _isDisposed;
private readonly object _saveLock = new object();
private bool _isSaving;
private bool _needsSave;
private int _totalCollected;
private int _totalTranslated;
private int _totalApplied;
private int _cacheHits;
private int _cacheMisses;
private long _lastSavedCount;
private long _translationQueryCount;
private long _translationCacheHits;
private long _translationNewCount;
private DateTime _lastCollectTime = DateTime.MinValue;
private Timer? _collectionEndCheckTimer;
private bool _collectionEnded;
private int _unsavedCount;
private readonly ConcurrentDictionary<string, string> _reverseTranslations = new ConcurrentDictionary<string, string>();
private ManualLogSource? _logSource;
public LocalizationPhase CurrentPhase => _currentPhase;
public bool IsInitialized => _isInitialized;
public LocalizationStatistics Statistics => GetStatistics();
public event Action<LocalizationPhase, LocalizationPhase>? OnPhaseChanged;
public event Action<string, string>? OnTranslationAdded;
public event Action<string, string, string>? OnTranslationUpdated;
public event Action<LocalizationStatistics>? OnStatisticsUpdated;
public SmartLocalizationManager(LocalizationFileManager fileManager)
{
}
public void SetLogSource(ManualLogSource logSource)
{
_logSource = logSource;
}
public async Task<bool> InitializeAsync()
{
if (_isInitialized)
{
return true;
}
try
{
string projectDirectory = PathManager.GetProjectDirectory();
PathManager.EnsureDirectoryExists(projectDirectory);
if (File.Exists(_translationFilePath))
{
try
{
await LoadTranslationsAsync();
}
catch (Exception ex)
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogError((object)("❌ 从新位置加载翻译文件失败: " + ex.Message));
}
}
}
Dictionary<string, string> dictionary = null;
if (File.Exists(_collectionFilePath))
{
try
{
dictionary = _fileManager.LoadFromYaml(_collectionFilePath);
}
catch (Exception ex2)
{
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogError((object)("加载收集文件失败: " + ex2.Message));
}
}
}
if (dictionary != null && dictionary.Count > 0 && !File.Exists(_translationFilePath))
{
ManualLogSource? logSource3 = _logSource;
if (logSource3 != null)
{
logSource3.LogInfo((object)"\ud83d\udcdd 发现收集文件但缺少翻译文件,创建空模板...");
}
Dictionary<string, string> translations = new Dictionary<string, string>();
_fileManager.SaveToYaml(translations, _translationFilePath);
ManualLogSource? logSource4 = _logSource;
if (logSource4 != null)
{
logSource4.LogInfo((object)("✅ 已创建空的翻译文件: " + _translationFilePath));
}
ManualLogSource? logSource5 = _logSource;
if (logSource5 != null)
{
logSource5.LogInfo((object)$"\ud83d\udca1 提示: 请参考 collected_items.yaml ({dictionary.Count} 项) 进行翻译");
}
ManualLogSource? logSource6 = _logSource;
if (logSource6 != null)
{
logSource6.LogInfo((object)"\ud83d\udca1 将翻译添加到 translations.yaml 格式: 原文: 翻译");
}
}
bool flag = false;
if (dictionary != null && dictionary.Count > 0)
{
int num = dictionary.Keys.Count((string key) => !_translations.ContainsKey(key) || string.IsNullOrWhiteSpace(_translations[key]));
if (num > 0)
{
flag = true;
LogInfo($"\ud83d\udcdd 发现收集文件中有 {num} 项未翻译内容,保持在收集阶段");
}
else
{
LogInfo($"✅ 收集文件中的所有 {dictionary.Count} 项都已翻译");
}
}
if (_translations.Count == 0)
{
ChangePhase(LocalizationPhase.Collection);
LogInfo("未找到翻译数据,进入收集阶段");
}
else if (flag)
{
ChangePhase(LocalizationPhase.Collection);
LogInfo("有未翻译的收集项,保持在收集阶段");
}
else
{
ChangePhase(LocalizationPhase.Application);
LogInfo("✅ 所有翻译数据完整,进入应用阶段");
}
await CleanupCollectedItemsAsync(dictionary);
await LoadCacheAsync();
_collectionEndCheckTimer = new Timer(CheckCollectionEnd, null, 5000, 5000);
_isInitialized = true;
return true;
}
catch (Exception ex3)
{
ManualLogSource? logSource7 = _logSource;
if (logSource7 != null)
{
logSource7.LogError((object)("❌ 初始化失败: " + ex3.Message));
}
return false;
}
}
public string GetTranslation(string originalText, bool autoCollect = true)
{
if (string.IsNullOrWhiteSpace(originalText))
{
return originalText;
}
Interlocked.Increment(ref _translationQueryCount);
if (_translations.TryGetValue(originalText, out string value))
{
Interlocked.Increment(ref _cacheHits);
Interlocked.Increment(ref _translationCacheHits);
return value;
}
if (_reverseTranslations.ContainsKey(originalText))
{
Interlocked.Increment(ref _cacheHits);
Interlocked.Increment(ref _translationCacheHits);
return originalText;
}
Interlocked.Increment(ref _cacheMisses);
if (autoCollect)
{
CollectText(originalText);
Interlocked.Increment(ref _translationNewCount);
}
return originalText;
}
public void StartCollectionPhase()
{
lock (_stateLock)
{
ChangePhase(LocalizationPhase.Collection);
}
LogInfo("\ud83d\udccb 开始收集阶段 - 将收集所有需要翻译的内容");
}
public async Task EndCollectionPhaseAsync()
{
lock (_stateLock)
{
ChangePhase(LocalizationPhase.Application);
}
LogInfo($"\ud83d\udccb 收集阶段结束 - 共收集 {_totalCollected} 项内容");
await SaveCollectedItemsAsync();
LogInfo("✅ 收集阶段完成");
}
public async Task<bool> StartApplicationPhaseAsync()
{
try
{
if (File.Exists(_translationFilePath))
{
await LoadTranslationsAsync();
ChangePhase(LocalizationPhase.Application);
LogInfo($"✅ 应用阶段开始 - 已加载 {_totalTranslated} 条翻译");
return true;
}
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogWarning((object)"⚠\ufe0f 未找到翻译文件,将进入收集阶段");
}
StartCollectionPhase();
return false;
}
catch (Exception ex)
{
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogError((object)("❌ 加载翻译文件失败: " + ex.Message));
}
StartCollectionPhase();
return false;
}
}
public void UpdateTranslation(string key, string newTranslation)
{
if (!string.IsNullOrWhiteSpace(key) && !string.IsNullOrWhiteSpace(newTranslation))
{
string orAdd = _translations.GetOrAdd(key, newTranslation);
_translations[key] = newTranslation;
_translationTimestamps[key] = DateTime.Now;
_reverseTranslations[newTranslation] = key;
this.OnTranslationUpdated?.Invoke(key, orAdd, newTranslation);
LogInfo("\ud83d\udd04 更新翻译: " + key + " -> " + newTranslation, "Debug");
}
}
public int CleanupTranslatedItems()
{
List<string> list = _collectedItems.Keys.Where((string key) => _translations.ContainsKey(key)).ToList();
foreach (string item in list)
{
_collectedItems.TryRemove(item, out string _);
}
if (list.Count > 0)
{
LogInfo($"\ud83e\uddf9 清理了 {list.Count} 个已翻译的收集项", "Detailed");
}
return list.Count;
}
public Task<bool> ReloadTranslationsAsync()
{
try
{
LoadTranslationsAsync().Wait();
LogInfo("\ud83d\udd04 翻译文件重新加载完成");
return Task.FromResult(result: true);
}
catch (Exception ex)
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogError((object)("❌ 重新加载翻译文件失败: " + ex.Message));
}
return Task.FromResult(result: false);
}
}
public Task ExportMissingTranslationsAsync()
{
Dictionary<string, string> dictionary = _collectedItems.Where<KeyValuePair<string, string>>((KeyValuePair<string, string> kvp) => !_translations.ContainsKey(kvp.Key)).ToDictionary((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => kvp.Value);
if (dictionary.Count > 0)
{
string text = Path.Combine(PathManager.GetProjectDirectory(), "missing_translations.yaml");
_fileManager.SaveToYaml(dictionary, text);
LogInfo($"\ud83d\udce4 已导出 {dictionary.Count} 条缺失翻译到: {text}", "Detailed");
}
return Task.CompletedTask;
}
public void ClearCache()
{
_translations.Clear();
_translationTimestamps.Clear();
_cacheHits = 0;
_cacheMisses = 0;
LogInfo("\ud83e\uddf9 翻译缓存已清理", "Debug");
}
public Dictionary<string, string> GetCollectedItems()
{
return _collectedItems.ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => kvp.Value);
}
public Dictionary<string, string> GetCurrentTranslations()
{
return _translations.ToDictionary<KeyValuePair<string, string>, string, string>((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => kvp.Value);
}
public void UpdateTranslations(Dictionary<string, string> translations)
{
LogInfo($"\ud83d\udd04 批量更新翻译: {translations.Count} 条", "Debug");
foreach (KeyValuePair<string, string> translation in translations)
{
UpdateTranslation(translation.Key, translation.Value);
}
LogInfo($"✅ 翻译更新完成,当前缓存中共有 {_translations.Count} 条翻译", "Debug");
}
private void LogInfo(string message, string requiredLevel = "Basic")
{
if (_logSource != null)
{
string text = LogFormatter.FormatLog("INFO", "SmartLocalization", message);
switch (requiredLevel)
{
case "Basic":
_logSource.LogInfo((object)text);
break;
case "Detailed":
_logSource.LogInfo((object)text);
break;
case "Debug":
_logSource.LogInfo((object)text);
break;
}
}
}
private void CheckCollectionEnd(object? state)
{
try
{
if (_collectionEnded || _collectedItems.Count == 0 || _lastCollectTime == DateTime.MinValue)
{
return;
}
double totalSeconds = (DateTime.Now - _lastCollectTime).TotalSeconds;
if (totalSeconds >= 10.0 && _unsavedCount > 0)
{
_collectionEnded = true;
LogInfo($"✅ 检测到收集阶段结束({totalSeconds:F1}秒无新增),准备保存...");
Task task = SaveCollectedItemsAsync();
task.Wait();
LogInfo($"\ud83d\udcbe 收集结束自动保存完成: {_collectedItems.Count} 项");
LogInfo($"\ud83d\udcca 收集统计: 总检测 {_totalCollected} 项, 需要翻译 {_collectedItems.Count} 项");
if (!File.Exists(_translationFilePath))
{
Task task2 = GenerateTranslationTemplateAsync();
task2.Wait();
}
_collectionEndCheckTimer?.Dispose();
_collectionEndCheckTimer = null;
LogTranslationCacheStats();
if (_collectedItems.Count > 0)
{
ChangePhase(LocalizationPhase.Application);
}
}
}
catch (Exception ex)
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogError((object)("❌ 检查收集结束时出错: " + ex.Message));
}
}
}
private void CollectText(string text)
{
if (_collectedItems.TryAdd(text, text))
{
Interlocked.Increment(ref _totalCollected);
Interlocked.Increment(ref _unsavedCount);
this.OnTranslationAdded?.Invoke(text, text);
_lastCollectTime = DateTime.Now;
if (_totalCollected % 100 == 0)
{
LogInfo($"\ud83d\udcdd 已收集 {_totalCollected} 项待翻译内容", "Detailed");
}
}
}
private void ChangePhase(LocalizationPhase newPhase)
{
LocalizationPhase currentPhase = _currentPhase;
_currentPhase = newPhase;
this.OnPhaseChanged?.Invoke(currentPhase, newPhase);
}
private Task LoadTranslationsAsync()
{
try
{
Dictionary<string, string> dictionary = _fileManager.LoadFromYaml(_translationFilePath);
_translations.Clear();
_reverseTranslations.Clear();
int num = 0;
int num2 = 0;
foreach (KeyValuePair<string, string> item in dictionary)
{
_translations[item.Key] = item.Value;
_translationTimestamps[item.Key] = DateTime.Now;
if (!string.IsNullOrWhiteSpace(item.Value))
{
_reverseTranslations[item.Value] = item.Key;
num++;
}
else
{
num2++;
}
}
_totalTranslated = dictionary.Count;
_totalApplied = num;
LogInfo("\ud83d\udce5 已加载翻译文件: " + _translationFilePath);
LogInfo($" \ud83d\udcca 总条目: {dictionary.Count}, 有效翻译: {num}, 待翻译: {num2}", "Detailed");
if (num2 > 0)
{
LogInfo($"\ud83d\udca1 提示: 有 {num2} 项待翻译,您可以编辑翻译文件进行翻译");
}
}
catch (Exception ex)
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogError((object)("加载翻译文件失败: " + ex.Message));
}
}
return Task.CompletedTask;
}
public Task SaveCollectedItemsAsync()
{
if (_collectedItems.Count == 0)
{
return Task.CompletedTask;
}
if (_collectedItems.Count == _lastSavedCount)
{
return Task.CompletedTask;
}
try
{
Dictionary<string, string> dictionary = _collectedItems.Where<KeyValuePair<string, string>>((KeyValuePair<string, string> kvp) => !_translations.ContainsKey(kvp.Key) || string.IsNullOrWhiteSpace(_translations[kvp.Key])).ToDictionary((KeyValuePair<string, string> kvp) => kvp.Key, (KeyValuePair<string, string> kvp) => kvp.Value);
int num = _collectedItems.Count - dictionary.Count;
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)$"\ud83d\udcbe 保存收集项: {dictionary.Count} 项 (已过滤 {num} 项已翻译内容)");
}
_fileManager.SaveToYaml(dictionary, _collectionFilePath);
_lastSavedCount = dictionary.Count;
_unsavedCount = 0;
}
catch (Exception ex)
{
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogError((object)("❌ 保存收集项失败: " + ex.Message));
}
}
return Task.CompletedTask;
}
private Task GenerateTranslationTemplateAsync()
{
try
{
if (File.Exists(_translationFilePath))
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)"\ud83d\udcdd 翻译文件已存在,跳过模板生成");
}
return Task.CompletedTask;
}
Dictionary<string, string> translations = new Dictionary<string, string>();
_fileManager.SaveToYaml(translations, _translationFilePath);
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogInfo((object)("\ud83d\udcdd 已创建空的翻译文件模板: " + _translationFilePath));
}
ManualLogSource? logSource3 = _logSource;
if (logSource3 != null)
{
logSource3.LogInfo((object)"\ud83d\udca1 提示: 请参考 collected_items.yaml 中的内容进行翻译");
}
}
catch (Exception ex)
{
ManualLogSource? logSource4 = _logSource;
if (logSource4 != null)
{
logSource4.LogError((object)("❌ 生成翻译模板失败: " + ex.Message));
}
}
return Task.CompletedTask;
}
private Task CleanupCollectedItemsAsync(Dictionary<string, string>? collectedItems)
{
try
{
if (_translations.Count == 0)
{
return Task.CompletedTask;
}
if (collectedItems == null || collectedItems.Count == 0)
{
return Task.CompletedTask;
}
int count = collectedItems.Count;
Dictionary<string, string> dictionary = new Dictionary<string, string>();
foreach (KeyValuePair<string, string> collectedItem in collectedItems)
{
if (!_translations.ContainsKey(collectedItem.Key) || string.IsNullOrWhiteSpace(_translations[collectedItem.Key]))
{
dictionary[collectedItem.Key] = collectedItem.Value;
}
}
int num = count - dictionary.Count;
if (num > 0)
{
_fileManager.SaveToYaml(dictionary, _collectionFilePath);
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogDebug((object)$"\ud83e\uddf9 已清理收集文件: 移除 {num} 项已翻译内容,剩余 {dictionary.Count} 项待翻译");
}
}
else
{
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogDebug((object)"✅ 收集文件无需清理 (所有项目均未翻译)");
}
}
}
catch (Exception ex)
{
ManualLogSource? logSource3 = _logSource;
if (logSource3 != null)
{
logSource3.LogError((object)("❌ 清理收集文件失败: " + ex.Message));
}
}
return Task.CompletedTask;
}
private Task CopyUserGuideAsync(string projectDir)
{
try
{
string text = Path.Combine(projectDir, "README_翻译指南.md");
if (File.Exists(text))
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogDebug((object)"\ud83d\udcd6 用户指南文档已存在,跳过复制");
}
return Task.CompletedTask;
}
string contents = "# 智能本地化翻译指南\r\n\r\n## 快速开始\r\n1. 编辑 `translations.yaml` 文件\r\n2. 格式:`原文: 翻译`\r\n3. 保存文件并重启服务器\r\n\r\n## 文件位置\r\n- 翻译文件:`translations.yaml`\r\n- 收集文件:`collected_items.yaml`\r\n\r\n## 注意事项\r\n- 保持冒号左边的原文不变\r\n- 只修改冒号右边的翻译\r\n- 修改后需要重启服务器生效\r\n\r\n更多详细信息请查看插件日志。\r\n";
File.WriteAllText(text, contents, Encoding.UTF8);
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogInfo((object)("已创建基本用户指南: " + text));
}
}
catch (Exception ex)
{
ManualLogSource? logSource3 = _logSource;
if (logSource3 != null)
{
logSource3.LogError((object)("复制用户指南失败: " + ex.Message));
}
}
return Task.CompletedTask;
}
private Task LoadCacheAsync()
{
if (!string.IsNullOrEmpty(_cacheFilePath) && File.Exists(_cacheFilePath))
{
Dictionary<string, string> dictionary = _fileManager.LoadFromYaml(_cacheFilePath);
foreach (KeyValuePair<string, string> item in dictionary)
{
_translations[item.Key] = item.Value;
}
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)$"\ud83d\udce5 已加载 {dictionary.Count} 条缓存翻译");
}
}
return Task.CompletedTask;
}
private LocalizationStatistics GetStatistics()
{
return new LocalizationStatistics
{
TotalCollected = _totalCollected,
TotalTranslated = _totalTranslated,
TotalApplied = _totalApplied,
CacheHits = _cacheHits,
CacheMisses = _cacheMisses,
CacheHitRate = ((_cacheHits + _cacheMisses > 0) ? ((double)_cacheHits / (double)(_cacheHits + _cacheMisses) * 100.0) : 0.0),
CurrentPhase = _currentPhase,
TranslationCount = _translations.Count,
CollectedCount = _collectedItems.Count
};
}
public void LogTranslationCacheStats()
{
if (_translationQueryCount > 0)
{
double num = (double)_translationCacheHits / (double)_translationQueryCount * 100.0;
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)$"[翻译统计] 查询: {_translationQueryCount}次, 缓存命中: {_translationCacheHits}次({num:F1}%), 新收集: {_translationNewCount}次");
}
}
}
public void Dispose()
{
if (_isDisposed)
{
return;
}
_collectionEndCheckTimer?.Dispose();
if (_unsavedCount > 0)
{
ManualLogSource? logSource = _logSource;
if (logSource != null)
{
logSource.LogInfo((object)"\ud83d\udcbe 插件卸载前保存未保存的数据...");
}
SaveCollectedItemsAsync().Wait();
}
_isDisposed = true;
ManualLogSource? logSource2 = _logSource;
if (logSource2 != null)
{
logSource2.LogInfo((object)"\ud83d\udd04 智能本地化管理器已释放");
}
}
}
public enum LocalizationPhase
{
Initialization,
Collection,
Application
}
public class LocalizationStatistics
{
public int TotalCollected { get; set; }
public int TotalTranslated { get; set; }
public int TotalApplied { get; set; }
public int CacheHits { get; set; }
public int CacheMisses { get; set; }
public double CacheHitRate { get; set; }
public LocalizationPhase CurrentPhase { get; set; }
public int TranslationCount { get; set; }
public int CollectedCount { get; set; }
}
}
namespace Autolocalization.Localization
{
public class LocalizationFileManager
{
private readonly ISerializer _yamlSerializer;
private readonly IDeserializer _yamlDeserializer;
private readonly object _fileLock = new object();
public LocalizationFileManager()
{
//IL_0012: Unknown result type (might be due to invalid IL or missing references)
//IL_0021: Expected O, but got Unknown
//IL_002c: Unknown result type (might be due to invalid IL or missing references)
//IL_003b: Expected O, but got Unknown
_yamlSerializer = ((BuilderSkeleton<SerializerBuilder>)new SerializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
_yamlDeserializer = ((BuilderSkeleton<DeserializerBuilder>)new DeserializerBuilder()).WithNamingConvention(CamelCaseNamingConvention.Instance).Build();
}
public Dictionary<string, string> LoadFromXml(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
{
Logger.CreateLogSource("Autolocalization").LogWarning((object)("XML文件不存在: " + filePath));
return new Dictionary<string, string>();
}
try
{
lock (_fileLock)
{
XDocument val = XDocument.Load(filePath);
Dictionary<string, string> dictionary = new Dictionary<string, string>();
IEnumerable<XElement> enumerable = ((XContainer)val).Descendants(XName.op_Implicit("item")) ?? ((XContainer)val).Descendants(XName.op_Implicit("translation")) ?? ((XContainer)val).Descendants(XName.op_Implicit("entry"));
foreach (XElement item in enumerable)
{
XAttribute obj = item.Attribute(XName.op_Implicit("key"));
object obj2 = ((obj != null) ? obj.Value : null);
if (obj2 == null)
{
XAttribute obj3 = item.Attribute(XName.op_Implicit("id"));
obj2 = ((obj3 != null) ? obj3.Value : null);
}
string text = (string)obj2;
string value = item.Value?.Trim();
if (!string.IsNullOrEmpty(text) && !string.IsNullOrEmpty(value))
{
dictionary[text] = value;
}
}
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"从XML文件加载翻译: {filePath}, 共{dictionary.Count}条");
return dictionary;
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("加载XML文件失败: " + filePath + ", 错误: " + ex.Message));
return new Dictionary<string, string>();
}
}
public Dictionary<string, string> LoadFromCsv(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
{
Logger.CreateLogSource("Autolocalization").LogWarning((object)("CSV文件不存在: " + filePath));
return new Dictionary<string, string>();
}
try
{
lock (_fileLock)
{
string[] array = File.ReadAllLines(filePath, Encoding.UTF8);
Dictionary<string, string> dictionary = new Dictionary<string, string>();
for (int i = 0; i < array.Length; i++)
{
string text = array[i].Trim();
if (string.IsNullOrEmpty(text) || text.StartsWith("#"))
{
continue;
}
string[] array2 = ParseCsvLine(text);
if (array2.Length >= 2)
{
string text2 = array2[0].Trim();
string value = array2[1].Trim();
if (!string.IsNullOrEmpty(text2) && !string.IsNullOrEmpty(value))
{
dictionary[text2] = value;
}
}
}
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"从CSV文件加载翻译: {filePath}, 共{dictionary.Count}条");
return dictionary;
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("加载CSV文件失败: " + filePath + ", 错误: " + ex.Message));
return new Dictionary<string, string>();
}
}
public Dictionary<string, string> LoadFromYaml(string filePath)
{
if (string.IsNullOrWhiteSpace(filePath) || !File.Exists(filePath))
{
Logger.CreateLogSource("Autolocalization").LogWarning((object)("YAML文件不存在: " + filePath));
return new Dictionary<string, string>();
}
try
{
lock (_fileLock)
{
string text = File.ReadAllText(filePath, Encoding.UTF8);
Dictionary<string, string> dictionary = _yamlDeserializer.Deserialize<Dictionary<string, string>>(text);
string fileName = Path.GetFileName(filePath);
string arg = (fileName.Contains("collected") ? "收集数据" : (fileName.Contains("translation") ? "翻译数据" : "YAML数据"));
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"从YAML文件加载{arg}: {filePath}, 共{dictionary?.Count ?? 0}条");
return dictionary ?? new Dictionary<string, string>();
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("加载YAML文件失败: " + filePath + ", 错误: " + ex.Message));
return new Dictionary<string, string>();
}
}
public void SaveToXml(Dictionary<string, string> translations, string filePath)
{
//IL_0065: Unknown result type (might be due to invalid IL or missing references)
//IL_006b: Expected O, but got Unknown
//IL_008a: Unknown result type (might be due to invalid IL or missing references)
//IL_0090: Expected O, but got Unknown
//IL_00b8: Unknown result type (might be due to invalid IL or missing references)
//IL_00be: Expected O, but got Unknown
//IL_00be: Unknown result type (might be due to invalid IL or missing references)
//IL_00c4: Expected O, but got Unknown
if (translations == null || string.IsNullOrWhiteSpace(filePath))
{
return;
}
try
{
lock (_fileLock)
{
string directoryName = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
XDocument val = new XDocument(new object[1] { (object)new XElement(XName.op_Implicit("translations"), new object[3]
{
(object)new XAttribute(XName.op_Implicit("version"), (object)"1.0"),
(object)new XAttribute(XName.op_Implicit("generated"), (object)DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss")),
translations.Select<KeyValuePair<string, string>, XElement>((KeyValuePair<string, string> kvp) => new XElement(XName.op_Implicit("item"), new object[2]
{
(object)new XAttribute(XName.op_Implicit("key"), (object)kvp.Key),
(object)new XText(kvp.Value)
}))
}) });
val.Save(filePath);
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"保存翻译到XML文件: {filePath}, 共{translations.Count}条");
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("保存XML文件失败: " + filePath + ", 错误: " + ex.Message));
}
}
public void SaveToCsv(Dictionary<string, string> translations, string filePath)
{
if (translations == null || string.IsNullOrWhiteSpace(filePath))
{
return;
}
try
{
lock (_fileLock)
{
string directoryName = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
List<string> list = new List<string>
{
"# 翻译文件",
"# 格式: 原文,译文",
$"# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}",
""
};
foreach (KeyValuePair<string, string> translation in translations)
{
string text = EscapeCsvValue(translation.Key);
string text2 = EscapeCsvValue(translation.Value);
list.Add(text + "," + text2);
}
File.WriteAllLines(filePath, list, Encoding.UTF8);
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"保存翻译到CSV文件: {filePath}, 共{translations.Count}条");
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("保存CSV文件失败: " + filePath + ", 错误: " + ex.Message));
}
}
public void SaveToYaml(Dictionary<string, string> translations, string filePath)
{
if (translations == null || string.IsNullOrWhiteSpace(filePath))
{
return;
}
try
{
lock (_fileLock)
{
string directoryName = Path.GetDirectoryName(filePath);
if (!string.IsNullOrEmpty(directoryName) && !Directory.Exists(directoryName))
{
Directory.CreateDirectory(directoryName);
}
string text = _yamlSerializer.Serialize((object)translations);
string text2;
if (!Path.GetFileName(filePath).Equals("translations.yaml", StringComparison.OrdinalIgnoreCase))
{
text2 = ((!Path.GetFileName(filePath).Equals("collected_items.yaml", StringComparison.OrdinalIgnoreCase)) ? $"# 翻译文件\n# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\n# 条目数量: {translations.Count}\n\n" : $"# ============================================\r\n# 收集文件 (待翻译项目)\r\n# ============================================\r\n# \r\n# 说明:\r\n# - 此文件包含所有待翻译的英文原文\r\n# - 已翻译的项目会自动从此文件中移除\r\n# - 请参考此文件内容编辑 translations.yaml\r\n# \r\n# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n# 待翻译数量: {translations.Count}\r\n# \r\n# ============================================\r\n\r\n");
}
else
{
int num = translations.Count<KeyValuePair<string, string>>((KeyValuePair<string, string> kvp) => !string.IsNullOrWhiteSpace(kvp.Value));
int num2 = translations.Count - num;
text2 = ((translations.Count != 0) ? $"# ============================================\r\n# 智能本地化翻译文件\r\n# ============================================\r\n# \r\n# 使用说明:\r\n# 1. 这是一个简单的翻译文件,格式为 \"原文: 翻译\"\r\n# 2. 请保持冒号左边的原文不变,只修改冒号右边的翻译\r\n# 3. 如果您不想翻译某个项目,可以删除该行或保持翻译部分为空\r\n# 4. 修改后请保存文件并重启服务器以使翻译生效\r\n# \r\n# 格式示例:\r\n# Enable Shadows: 启用阴影\r\n# Master Volume: 主音量\r\n# Test Section: 测试区域\r\n# \r\n# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n# 条目数量: {translations.Count} (已翻译: {num}, 待翻译: {num2})\r\n# \r\n# ============================================\r\n\r\n" : $"# ============================================\r\n# 智能本地化翻译文件\r\n# ============================================\r\n# \r\n# 使用说明:\r\n# 1. 这是您的翻译文件,请手动添加翻译条目\r\n# 2. 格式为 \"原文: 翻译\"\r\n# 3. 请参考 collected_items.yaml 查看需要翻译的内容\r\n# 4. 只添加您已经翻译好的条目,无需添加空值\r\n# \r\n# 示例:\r\n# Enable Shadows: 启用阴影\r\n# Master Volume: 主音量\r\n# Graphics Settings: 图形设置\r\n# \r\n# 提示:\r\n# - 从 collected_items.yaml 复制需要翻译的原文\r\n# - 在冒号后面添加中文翻译\r\n# - 保存文件后重启服务器生效\r\n# \r\n# 生成时间: {DateTime.Now:yyyy-MM-dd HH:mm:ss}\r\n# \r\n# ============================================\r\n\r\n# 在下方添加您的翻译(删除此注释行)\r\n");
}
string contents = text2 + text;
File.WriteAllText(filePath, contents, Encoding.UTF8);
Logger.CreateLogSource("Autolocalization").LogInfo((object)$"保存翻译到YAML文件: {filePath}, 共{translations.Count}条");
}
}
catch (Exception ex)
{
Logger.CreateLogSource("Autolocalization").LogError((object)("保存YAML文件失败: " + filePath + ", 错误: " + ex.Message));
}
}
private string[] ParseCsvLine(string line)
{
List<string> list = new List<string>();
string text = "";
bool flag = false;
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
switch (c)
{
case '"':
flag = !flag;
continue;
case ',':
if (!flag)
{
list.Add(text);
text = "";
continue;
}
break;
}
text += c;
}
list.Add(text);
return list.ToArray();
}
private string EscapeCsvValue(string value)
{
if (string.IsNullOrEmpty(value))
{
return "";
}
if (value.Contains(",") || value.Contains("\"") || value.Contains("\n") || value.Contains("\r"))
{
return "\"" + value.Replace("\"", "\"\"") + "\"";
}
return value;
}
}
}