Rewrite huge parts of the localization class to support plurals and localized date time formating, also created a TranslationGuildContext class for use in commands

This commit is contained in:
runebaas 2019-05-12 00:17:34 +02:00
parent 4143180b42
commit 8effc42f92
No known key found for this signature in database
GPG key ID: 2677AF508D0300D6
5 changed files with 211 additions and 79 deletions

View file

@ -36,17 +36,18 @@ namespace Geekbot.net.Commands.Rpg
{ {
try try
{ {
var transDict = await _translation.GetDict(Context); var transContext = await _translation.GetGuildContext(Context);
var actor = await GetUser(Context.User.Id); var actor = await GetUser(Context.User.Id);
if (actor.LastPayout.Value.AddHours(24) > DateTimeOffset.Now) if (actor.LastPayout.Value.AddHours(24) > DateTimeOffset.Now)
{ {
await ReplyAsync(string.Format(transDict["WaitForMoreCookies"], actor.LastPayout.Value.AddHours(24).ToString("HH:mm:ss"))); var formatedWaitTime = transContext.FormatDateTimeAsRemaining(actor.LastPayout.Value.AddHours(24));
await ReplyAsync(transContext.GetString("WaitForMoreCookies", formatedWaitTime));
return; return;
} }
actor.Cookies += 10; actor.Cookies += 10;
actor.LastPayout = DateTimeOffset.Now; actor.LastPayout = DateTimeOffset.Now;
await SetUser(actor); await SetUser(actor);
await ReplyAsync(string.Format(transDict["GetCookies"], 10, actor.Cookies)); await ReplyAsync(transContext.GetString("GetCookies", 10, actor.Cookies));
} }
catch (Exception e) catch (Exception e)
@ -61,9 +62,9 @@ namespace Geekbot.net.Commands.Rpg
{ {
try try
{ {
var transDict = await _translation.GetDict(Context); var transContext = await _translation.GetGuildContext(Context);
var actor = await GetUser(Context.User.Id); var actor = await GetUser(Context.User.Id);
await ReplyAsync(string.Format(transDict["InYourJar"], actor.Cookies)); await ReplyAsync(transContext.GetString("InYourJar", actor.Cookies));
} }
catch (Exception e) catch (Exception e)
{ {
@ -77,12 +78,12 @@ namespace Geekbot.net.Commands.Rpg
{ {
try try
{ {
var transDict = await _translation.GetDict(Context); var transContext = await _translation.GetGuildContext(Context);
var giver = await GetUser(Context.User.Id); var giver = await GetUser(Context.User.Id);
if (giver.Cookies < amount) if (giver.Cookies < amount)
{ {
await ReplyAsync(string.Format(transDict["NotEnoughToGive"])); await ReplyAsync(transContext.GetString("NotEnoughToGive"));
return; return;
} }
@ -94,7 +95,7 @@ namespace Geekbot.net.Commands.Rpg
await SetUser(giver); await SetUser(giver);
await SetUser(taker); await SetUser(taker);
await ReplyAsync(string.Format(transDict["Given"], amount, user.Username)); await ReplyAsync(transContext.GetString("Given", amount, user.Username));
} }
catch (Exception e) catch (Exception e)
{ {
@ -108,12 +109,12 @@ namespace Geekbot.net.Commands.Rpg
{ {
try try
{ {
var transDict = await _translation.GetDict(Context); var transContext = await _translation.GetGuildContext(Context);
var actor = await GetUser(Context.User.Id); var actor = await GetUser(Context.User.Id);
if (actor.Cookies < 5) if (actor.Cookies < 5)
{ {
await ReplyAsync(string.Format(transDict["NotEnoughCookiesToEat"])); await ReplyAsync(transContext.GetString("NotEnoughCookiesToEat"));
return; return;
} }
@ -122,7 +123,7 @@ namespace Geekbot.net.Commands.Rpg
await SetUser(actor); await SetUser(actor);
await ReplyAsync(string.Format(transDict["AteCookies"], amount, actor.Cookies)); await ReplyAsync(transContext.GetString("AteCookies", amount, actor.Cookies));
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -7,8 +7,10 @@ namespace Geekbot.net.Lib.Localization
public interface ITranslationHandler public interface ITranslationHandler
{ {
Task<string> GetString(ulong guildId, string command, string stringName); Task<string> GetString(ulong guildId, string command, string stringName);
List<string> GetStrings(string language, string command, string stringName);
Task<Dictionary<string, string>> GetDict(ICommandContext context); Task<Dictionary<string, string>> GetDict(ICommandContext context);
Task<Dictionary<string, string>> GetDict(ICommandContext context, string command); Task<Dictionary<string, string>> GetDict(ICommandContext context, string command);
Task<TranslationGuildContext> GetGuildContext(ICommandContext context);
Task<bool> SetLanguage(ulong guildId, string language); Task<bool> SetLanguage(ulong guildId, string language);
List<string> SupportedLanguages { get; } List<string> SupportedLanguages { get; }
} }

View file

@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Geekbot.net.Lib.Localization
{
public class TranslationGuildContext
{
public ITranslationHandler TranslationHandler { get; }
public string Language { get; }
public Dictionary<string, string> Dict { get; }
public TranslationGuildContext(ITranslationHandler translationHandler, string language, Dictionary<string, string> dict)
{
TranslationHandler = translationHandler;
Language = language;
Dict = dict;
}
public string GetString(string stringToFormat, params object[] args)
{
return string.Format(Dict[stringToFormat] ?? "", args);
}
public string FormatDateTimeAsRemaining(DateTimeOffset dateTime)
{
var remaining = dateTime - DateTimeOffset.Now;
const string formattable = "{0} {1}";
var sb = new StringBuilder();
if (remaining.Days > 0)
{
var s = GetTimeString(TimeTypes.Days);
sb.AppendFormat(formattable, remaining.Days, GetSingOrPlur(remaining.Days, s));
}
if (remaining.Hours > 0)
{
if (sb.Length > 0) sb.Append(", ");
var s = GetTimeString(TimeTypes.Hours);
sb.AppendFormat(formattable, remaining.Hours, GetSingOrPlur(remaining.Hours, s));
}
if (remaining.Minutes > 0)
{
if (sb.Length > 0) sb.Append(", ");
var s = GetTimeString(TimeTypes.Minutes);
sb.AppendFormat(formattable, remaining.Minutes, GetSingOrPlur(remaining.Minutes, s));
}
if (remaining.Seconds > 0)
{
if (sb.Length > 0)
{
var and = TranslationHandler.GetStrings(Language, "dateTime", "And").First();
sb.AppendFormat(" {0} ", and);
}
var s = GetTimeString(TimeTypes.Seconds);
sb.AppendFormat(formattable, remaining.Seconds, GetSingOrPlur(remaining.Seconds, s));
}
return sb.ToString().Trim();
}
public Task<bool> SetLanguage(ulong guildId, string language)
{
return TranslationHandler.SetLanguage(guildId, language);
}
private List<string> GetTimeString(TimeTypes type)
{
return TranslationHandler.GetStrings(Language, "dateTime", type.ToString());
}
private string GetSingOrPlur(int number, List<string> versions)
{
return number == 1 ? versions[0] : versions[1];
}
private enum TimeTypes
{
Days,
Hours,
Minutes,
Seconds
}
}
}

View file

@ -17,7 +17,7 @@ namespace Geekbot.net.Lib.Localization
private readonly DatabaseContext _database; private readonly DatabaseContext _database;
private readonly IGeekbotLogger _logger; private readonly IGeekbotLogger _logger;
private readonly Dictionary<ulong, string> _serverLanguages; private readonly Dictionary<ulong, string> _serverLanguages;
private Dictionary<string, Dictionary<string, Dictionary<string, string>>> _translations; private Dictionary<string, Dictionary<string, Dictionary<string, List<string>>>> _translations;
public TranslationHandler(DatabaseContext database, IGeekbotLogger logger) public TranslationHandler(DatabaseContext database, IGeekbotLogger logger)
{ {
@ -33,8 +33,8 @@ namespace Geekbot.net.Lib.Localization
try try
{ {
var translationFile = File.ReadAllText(Path.GetFullPath("./Lib/Localization/Translations.json")); var translationFile = File.ReadAllText(Path.GetFullPath("./Lib/Localization/Translations.json"));
var rawTranslations = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(translationFile); var rawTranslations = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, List<string>>>>>(translationFile);
var sortedPerLanguage = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>(); var sortedPerLanguage = new Dictionary<string, Dictionary<string, Dictionary<string, List<string>>>>();
foreach (var command in rawTranslations) foreach (var command in rawTranslations)
{ {
foreach (var str in command.Value) foreach (var str in command.Value)
@ -43,8 +43,8 @@ namespace Geekbot.net.Lib.Localization
{ {
if (!sortedPerLanguage.ContainsKey(lang.Key)) if (!sortedPerLanguage.ContainsKey(lang.Key))
{ {
var commandDict = new Dictionary<string, Dictionary<string, string>>(); var commandDict = new Dictionary<string, Dictionary<string, List<string>>>();
var strDict = new Dictionary<string, string> var strDict = new Dictionary<string, List<string>>
{ {
{str.Key, lang.Value} {str.Key, lang.Value}
}; };
@ -53,8 +53,10 @@ namespace Geekbot.net.Lib.Localization
} }
if (!sortedPerLanguage[lang.Key].ContainsKey(command.Key)) if (!sortedPerLanguage[lang.Key].ContainsKey(command.Key))
{ {
var strDict = new Dictionary<string, string>(); var strDict = new Dictionary<string, List<string>>
strDict.Add(str.Key, lang.Value); {
{str.Key, lang.Value}
};
sortedPerLanguage[lang.Key].Add(command.Key, strDict); sortedPerLanguage[lang.Key].Add(command.Key, strDict);
} }
if (!sortedPerLanguage[lang.Key][command.Key].ContainsKey(str.Key)) if (!sortedPerLanguage[lang.Key][command.Key].ContainsKey(str.Key))
@ -109,10 +111,16 @@ namespace Geekbot.net.Lib.Localization
public async Task<string> GetString(ulong guildId, string command, string stringName) public async Task<string> GetString(ulong guildId, string command, string stringName)
{ {
var translation = _translations[await GetServerLanguage(guildId)][command][stringName]; var serverLang = await GetServerLanguage(guildId);
if (!string.IsNullOrWhiteSpace(translation)) return translation; return GetStrings(serverLang, command, stringName).First();
}
public List<string> GetStrings(string language, string command, string stringName)
{
var translation = _translations[language][command][stringName];
if (!string.IsNullOrWhiteSpace(translation.First())) return translation;
translation = _translations[command][stringName]["EN"]; translation = _translations[command][stringName]["EN"];
if (string.IsNullOrWhiteSpace(translation)) if (string.IsNullOrWhiteSpace(translation.First()))
{ {
_logger.Warning(LogSource.Geekbot, $"No translation found for {command} - {stringName}"); _logger.Warning(LogSource.Geekbot, $"No translation found for {command} - {stringName}");
} }
@ -125,7 +133,8 @@ namespace Geekbot.net.Lib.Localization
{ {
var command = context.Message.Content.Split(' ').First().TrimStart('!').ToLower(); var command = context.Message.Content.Split(' ').First().TrimStart('!').ToLower();
var serverLanguage = await GetServerLanguage(context.Guild?.Id ?? 0); var serverLanguage = await GetServerLanguage(context.Guild?.Id ?? 0);
return _translations[serverLanguage][command]; return _translations[serverLanguage][command]
.ToDictionary(dict => dict.Key, dict => dict.Value.First());
} }
catch (Exception e) catch (Exception e)
{ {
@ -133,13 +142,21 @@ namespace Geekbot.net.Lib.Localization
return new Dictionary<string, string>(); return new Dictionary<string, string>();
} }
} }
public async Task<TranslationGuildContext> GetGuildContext(ICommandContext context)
{
var dict = await GetDict(context);
var language = await GetServerLanguage(context.Guild?.Id ?? 0);
return new TranslationGuildContext(this, language, dict);
}
public async Task<Dictionary<string, string>> GetDict(ICommandContext context, string command) public async Task<Dictionary<string, string>> GetDict(ICommandContext context, string command)
{ {
try try
{ {
var serverLanguage = await GetServerLanguage(context.Guild?.Id ?? 0); var serverLanguage = await GetServerLanguage(context.Guild?.Id ?? 0);
return _translations[serverLanguage][command]; return _translations[serverLanguage][command]
.ToDictionary(dict => dict.Key, dict => dict.Value.First());
} }
catch (Exception e) catch (Exception e)
{ {

View file

@ -1,130 +1,152 @@
{ {
"dateTime": {
"Days": {
"EN": ["day", "days"],
"CHDE": ["tag", "täg"]
},
"Hours": {
"EN": ["hour", "hours"],
"CHDE": ["stund", "stunde"]
},
"Minutes": {
"EN": ["minute", "minutes"],
"CHDE": ["minute", "minute"]
},
"Seconds": {
"EN": ["second", "seconds"],
"CHDE": ["sekunde", "sekunde"]
},
"And": {
"EN": ["and"],
"CHDE": ["und"]
}
},
"admin": { "admin": {
"NewLanguageSet": { "NewLanguageSet": {
"EN": "I will reply in english from now on", "EN": ["I will reply in english from now on"],
"CHDE": "I werd ab jetzt uf schwiizerdüütsch antworte, äuuä" "CHDE": ["I werd ab jetzt uf schwiizerdüütsch antworte, äuuä"]
}, },
"GetLanguage": { "GetLanguage": {
"EN": "I'm talking english", "EN": ["I'm talking english"],
"CHDE": "I red schwiizerdüütsch" "CHDE": ["I red schwiizerdüütsch"]
} }
}, },
"errorHandler": { "errorHandler": {
"SomethingWentWrong": { "SomethingWentWrong": {
"EN": "Something went wrong :confused:", "EN": ["Something went wrong :confused:"],
"CHDE": "Öppis isch schief gange :confused:" "CHDE": ["Öppis isch schief gange :confused:"]
} }
}, },
"httpErrors": { "httpErrors": {
"403": { "403": {
"EN": "Seems like i don't have enough permission to that :confused:", "EN": ["Seems like i don't have enough permission to that :confused:"],
"CHDE": "Gseht danach us das ich nid gnueg recht han zum das mache :confused:" "CHDE": ["Gseht danach us das ich nid gnueg recht han zum das mache :confused:"]
} }
}, },
"choose": { "choose": {
"Choice": { "Choice": {
"EN": "I Choose **{0}**", "EN": ["I Choose **{0}**"],
"CHDE": "I nimme **{0}**" "CHDE": ["I nimme **{0}**"]
} }
}, },
"good": { "good": {
"CannotChangeOwn": { "CannotChangeOwn": {
"EN": "Sorry {0}, but you can't give yourself karma", "EN": ["Sorry {0}, but you can't give yourself karma"],
"CHDE": "Sorry {0}, aber du chasch dr selber kei karma geh" "CHDE": ["Sorry {0}, aber du chasch dr selber kei karma geh"]
}, },
"WaitUntill": { "WaitUntill": {
"EN": "Sorry {0}, but you have to wait {1} before you can give karma again...", "EN": ["Sorry {0}, but you have to wait {1} before you can give karma again..."],
"CHDE": "Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch geh..." "CHDE": ["Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch geh..."]
}, },
"Increased": { "Increased": {
"EN": "Karma gained", "EN": ["Karma gained"],
"CHDE": "Karma becho" "CHDE": ["Karma becho"]
}, },
"By": { "By": {
"EN": "By", "EN": ["By"],
"CHDE": "Vo" "CHDE": ["Vo"]
}, },
"Amount": { "Amount": {
"EN": "Amount", "EN": ["Amount"],
"CHDE": "Mengi" "CHDE": ["Mengi"]
}, },
"Current": { "Current": {
"EN": "Current", "EN": ["Current"],
"CHDE": "Jetzt" "CHDE": ["Jetzt"]
} }
}, },
"bad": { "bad": {
"CannotChangeOwn": { "CannotChangeOwn": {
"EN": "Sorry {0}, but you can't lower your own karma", "EN": ["Sorry {0}, but you can't lower your own karma"],
"CHDE": "Sorry {0}, aber du chasch dr din eigete karma nid weg neh" "CHDE": ["Sorry {0}, aber du chasch dr din eigete karma nid weg neh"]
}, },
"WaitUntill": { "WaitUntill": {
"EN": "Sorry {0}, but you have to wait {1} before you can lower karma again...", "EN": ["Sorry {0}, but you have to wait {1} before you can lower karma again..."],
"CHDE": "Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch senke..." "CHDE": ["Sorry {0}, aber du musch no {1} warte bisch d wieder karma chasch senke..."]
}, },
"Decreased": { "Decreased": {
"EN": "Karma lowered", "EN": ["Karma lowered"],
"CHDE": "Karma gsenkt" "CHDE": ["Karma gsenkt"]
}, },
"By": { "By": {
"EN": "By", "EN": ["By"],
"CHDE": "Vo" "CHDE": ["Vo"]
}, },
"Amount": { "Amount": {
"EN": "Amount", "EN": ["Amount"],
"CHDE": "Mengi" "CHDE": ["Mengi"]
}, },
"Current": { "Current": {
"EN": "Current", "EN": ["Current"],
"CHDE": "Jetzt" "CHDE": ["Jetzt"]
} }
}, },
"roll": { "roll": {
"Rolled": { "Rolled": {
"EN": "{0}, you rolled {1}, your guess was {2}", "EN": ["{0}, you rolled {1}, your guess was {2}"],
"CHDE": "{0}, du hesch {1} grollt und hesch {2} grate" "CHDE": ["{0}, du hesch {1} grollt und hesch {2} grate"]
}, },
"Gratz": { "Gratz": {
"EN": "Congratulations {0}, your guess was correct!", "EN": ["Congratulations {0}, your guess was correct!"],
"CHDE": "Gratuliere {0}, du hesch richtig grate!" "CHDE": ["Gratuliere {0}, du hesch richtig grate!"]
}, },
"RolledNoGuess": { "RolledNoGuess": {
"EN": "{0}, you rolled {1}", "EN": ["{0}, you rolled {1}"],
"CHDE": "{0}, du hesch {1} grollt" "CHDE": ["{0}, du hesch {1} grollt"]
}, },
"NoPrevGuess": { "NoPrevGuess": {
"EN": ":red_circle: {0}, you can't guess the same number again", "EN": [":red_circle: {0}, you can't guess the same number again"],
"CHDE": ":red_circle: {0}, du chasch nid nomol es gliche rate" "CHDE": [":red_circle: {0}, du chasch nid nomol es gliche rate"]
} }
}, },
"cookie": { "cookie": {
"GetCookies": { "GetCookies": {
"EN": "You got {0} cookies, there are now {1} cookies in you cookie jar", "EN": ["You got {0} cookies, there are now {1} cookies in you cookie jar"],
"CHDE": "Du häsch {0} guetzli becho, du häsch jetzt {1} guetzli ih dr büchse" "CHDE": ["Du häsch {0} guetzli becho, du häsch jetzt {1} guetzli ih dr büchse"]
}, },
"WaitForMoreCookies": { "WaitForMoreCookies": {
"EN": "You already got cookies in the last 24 hours, wait until {0} for more cookies", "EN": ["You already got cookies in the last 24 hours, you can have more cookies in {0}"],
"CHDE": "Du hesch scho guetzli becho ih de letzti 24 stund, wart no bis {0}" "CHDE": ["Du hesch scho guetzli becho ih de letzti 24 stund, du chasch meh ha in {0}"]
}, },
"InYourJar": { "InYourJar": {
"EN": "There are {0} cookies in you cookie jar", "EN": ["There are {0} cookies in you cookie jar"],
"CHDE": "Es hät {0} guetzli ih dineri büchs" "CHDE": ["Es hät {0} guetzli ih dineri büchs"]
}, },
"Given": { "Given": {
"EN": "You gave {0} cookies to {1}", "EN": ["You gave {0} cookies to {1}"],
"CHDE": "Du hesch {1} {0} guetzli geh" "CHDE": ["Du hesch {1} {0} guetzli geh"]
}, },
"NotEnoughToGive": { "NotEnoughToGive": {
"EN": "You don't have enough cookies", "EN": ["You don't have enough cookies"],
"CHDE": "Du hesch nid gnueg guetzli" "CHDE": ["Du hesch nid gnueg guetzli"]
}, },
"NotEnoughCookiesToEat": { "NotEnoughCookiesToEat": {
"EN": "Your cookie jar looks almost empty, you should probably not eat a cookie", "EN": ["Your cookie jar looks almost empty, you should probably not eat a cookie"],
"CHDE": "Du hesch chuum no guetzli ih dineri büchs, du sötsch warschinli keini esse" "CHDE": ["Du hesch chuum no guetzli ih dineri büchs, du sötsch warschinli keini esse"]
}, },
"AteCookies": { "AteCookies": {
"EN": "You ate {0} cookies, you've only got {1} cookies left", "EN": ["You ate {0} cookies, you've only got {1} cookies left"],
"CHDE": "Du hesch {0} guetzli gesse und hesch jezt no {1} übrig" "CHDE": ["Du hesch {0} guetzli gesse und hesch jezt no {1} übrig"]
} }
} }
} }