mirror of
https://github.com/MSWS/TTT.git
synced 2025-12-05 22:20:25 -08:00
127 lines
4.3 KiB
C#
127 lines
4.3 KiB
C#
using System.Reflection;
|
|
using System.Text.RegularExpressions;
|
|
using CounterStrikeSharp.API;
|
|
using CounterStrikeSharp.API.Modules.Utils;
|
|
using Microsoft.Extensions.Localization;
|
|
|
|
namespace TTT.Locale;
|
|
|
|
/// <summary>
|
|
/// A custom implementation of <see cref="IStringLocalizer" /> that adds support
|
|
/// for in-string placeholders like %key% and grammatical pluralization with %s%.
|
|
/// </summary>
|
|
public partial class StringLocalizer : IMsgLocalizer {
|
|
// public static readonly StringLocalizer Instance =
|
|
// new(new JsonLocalizerFactory());
|
|
|
|
private readonly IStringLocalizer localizer;
|
|
|
|
public StringLocalizer(IStringLocalizerFactory factory) {
|
|
var type = typeof(StringLocalizer);
|
|
var assemblyName =
|
|
new AssemblyName(type.GetTypeInfo().Assembly.FullName ?? string.Empty);
|
|
localizer = factory.Create(string.Empty, assemblyName.FullName);
|
|
}
|
|
|
|
public string this[IMsg msg] => getString(msg.Key, msg.Args);
|
|
|
|
public LocalizedString this[string name] => getString(name);
|
|
|
|
public LocalizedString this[string name, params object[] arguments]
|
|
=> getString(name, arguments);
|
|
|
|
public IEnumerable<LocalizedString>
|
|
GetAllStrings(bool includeParentCultures) {
|
|
return localizer.GetAllStrings(includeParentCultures)
|
|
.Select(str => getString(str.Name));
|
|
}
|
|
|
|
[GeneratedRegex("%.*?%")]
|
|
private static partial Regex percentRegex();
|
|
|
|
[GeneratedRegex(@"\b(\w+)%s%")]
|
|
private static partial Regex pluralRegex();
|
|
|
|
private LocalizedString getString(string name, params object[] arguments) {
|
|
// Get the localized value
|
|
var value = localizer[name].Value;
|
|
|
|
// Replace placeholders like %key% with their respective values
|
|
var matches = percentRegex().Matches(value);
|
|
foreach (Match match in matches) {
|
|
var key = match.Value;
|
|
var trimmedKey = key[1..^1]; // Trim % symbols
|
|
|
|
// NullReferenceException catch block if key does not exist
|
|
try {
|
|
// CS# forces a space before a chat color if the entirety
|
|
// of the strong is a color code. This is undesired
|
|
// in our case, so we trim the value when we have a prefix.
|
|
var replacement = getString(trimmedKey).Value;
|
|
value = value.Replace(key,
|
|
trimmedKey.Contains("PREFIX", StringComparison.OrdinalIgnoreCase) ?
|
|
replacement :
|
|
replacement.Trim());
|
|
} catch (NullReferenceException) {
|
|
// Key doesn't exist, move on
|
|
}
|
|
}
|
|
|
|
// Format with arguments if provided
|
|
if (arguments.Length > 0) value = string.Format(value, arguments);
|
|
|
|
// Handle pluralization
|
|
value = HandlePluralization(value);
|
|
|
|
return new LocalizedString(name, value);
|
|
}
|
|
|
|
public static string HandlePluralization(string value) {
|
|
var pluralMatches = pluralRegex().Matches(value);
|
|
foreach (Match match in pluralMatches) {
|
|
var word = match.Groups[1].Value.ToLower();
|
|
var index = match.Index;
|
|
var prefix = value[..index].Trim();
|
|
|
|
var lastWords = prefix.Split(' ')
|
|
.Select(w
|
|
=> w.Where(c => char.IsLetterOrDigit(c) || c == '-').ToArray());
|
|
|
|
var previousNumber = lastWords.LastOrDefault(w => int.TryParse(w, out _));
|
|
|
|
if (previousNumber != null)
|
|
value = value[..index] + value[index..]
|
|
.Replace("%s%", int.Parse(previousNumber) == 1 ? "" : "s");
|
|
else
|
|
value = value[..index] + value[index..]
|
|
.Replace("%s%", word.EndsWith('s') ? "" : "s");
|
|
}
|
|
|
|
value = value.Replace("%s%", "s");
|
|
|
|
var trailingIndex = -1;
|
|
|
|
// We have to do this chicanery due to supporting colors in the string
|
|
|
|
while ((trailingIndex =
|
|
value.IndexOf("'s", trailingIndex + 1, StringComparison.Ordinal)) != -1) {
|
|
var startingWordBoundary = value[..trailingIndex].LastIndexOf(' ');
|
|
if (startingWordBoundary == -1
|
|
|| startingWordBoundary + 2 > value.Length) {
|
|
if (value.EndsWith("s's")) value = value[..^1];
|
|
break;
|
|
}
|
|
|
|
var endingWordBoundary = value.IndexOf(' ', trailingIndex + 2);
|
|
var word = value[(startingWordBoundary + 1)..endingWordBoundary];
|
|
var filteredWord = word.Where(c => char.IsLetterOrDigit(c) || c == '\'')
|
|
.ToArray();
|
|
if (new string(filteredWord).EndsWith("s's",
|
|
StringComparison.OrdinalIgnoreCase))
|
|
value = value[..(trailingIndex + 1)] + " "
|
|
+ value[(trailingIndex + 4)..];
|
|
}
|
|
|
|
return value;
|
|
}
|
|
} |