PKHeX/PKHeX.WinForms/Util/WinFormsTranslator.cs
Kurt aedc0092b4 Rework translations
instead of:
controls on form->dict of names, look up line to find control
now:
dict of names->look up control to find name

faster overall, simpler, and updates the dictionary with new control
entries. browse thru forms that call translateinterface with one
language (english), then updateall(en) and dump to get the updated
translation files.

#1864
2018-03-20 22:34:44 -07:00

183 lines
6.9 KiB
C#

using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Windows.Forms;
using PKHeX.Core;
namespace PKHeX.WinForms
{
public static class WinFormsTranslator
{
private static readonly Dictionary<string, TranslationContext> Context = new Dictionary<string, TranslationContext>();
internal static void TranslateInterface(this Control form, string lang) => TranslateForm(form, GetContext(lang));
private static string GetTranslationFileNameInternal(string lang) => $"lang_{lang}";
private static string GetTranslationFileNameExternal(string lang) => $"lang_{lang}.txt";
private static TranslationContext GetContext(string lang)
{
if (Context.TryGetValue(lang, out var context))
return context;
var lines = GetTranslationFile(lang);
Context.Add(lang, context = new TranslationContext(lines));
return context;
}
private static void TranslateForm(Control form, TranslationContext context)
{
form.SuspendLayout();
var formname = form.Name;
// Translate Title
form.Text = context.GetTranslatedText(formname, form.Text);
var translatable = GetTranslatableControls(form);
foreach (var c in translatable)
{
if (c is Control r)
r.Text = context.GetTranslatedText($"{formname}.{r.Name}", r.Text);
else if (c is ToolStripItem t)
t.Text = context.GetTranslatedText($"{formname}.{t.Name}", t.Text);
}
form.ResumeLayout();
}
private static IEnumerable<string> GetTranslationFile(string lang)
{
var file = GetTranslationFileNameInternal(lang);
// Check to see if a the translation file exists in the same folder as the executable
string externalLangPath = GetTranslationFileNameExternal(file);
if (File.Exists(externalLangPath))
{
try { return File.ReadAllLines(externalLangPath); }
catch { /* In use? Just return the internal resource. */ }
}
return Util.GetStringList(file);
}
private static IEnumerable<object> GetTranslatableControls(Control f)
{
foreach (var z in f.GetChildrenOfType<Control>())
{
switch (z)
{
case ToolStrip menu:
foreach (var obj in GetToolStripMenuItems(menu))
yield return obj;
break;
default:
if (string.IsNullOrWhiteSpace(z.Name))
break;
if (z.ContextMenuStrip != null) // control has attached menustrip
foreach (var obj in GetToolStripMenuItems(z.ContextMenuStrip))
yield return obj;
if (z is ComboBox || z is TextBox || z is MaskedTextBox || z is LinkLabel)
break; // undesirable to modify, ignore
if (!string.IsNullOrWhiteSpace(z.Text))
yield return z;
break;
}
}
}
private static IEnumerable<T> GetChildrenOfType<T>(this Control control) where T : class
{
foreach (Control child in control.Controls)
{
var childOfT = child as T;
if (childOfT != null)
yield return childOfT;
if (!child.HasChildren) continue;
foreach (var descendant in GetChildrenOfType<T>(child))
yield return descendant;
}
}
private static IEnumerable<object> GetToolStripMenuItems(ToolStrip menu)
{
foreach (var i in menu.Items.OfType<ToolStripMenuItem>())
{
if (!string.IsNullOrWhiteSpace(i.Text))
yield return i;
foreach (var sub in GetToolsStripDropDownItems(i).Where(z => !string.IsNullOrWhiteSpace(z.Text)))
yield return sub;
}
}
private static IEnumerable<ToolStripMenuItem> GetToolsStripDropDownItems(ToolStripDropDownItem item)
{
foreach (var dropDownItem in item.DropDownItems.OfType<ToolStripMenuItem>())
{
yield return dropDownItem;
if (!dropDownItem.HasDropDownItems) continue;
foreach (ToolStripMenuItem subItem in GetToolsStripDropDownItems(dropDownItem))
yield return subItem;
}
}
public static void UpdateAll(string baseLanguage, IEnumerable<string> others)
{
var basecontext = GetContext(baseLanguage);
foreach (var lang in others)
{
var c = GetContext(lang);
c.UpdateFrom(basecontext);
}
}
public static void DumpAll()
{
var results = Context.Select(z => new {Lang = z.Key, Lines = z.Value.Write()});
foreach (var c in results)
File.WriteAllLines(GetTranslationFileNameExternal(c.Lang), c.Lines);
}
}
public class TranslationContext
{
public bool AddNew { get; set; } = true;
public bool RemoveUsedKeys { get; set; } = false;
private readonly Dictionary<string, string> Translation = new Dictionary<string, string>();
public TranslationContext(IEnumerable<string> content, char separator = '=')
{
var entries = content.Select(z => z.Split(separator)).Where(z => z.Length == 2);
foreach (var r in entries.Where(z => !Translation.ContainsKey(z[0])))
Translation.Add(r[0], r[1]);
}
public string GetTranslatedText(string val, string fallback)
{
if (RemoveUsedKeys)
Translation.Remove(val);
if (Translation.TryGetValue(val, out var translated))
return translated;
if (fallback != null && AddNew)
Translation.Add(val, fallback);
return fallback;
}
public IEnumerable<string> Write(char separator = '=')
{
return Translation.Select(z => $"{z.Key}{separator}{z.Value}").OrderBy(z => z.Contains(".")).ThenBy(z => z);
}
public void UpdateFrom(TranslationContext other)
{
bool oldAdd = AddNew;
AddNew = true;
foreach (var kvp in other.Translation)
GetTranslatedText(kvp.Key, kvp.Value);
AddNew = oldAdd;
}
public void RemoveKeys(TranslationContext other)
{
foreach (var kvp in other.Translation)
Translation.Remove(kvp.Key);
}
}
}