server-master/srcs/TranslationsServer/Loader/GenericTranslationFileLoader.cs

229 lines
No EOL
9.3 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Npgsql;
using PhoenixLib.Logging;
using PhoenixLib.MultiLanguage;
using WingsAPI.Data.GameData;
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.NamingConventions;
namespace TranslationServer.Loader
{
public class GenericTranslationFileLoader : IResourceLoader<GenericTranslationDto>
{
private static readonly INamingConvention __NamingConvention = UnderscoredNamingConvention.Instance;
private static readonly IDeserializer __Deserializer = new DeserializerBuilder().WithNamingConvention(__NamingConvention).Build();
private readonly TranslationsFileLoaderOptions _options;
private readonly List<GenericTranslationDto> _translations = new();
public GenericTranslationFileLoader(TranslationsFileLoaderOptions options) => _options = options;
public async Task<IReadOnlyList<GenericTranslationDto>> LoadAsync()
{
if (_translations.Any())
{
return _translations;
}
Dictionary<string, string> english = null;
foreach (RegionLanguageType i in Enum.GetValues(typeof(RegionLanguageType)))
{
if (i == RegionLanguageType.RU)
{
continue;
}
string languageType = i.ToString().ToLowerInvariant();
var newTmp = new Dictionary<string, string>();
string languageDirectory = Path.Combine(_options.TranslationsPath, $"{languageType}");
IEnumerable<string> translationFiles = Enumerable.Empty<string>();
if (Directory.Exists(languageDirectory))
{
translationFiles = Directory.GetFiles(languageDirectory, "*.yml").Concat(Directory.GetFiles(languageDirectory, "*.yaml"));
}
foreach (string translationFile in translationFiles)
{
try
{
string fileContent = await File.ReadAllTextAsync(translationFile);
IDeserializer deserializer = __Deserializer;
Dictionary<string, string> tmp = deserializer.Deserialize<Dictionary<string, string>>(fileContent);
foreach ((string s, string value) in tmp)
{
if (string.IsNullOrEmpty(value))
{
continue;
}
if (value == $"#{s}" && english != null && english.TryGetValue(s, out string translated))
{
newTmp[s] = translated;
continue;
}
newTmp[s] = value;
}
}
catch (Exception e)
{
Log.Error($"[RESOURCE_LOADER] {translationFile} {languageType}", e);
}
}
if (!newTmp.Any())
{
foreach ((string key, string value) in LoadFromResourceLangFiles(i))
{
newTmp[key] = value;
}
if (!newTmp.Any())
{
foreach ((string key, string value) in LoadFromDatabaseLangFiles(i))
{
newTmp[key] = value;
}
}
}
if (i == RegionLanguageType.EN)
{
english = newTmp;
}
_translations.AddRange(newTmp.Select(s => new GenericTranslationDto
{
Key = s.Key,
Value = s.Value,
Language = i
}));
}
Log.Info($"[RESOURCE_LOADER] {_translations.Count.ToString()} translations loaded");
return _translations;
}
private IEnumerable<(string key, string value)> LoadFromResourceLangFiles(RegionLanguageType lang)
{
var entries = new List<(string key, string value)>();
string marker = lang switch
{
RegionLanguageType.EN => "_uk_",
RegionLanguageType.DE => "_de_",
RegionLanguageType.FR => "_fr_",
RegionLanguageType.IT => "_it_",
RegionLanguageType.ES => "_es_",
RegionLanguageType.CZ => "_cz_",
RegionLanguageType.PL => "_pl_",
RegionLanguageType.TR => "_tr_",
_ => null
};
if (marker == null)
{
return entries;
}
try
{
const string resourceLangPath = "/app/resources/lang";
if (!Directory.Exists(resourceLangPath))
{
return entries;
}
foreach (string file in Directory.GetFiles(resourceLangPath, $"*{marker}*.txt", SearchOption.TopDirectoryOnly))
{
string filePrefix = Path.GetFileNameWithoutExtension(file)?.Replace('.', '_') ?? "lang";
foreach (string line in File.ReadAllLines(file, Encoding.Latin1))
{
string[] parts = line.Split('\t');
if (parts.Length < 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))
{
continue;
}
entries.Add(($"{filePrefix}.{parts[0]}", parts[1]));
}
}
}
catch (Exception ex)
{
Log.Error($"[RESOURCE_LOADER] Resource language fallback failed for {lang}", ex);
}
return entries;
}
private IEnumerable<(string key, string value)> LoadFromDatabaseLangFiles(RegionLanguageType lang)
{
var entries = new List<(string key, string value)>();
try
{
string marker = lang switch
{
RegionLanguageType.EN => "_uk_",
RegionLanguageType.DE => "_de_",
RegionLanguageType.FR => "_fr_",
RegionLanguageType.IT => "_it_",
RegionLanguageType.ES => "_es_",
RegionLanguageType.CZ => "_cz_",
RegionLanguageType.PL => "_pl_",
RegionLanguageType.TR => "_tr_",
_ => null
};
if (marker == null)
{
return entries;
}
string host = Environment.GetEnvironmentVariable("DATABASE_IP") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_IP") ?? "127.0.0.1";
string port = Environment.GetEnvironmentVariable("DATABASE_PORT") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PORT") ?? "5432";
string db = Environment.GetEnvironmentVariable("DATABASE_NAME") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_NAME") ?? "game";
string user = Environment.GetEnvironmentVariable("DATABASE_USER") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_USER") ?? "postgres";
string pass = Environment.GetEnvironmentVariable("DATABASE_PASSWORD") ?? Environment.GetEnvironmentVariable("POSTGRES_DATABASE_PASSWORD") ?? "postgres";
using var conn = new NpgsqlConnection($"Host={host};Port={port};Database={db};Username={user};Password={pass}");
conn.Open();
using var cmd = new NpgsqlCommand("SELECT relative_path, content FROM resources.resource_files WHERE category='lang' AND lower(relative_path) LIKE lower(@needle);", conn);
cmd.Parameters.AddWithValue("needle", $"%{marker}%");
using var reader = cmd.ExecuteReader();
while (reader.Read())
{
string path = reader.GetString(0);
string filePrefix = Path.GetFileNameWithoutExtension(path)?.Replace('.', '_') ?? "lang";
byte[] content = (byte[])reader[1];
string text = Encoding.Latin1.GetString(content);
foreach (string line in text.Split(new[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries))
{
string[] parts = line.Split('\t');
if (parts.Length < 2 || string.IsNullOrWhiteSpace(parts[0]) || string.IsNullOrWhiteSpace(parts[1]))
{
continue;
}
string key = $"{filePrefix}.{parts[0]}";
entries.Add((key, parts[1]));
}
}
}
catch (Exception ex)
{
Log.Error($"[RESOURCE_LOADER] DB language fallback failed for {lang}", ex);
}
return entries;
}
}
}