feat: Set up initial WebClientTpos .NET project structure, including client, server, assets, and documentation.

This commit is contained in:
Ho Ngoc Hai
2026-02-12 00:41:43 +07:00
parent b661bb7d8b
commit 689f4fa96f
51 changed files with 4860 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
using System.Globalization;
using System.Net.Http.Json;
using Microsoft.Extensions.Localization;
namespace WebClientTpos.Client.Localization;
public class JsonStringLocalizer : IStringLocalizer
{
private readonly LocalizationCache _cache;
private readonly string _resourceName;
public JsonStringLocalizer(LocalizationCache cache, string resourceName)
{
_cache = cache;
_resourceName = resourceName;
}
// This constructor style is used by the Factory (if we update factory)
public JsonStringLocalizer(LocalizationCache cache)
{
_cache = cache;
_resourceName = "Shared";
}
public LocalizedString this[string name]
{
get
{
var value = GetString(name);
return new LocalizedString(name, value ?? name, resourceNotFound: value == null);
}
}
public LocalizedString this[string name, params object[] arguments]
{
get
{
var format = GetString(name);
var value = string.Format(format ?? name, arguments);
return new LocalizedString(name, value, resourceNotFound: format == null);
}
}
public IEnumerable<LocalizedString> GetAllStrings(bool includeParentCultures)
{
// Not fully supported by simple cache
return Enumerable.Empty<LocalizedString>();
}
private string? GetString(string name)
{
return _cache.GetString(name);
}
}

View File

@@ -0,0 +1,26 @@
using Microsoft.Extensions.Localization;
namespace WebClientTpos.Client.Localization;
public class JsonStringLocalizerFactory : IStringLocalizerFactory
{
private readonly LocalizationCache _cache;
private readonly IServiceProvider _serviceProvider;
public JsonStringLocalizerFactory(LocalizationCache cache, IServiceProvider serviceProvider)
{
_cache = cache;
_serviceProvider = serviceProvider;
}
public IStringLocalizer Create(Type resourceSource)
{
return new JsonStringLocalizer(_cache, resourceSource.Name);
}
public IStringLocalizer Create(string baseName, string location)
{
return new JsonStringLocalizer(_cache, baseName);
}
}

View File

@@ -0,0 +1,50 @@
using System.Globalization;
using System.Net.Http.Json;
namespace WebClientTpos.Client.Localization;
public class LocalizationCache
{
private readonly HttpClient _httpClient;
private Dictionary<string, string> _strings = new();
private bool _isLoaded;
public LocalizationCache(HttpClient httpClient)
{
_httpClient = httpClient;
}
public string? GetString(string key)
{
if (_strings.TryGetValue(key, out var value))
{
return value;
}
return null;
}
public async Task LoadAsync(CultureInfo culture)
{
if (_isLoaded) return; // Or check if culture changed
try
{
var cultureName = culture.Name;
// Map generic "vi" to "vi-VN" if needed, but for now we trust the culture name matches file
// Fallback for simple "vi" -> "vi-VN"
if (cultureName == "vi") cultureName = "vi-VN";
if (cultureName == "en") cultureName = "en-US";
var loaded = await _httpClient.GetFromJsonAsync<Dictionary<string, string>>($"/locales/{cultureName}.json?v={DateTime.Now.Ticks}");
if (loaded != null)
{
_strings = loaded;
_isLoaded = true;
}
}
catch (Exception ex)
{
Console.WriteLine($"Error loading localization for {culture.Name}: {ex.Message}");
}
}
}