-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathModLocalization.cs
More file actions
156 lines (130 loc) · 4.92 KB
/
ModLocalization.cs
File metadata and controls
156 lines (130 loc) · 4.92 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
using System.Reflection;
using System.Text.Json;
using Godot;
using MegaCrit.Sts2.Core.Localization;
using FileAccess = Godot.FileAccess;
namespace damageStat;
public static class ModLocalization
{
private const string DefaultLanguage = "en";
private static readonly object SyncRoot = new();
private static Dictionary<string, string> _translations = new(StringComparer.OrdinalIgnoreCase);
private static string? _loadedLanguage;
public static string Get(string key, string fallback = "")
{
EnsureLoaded();
return _translations.GetValueOrDefault(key) ?? fallback;
}
private static void EnsureLoaded()
{
string language = ResolveLanguage();
lock (SyncRoot)
{
if (string.Equals(_loadedLanguage, language, StringComparison.OrdinalIgnoreCase))
return;
_translations = LoadTranslations(language);
_loadedLanguage = language;
}
}
private static Dictionary<string, string> LoadTranslations(string language)
{
foreach (string candidate in GetLanguageCandidates(language))
{
Dictionary<string, string>? translations = TryLoadEmbedded(candidate);
if (translations is { Count: > 0 })
{
MainFile.Logger.Info($"Loaded embedded localization for '{candidate}'.");
return translations;
}
string path = $"res://{MainFile.ModId}/localization/{candidate}.json";
translations = TryLoadFromGodotPath(path);
if (translations is { Count: > 0 })
{
MainFile.Logger.Info($"Loaded localization file: {path}");
return translations;
}
}
MainFile.Logger.Info($"No localization file found for '{language}', falling back to defaults.");
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
}
private static Dictionary<string, string>? TryLoadEmbedded(string language)
{
string resourceName = $"{MainFile.ModId}.localization.{language}.json";
using Stream? stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
if (stream == null)
return null;
try
{
return JsonSerializer.Deserialize<Dictionary<string, string>>(stream);
}
catch (JsonException ex)
{
MainFile.Logger.Error($"Failed to parse embedded localization '{resourceName}': {ex.Message}");
return null;
}
}
private static Dictionary<string, string>? TryLoadFromGodotPath(string path)
{
if (!FileAccess.FileExists(path))
return null;
using FileAccess? file = FileAccess.Open(path, FileAccess.ModeFlags.Read);
if (file == null)
{
MainFile.Logger.Error($"Failed to open localization file: {path}");
return null;
}
try
{
return JsonSerializer.Deserialize<Dictionary<string, string>>(file.GetAsText());
}
catch (JsonException ex)
{
MainFile.Logger.Error($"Failed to parse localization file '{path}': {ex.Message}");
return null;
}
}
private static string ResolveLanguage()
{
string? language = LocManager.Instance?.Language;
if (string.IsNullOrWhiteSpace(language))
language = TranslationServer.GetLocale();
return NormalizeLanguageCode(language);
}
private static IEnumerable<string> GetLanguageCandidates(string language)
{
HashSet<string> candidates = new(StringComparer.OrdinalIgnoreCase);
foreach (string candidate in ExpandLanguageCandidates(language))
{
if (candidates.Add(candidate))
yield return candidate;
}
if (candidates.Add(DefaultLanguage))
yield return DefaultLanguage;
}
private static IEnumerable<string> ExpandLanguageCandidates(string? language)
{
if (string.IsNullOrWhiteSpace(language))
yield break;
string normalized = NormalizeLanguageCode(language);
yield return normalized;
int separatorIndex = normalized.IndexOf('_');
if (separatorIndex > 0)
yield return normalized[..separatorIndex];
if (normalized.StartsWith("zh", StringComparison.OrdinalIgnoreCase))
yield return "zhs";
if (normalized.StartsWith("en", StringComparison.OrdinalIgnoreCase))
yield return "en";
}
private static string NormalizeLanguageCode(string? language)
{
if (string.IsNullOrWhiteSpace(language))
return DefaultLanguage;
string normalized = language.Trim().Replace('-', '_').ToLowerInvariant();
return normalized switch
{
"zh_cn" or "zh_hans" or "zh_sg" or "zh" => "zhs",
"en_us" or "en_gb" => "en",
_ => normalized,
};
}
}