feat(i18n): add LocaleStore folder discovery
This commit is contained in:
31
src/ClaudeDo.Localization/LocaleStore.cs
Normal file
31
src/ClaudeDo.Localization/LocaleStore.cs
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
namespace ClaudeDo.Localization;
|
||||||
|
|
||||||
|
public sealed class LocaleStore
|
||||||
|
{
|
||||||
|
private readonly Dictionary<string, LocaleFile> _byCode;
|
||||||
|
|
||||||
|
private LocaleStore(Dictionary<string, LocaleFile> byCode) => _byCode = byCode;
|
||||||
|
|
||||||
|
public IReadOnlyList<LocaleFile> Available => _byCode.Values.ToList();
|
||||||
|
|
||||||
|
public bool TryGet(string code, out LocaleFile? file) => _byCode.TryGetValue(code, out file);
|
||||||
|
|
||||||
|
public static LocaleStore Load(string folder)
|
||||||
|
{
|
||||||
|
var byCode = new Dictionary<string, LocaleFile>(StringComparer.OrdinalIgnoreCase);
|
||||||
|
if (Directory.Exists(folder))
|
||||||
|
{
|
||||||
|
foreach (var path in Directory.EnumerateFiles(folder, "*.json"))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var file = LocaleJson.Parse(File.ReadAllText(path));
|
||||||
|
if (!string.IsNullOrWhiteSpace(file.Code))
|
||||||
|
byCode[file.Code] = file;
|
||||||
|
}
|
||||||
|
catch { /* skip malformed locale files */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new LocaleStore(byCode);
|
||||||
|
}
|
||||||
|
}
|
||||||
38
tests/ClaudeDo.Localization.Tests/LocaleStoreTests.cs
Normal file
38
tests/ClaudeDo.Localization.Tests/LocaleStoreTests.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using ClaudeDo.Localization;
|
||||||
|
|
||||||
|
namespace ClaudeDo.Localization.Tests;
|
||||||
|
|
||||||
|
public class LocaleStoreTests
|
||||||
|
{
|
||||||
|
private static string WriteTempLocales(params (string file, string json)[] files)
|
||||||
|
{
|
||||||
|
var dir = Path.Combine(Path.GetTempPath(), "loc_" + Guid.NewGuid().ToString("N"));
|
||||||
|
Directory.CreateDirectory(dir);
|
||||||
|
foreach (var (file, json) in files)
|
||||||
|
File.WriteAllText(Path.Combine(dir, file), json);
|
||||||
|
return dir;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Load_discovers_all_json_files()
|
||||||
|
{
|
||||||
|
var dir = WriteTempLocales(
|
||||||
|
("en.json", """{ "metadata": { "code": "en", "name": "English" }, "a": { "b": "x" } }"""),
|
||||||
|
("de.json", """{ "metadata": { "code": "de", "name": "Deutsch" }, "a": { "b": "y" } }"""));
|
||||||
|
|
||||||
|
var store = LocaleStore.Load(dir);
|
||||||
|
|
||||||
|
Assert.Equal(2, store.Available.Count);
|
||||||
|
Assert.True(store.TryGet("en", out var en));
|
||||||
|
Assert.Equal("x", en!.Strings["a.b"]);
|
||||||
|
Assert.True(store.TryGet("de", out var de));
|
||||||
|
Assert.Equal("Deutsch", de!.Name);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Load_returns_empty_when_folder_missing()
|
||||||
|
{
|
||||||
|
var store = LocaleStore.Load(Path.Combine(Path.GetTempPath(), "does_not_exist_" + Guid.NewGuid()));
|
||||||
|
Assert.Empty(store.Available);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user