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
{
var transDict = await _translation.GetDict(Context);
var transContext = await _translation.GetGuildContext(Context);
var actor = await GetUser(Context.User.Id);
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;
}
actor.Cookies += 10;
actor.LastPayout = DateTimeOffset.Now;
await SetUser(actor);
await ReplyAsync(string.Format(transDict["GetCookies"], 10, actor.Cookies));
await ReplyAsync(transContext.GetString("GetCookies", 10, actor.Cookies));
}
catch (Exception e)
@ -61,9 +62,9 @@ namespace Geekbot.net.Commands.Rpg
{
try
{
var transDict = await _translation.GetDict(Context);
var transContext = await _translation.GetGuildContext(Context);
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)
{
@ -77,12 +78,12 @@ namespace Geekbot.net.Commands.Rpg
{
try
{
var transDict = await _translation.GetDict(Context);
var transContext = await _translation.GetGuildContext(Context);
var giver = await GetUser(Context.User.Id);
if (giver.Cookies < amount)
{
await ReplyAsync(string.Format(transDict["NotEnoughToGive"]));
await ReplyAsync(transContext.GetString("NotEnoughToGive"));
return;
}
@ -94,7 +95,7 @@ namespace Geekbot.net.Commands.Rpg
await SetUser(giver);
await SetUser(taker);
await ReplyAsync(string.Format(transDict["Given"], amount, user.Username));
await ReplyAsync(transContext.GetString("Given", amount, user.Username));
}
catch (Exception e)
{
@ -108,12 +109,12 @@ namespace Geekbot.net.Commands.Rpg
{
try
{
var transDict = await _translation.GetDict(Context);
var transContext = await _translation.GetGuildContext(Context);
var actor = await GetUser(Context.User.Id);
if (actor.Cookies < 5)
{
await ReplyAsync(string.Format(transDict["NotEnoughCookiesToEat"]));
await ReplyAsync(transContext.GetString("NotEnoughCookiesToEat"));
return;
}
@ -122,7 +123,7 @@ namespace Geekbot.net.Commands.Rpg
await SetUser(actor);
await ReplyAsync(string.Format(transDict["AteCookies"], amount, actor.Cookies));
await ReplyAsync(transContext.GetString("AteCookies", amount, actor.Cookies));
}
catch (Exception e)
{

View file

@ -7,8 +7,10 @@ namespace Geekbot.net.Lib.Localization
public interface ITranslationHandler
{
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, string command);
Task<TranslationGuildContext> GetGuildContext(ICommandContext context);
Task<bool> SetLanguage(ulong guildId, string language);
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 IGeekbotLogger _logger;
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)
{
@ -33,8 +33,8 @@ namespace Geekbot.net.Lib.Localization
try
{
var translationFile = File.ReadAllText(Path.GetFullPath("./Lib/Localization/Translations.json"));
var rawTranslations = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, string>>>>(translationFile);
var sortedPerLanguage = new Dictionary<string, Dictionary<string, Dictionary<string, string>>>();
var rawTranslations = JsonSerializer.Deserialize<Dictionary<string, Dictionary<string, Dictionary<string, List<string>>>>>(translationFile);
var sortedPerLanguage = new Dictionary<string, Dictionary<string, Dictionary<string, List<string>>>>();
foreach (var command in rawTranslations)
{
foreach (var str in command.Value)
@ -43,8 +43,8 @@ namespace Geekbot.net.Lib.Localization
{
if (!sortedPerLanguage.ContainsKey(lang.Key))
{
var commandDict = new Dictionary<string, Dictionary<string, string>>();
var strDict = new Dictionary<string, string>
var commandDict = new Dictionary<string, Dictionary<string, List<string>>>();
var strDict = new Dictionary<string, List<string>>
{
{str.Key, lang.Value}
};
@ -53,8 +53,10 @@ namespace Geekbot.net.Lib.Localization
}
if (!sortedPerLanguage[lang.Key].ContainsKey(command.Key))
{
var strDict = new Dictionary<string, string>();
strDict.Add(str.Key, lang.Value);
var strDict = new Dictionary<string, List<string>>
{
{str.Key, lang.Value}
};
sortedPerLanguage[lang.Key].Add(command.Key, strDict);
}
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)
{
var translation = _translations[await GetServerLanguage(guildId)][command][stringName];
if (!string.IsNullOrWhiteSpace(translation)) return translation;
var serverLang = await GetServerLanguage(guildId);
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"];
if (string.IsNullOrWhiteSpace(translation))
if (string.IsNullOrWhiteSpace(translation.First()))
{
_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 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)
{
@ -133,13 +142,21 @@ namespace Geekbot.net.Lib.Localization
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)
{
try
{
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)
{

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": {
"NewLanguageSet": {
"EN": "I will reply in english from now on",
"CHDE": "I werd ab jetzt uf schwiizerdüütsch antworte, äuuä"
"EN": ["I will reply in english from now on"],
"CHDE": ["I werd ab jetzt uf schwiizerdüütsch antworte, äuuä"]
},
"GetLanguage": {
"EN": "I'm talking english",
"CHDE": "I red schwiizerdüütsch"
"EN": ["I'm talking english"],
"CHDE": ["I red schwiizerdüütsch"]
}
},
"errorHandler": {
"SomethingWentWrong": {
"EN": "Something went wrong :confused:",
"CHDE": "Öppis isch schief gange :confused:"
"EN": ["Something went wrong :confused:"],
"CHDE": ["Öppis isch schief gange :confused:"]
}
},
"httpErrors": {
"403": {
"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:"
"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:"]
}
},
"choose": {
"Choice": {
"EN": "I Choose **{0}**",
"CHDE": "I nimme **{0}**"
"EN": ["I Choose **{0}**"],
"CHDE": ["I nimme **{0}**"]
}
},
"good": {
"CannotChangeOwn": {
"EN": "Sorry {0}, but you can't give yourself karma",
"CHDE": "Sorry {0}, aber du chasch dr selber kei karma geh"
"EN": ["Sorry {0}, but you can't give yourself karma"],
"CHDE": ["Sorry {0}, aber du chasch dr selber kei karma geh"]
},
"WaitUntill": {
"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..."
"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..."]
},
"Increased": {
"EN": "Karma gained",
"CHDE": "Karma becho"
"EN": ["Karma gained"],
"CHDE": ["Karma becho"]
},
"By": {
"EN": "By",
"CHDE": "Vo"
"EN": ["By"],
"CHDE": ["Vo"]
},
"Amount": {
"EN": "Amount",
"CHDE": "Mengi"
"EN": ["Amount"],
"CHDE": ["Mengi"]
},
"Current": {
"EN": "Current",
"CHDE": "Jetzt"
"EN": ["Current"],
"CHDE": ["Jetzt"]
}
},
"bad": {
"CannotChangeOwn": {
"EN": "Sorry {0}, but you can't lower your own karma",
"CHDE": "Sorry {0}, aber du chasch dr din eigete karma nid weg neh"
"EN": ["Sorry {0}, but you can't lower your own karma"],
"CHDE": ["Sorry {0}, aber du chasch dr din eigete karma nid weg neh"]
},
"WaitUntill": {
"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..."
"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..."]
},
"Decreased": {
"EN": "Karma lowered",
"CHDE": "Karma gsenkt"
"EN": ["Karma lowered"],
"CHDE": ["Karma gsenkt"]
},
"By": {
"EN": "By",
"CHDE": "Vo"
"EN": ["By"],
"CHDE": ["Vo"]
},
"Amount": {
"EN": "Amount",
"CHDE": "Mengi"
"EN": ["Amount"],
"CHDE": ["Mengi"]
},
"Current": {
"EN": "Current",
"CHDE": "Jetzt"
"EN": ["Current"],
"CHDE": ["Jetzt"]
}
},
"roll": {
"Rolled": {
"EN": "{0}, you rolled {1}, your guess was {2}",
"CHDE": "{0}, du hesch {1} grollt und hesch {2} grate"
"EN": ["{0}, you rolled {1}, your guess was {2}"],
"CHDE": ["{0}, du hesch {1} grollt und hesch {2} grate"]
},
"Gratz": {
"EN": "Congratulations {0}, your guess was correct!",
"CHDE": "Gratuliere {0}, du hesch richtig grate!"
"EN": ["Congratulations {0}, your guess was correct!"],
"CHDE": ["Gratuliere {0}, du hesch richtig grate!"]
},
"RolledNoGuess": {
"EN": "{0}, you rolled {1}",
"CHDE": "{0}, du hesch {1} grollt"
"EN": ["{0}, you rolled {1}"],
"CHDE": ["{0}, du hesch {1} grollt"]
},
"NoPrevGuess": {
"EN": ":red_circle: {0}, you can't guess the same number again",
"CHDE": ":red_circle: {0}, du chasch nid nomol es gliche rate"
"EN": [":red_circle: {0}, you can't guess the same number again"],
"CHDE": [":red_circle: {0}, du chasch nid nomol es gliche rate"]
}
},
"cookie": {
"GetCookies": {
"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"
"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"]
},
"WaitForMoreCookies": {
"EN": "You already got cookies in the last 24 hours, wait until {0} for more cookies",
"CHDE": "Du hesch scho guetzli becho ih de letzti 24 stund, wart no bis {0}"
"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, du chasch meh ha in {0}"]
},
"InYourJar": {
"EN": "There are {0} cookies in you cookie jar",
"CHDE": "Es hät {0} guetzli ih dineri büchs"
"EN": ["There are {0} cookies in you cookie jar"],
"CHDE": ["Es hät {0} guetzli ih dineri büchs"]
},
"Given": {
"EN": "You gave {0} cookies to {1}",
"CHDE": "Du hesch {1} {0} guetzli geh"
"EN": ["You gave {0} cookies to {1}"],
"CHDE": ["Du hesch {1} {0} guetzli geh"]
},
"NotEnoughToGive": {
"EN": "You don't have enough cookies",
"CHDE": "Du hesch nid gnueg guetzli"
"EN": ["You don't have enough cookies"],
"CHDE": ["Du hesch nid gnueg guetzli"]
},
"NotEnoughCookiesToEat": {
"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"
"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"]
},
"AteCookies": {
"EN": "You ate {0} cookies, you've only got {1} cookies left",
"CHDE": "Du hesch {0} guetzli gesse und hesch jezt no {1} übrig"
"EN": ["You ate {0} cookies, you've only got {1} cookies left"],
"CHDE": ["Du hesch {0} guetzli gesse und hesch jezt no {1} übrig"]
}
}
}