Mercurial > repos > IBBoard
changeset 6:f269d8bcc152
Re #2 - Refactor API
* Refactor translations in to smaller and clearer functions
* Add "get current translation language" function
* Add documentation of public methods
author | IBBoard <dev@ibboard.co.uk> |
---|---|
date | Sat, 27 Dec 2008 20:28:04 +0000 |
parents | 5ee4956dae76 |
children | f4da31cb09d9 |
files | Lang/Translation.cs |
diffstat | 1 files changed, 231 insertions(+), 137 deletions(-) [+] |
line wrap: on
line diff
--- a/Lang/Translation.cs Sat Dec 27 18:48:12 2008 +0000 +++ b/Lang/Translation.cs Sat Dec 27 20:28:04 2008 +0000 @@ -3,7 +3,6 @@ using System.Xml; using System.Xml.Schema; using System.Collections.Generic; -using System.Windows.Forms; using System.Reflection; using System.ComponentModel; using IBBoard.IO; @@ -13,68 +12,116 @@ namespace IBBoard.Lang { /// <summary> - /// Summary description for Translation. + /// A basic string translator that loads a default language and a specified language and returns translated strings that correspond to translation IDs. + /// If the string doesn't exist in the specified language then the translator falls back to the default language. If the translation doesn't exist in the default language + /// then either a supplied value or a "no validation available" message is returned. /// </summary> public class Translation - { + { + private static readonly string DEFAULT_LANGUAGE = "en"; + private static readonly string DIVIDER_STRING = "-"; private static string lang = ""; - private static DirectoryInfo translationDir = null; - private static Dictionary<string, string> translationsLocal = null; - private static Dictionary<string, string> translationsDefault = null; - + private static DirectoryInfo translationDir; + private static Dictionary<string, string> translationsLocal; + private static Dictionary<string, string> translationsDefault; + + /// <summary> + /// Initialises the translations for the language specified and the default translations so that the Translation class can be used + /// </summary> + /// <param name="appPath"> + /// The full path that the application is running from. Must contain the "translations" folder. + /// </param> + /// <param name="language"> + /// The language to use as the load language + /// </param> public static void InitialiseTranslations(string appPath, string language) - { - string translationPath = appPath+Constants.DirectoryString+"translations"; + { + InitialiseDefaults(appPath); + FileInfo file = GetTranslationFile(DEFAULT_LANGUAGE); + XmlDocument doc = LoadTranslationDocument(file); + LoadTranslationsFromDocument(doc, translationsDefault); + translationsLocal = null; + LoadTranslation(lang); + } + + private static void InitialiseDefaults(string appPath) + { + string translationPath = appPath.TrimEnd(Constants.DirectoryChar) + Constants.DirectoryString + "translations"; if (Directory.Exists(translationPath)) - { + { + translationsDefault = new Dictionary<string,string>(); + translationsLocal = new Dictionary<string,string>(); translationDir = new DirectoryInfo(translationPath); - - FileInfo file = new FileInfo(translationDir.FullName.TrimEnd(Constants.DirectoryChar)+Constants.DirectoryString+"en.translation"); - - if (!File.Exists(file.FullName)) - { - throw new FileNotFoundException("en.translation could not be found in "+translationDir.FullName); - } - - translationsDefault = new Dictionary<string,string>(); - - XmlDocument doc = new XmlDocument(); - XmlReaderSettings settings = new XmlReaderSettings(); - settings.XmlResolver = new IBBXmlResolver(appPath); - settings.ValidationType = ValidationType.DTD; - settings.ProhibitDtd = false; - settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod); - XmlReader valReader = XmlReader.Create(file.FullName, settings); - doc.Load(valReader); - valReader.Close(); - - try - { - XmlNodeList translations = doc.GetElementsByTagName("translation"); - - if (translations.Count==0) - { - throw new InvalidFileException("No translations found in "+language+".translation"); - } - - foreach (XmlNode node in translations) - { - translationsDefault.Add(node.Attributes["id"].Value, node.InnerText); - } - } - catch(Exception ex) - { - throw new XmlParseException("Error while parsing "+file.FullName+": "+ex.Message); - } - - translationsLocal = null; - LoadTranslation(lang); } else { throw new ArgumentException("Translation path must exist ("+translationPath+")"); + } + } + + private static XmlDocument LoadTranslationDocument(FileInfo file) + { + XmlDocument doc = new XmlDocument(); + XmlReaderSettings settings = new XmlReaderSettings(); + settings.XmlResolver = new IBBXmlResolver(translationDir.Parent.FullName); + settings.ValidationType = ValidationType.DTD; + settings.ProhibitDtd = false; + settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod); + XmlReader valReader = XmlReader.Create(file.FullName, settings); + doc.Load(valReader); + valReader.Close(); + return doc; + } + + private static FileInfo GetTranslationFile(string language) + { + FileInfo file = new FileInfo(translationDir.FullName + Constants.DirectoryString + language + ".translation"); + + if (!file.Exists) + { + throw new FileNotFoundException(language + "translation could not be found in "+translationDir.FullName); + } + + return file; + } + + private static void LoadTranslationsFromDocument(XmlDocument doc, Dictionary<string, string> translationTable) + { + try + { + XmlNodeList translations = doc.GetElementsByTagName("translation"); + + if (translations.Count==0) + { + throw new InvalidFileException("No translations found in "+GetLanguageOfDocument(doc)+".translation"); + } + + Dictionary<string, string> tempTranslationTable = new Dictionary<string,string>(); + + foreach (XmlNode node in translations) + { + tempTranslationTable.Add(node.Attributes["id"].Value, node.InnerText); + } + + translationTable.Clear(); + + foreach (string key in tempTranslationTable.Keys) + { + string translation; + tempTranslationTable.TryGetValue(key, out translation); + translationTable.Add(key, translation); + } } + catch(Exception ex) + { + throw new XmlParseException("Error while parsing " + GetLanguageOfDocument(doc)+" translation: "+ex.Message); + } + } + + private static string GetLanguageOfDocument(XmlDocument doc) + { + return doc != null ? doc.DocumentElement.GetAttribute("lang") : ""; } private static void ValidationEventMethod(object sender, ValidationEventArgs e) @@ -82,113 +129,140 @@ //TODO: Fire a validation failure event LogNotifier.Error(typeof(Translation), "Validation Error", e.Exception); } - + + /// <summary> + /// Loads translations for a given language and sets them as the local language + /// </summary> + /// <param name="translationLang"> + /// The new local language to load + /// </param> public static void LoadTranslation(string translationLang) { - checkInitialisation(); - - if (translationLang!="" && translationLang!="en") - { - FileInfo file = new FileInfo(translationDir.FullName.TrimEnd(Constants.DirectoryChar)+Constants.DirectoryString+translationLang+".translation"); - - if (!File.Exists(file.FullName)) - { - throw new FileNotFoundException(translationLang+".translation could not be found in "+translationDir.FullName); - } - - XmlDocument doc = new XmlDocument(); - XmlReaderSettings settings = new XmlReaderSettings(); - settings.XmlResolver = new IBBXmlResolver(translationDir.Parent.FullName); - settings.ValidationType = ValidationType.DTD; - settings.ProhibitDtd = false; - settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod); - XmlReader valReader = XmlReader.Create(file.FullName, settings); - doc.Load(valReader); - valReader.Close(); - Dictionary<string, string> tempTranslations = new Dictionary<string, string>(); - - try - { - XmlNodeList translations = doc.GetElementsByTagName("translation"); + checkInitialisation(); + + if (translationLang == "" || translationLang == null) + { + throw new ArgumentException("Translation language cannot be empty or null"); + } - if (translations.Count==0) - { - throw new InvalidFileException("No translations found in "+translationLang+".translation"); - } - - foreach (XmlNode node in translations) - { - tempTranslations.Add(node.Attributes["id"].Value, node.InnerText); - } - } - catch(XmlParseException) - { - throw; - } - catch(Exception ex) - { - throw new XmlParseException("Error while parsing "+file.FullName+": "+ex.Message); - } - - translationsLocal = tempTranslations; - lang = translationLang; - } - else + if (translationLang != DEFAULT_LANGUAGE) { - lang = translationLang; - } + FileInfo file = GetTranslationFile(translationLang); + XmlDocument doc = LoadTranslationDocument(file); + LoadTranslationsFromDocument(doc, translationsLocal); + } + else + { + translationsLocal.Clear(); + } + + lang = translationLang; } - + + /// <summary> + /// Gets a translation for a given ID, falling back to a "missing translation" message if none can be found. Also optionally replaces any placeholders with the supplied values. + /// </summary> + /// <param name="translationID"> + /// The ID to look up the translation for + /// </param> + /// <param name="replacements"> + /// A collection of <see cref="System.Object"/>s to replace placeholders with + /// </param> + /// <returns> + /// The translation with the placeholders replaced or a "missing translation" message + /// </returns> public static string GetTranslation(string translationID, params object[] replacements) { return GetTranslation(translationID, false, replacements); } - + + /// <summary> + /// Gets a translation for a given ID, falling back to null or a warning message if a translation cannot be found. Also optionally replaces any placeholders with the supplied values. + /// </summary> + /// <param name="translationID"> + /// The ID to look up the translation for + /// </param> + /// <param name="returnNullOnFail"> + /// TRUE if null should be returned when no translation can be found, or FALSE if a "missing translation" message should be returned + /// </param> + /// <param name="replacements"> + /// A collection of <see cref="System.Object"/>s to replace placeholders with + /// </param> + /// <returns> + /// The translation with the placeholders replaced, or a "missing translation" message or null depending on <param name="returnNullOnFail"> + /// </returns> public static string GetTranslation(string translationID, bool returnNullOnFail, params object[] replacements) { return GetTranslation(translationID, returnNullOnFail ? null : "", replacements); } - + + /// <summary> + /// Gets a translation for a given ID, falling back to a supplied default if a translation cannot be found. Also optionally replaces any placeholders with the supplied values. + /// </summary> + /// <param name="translationID"> + /// The ID to look up the translation for + /// </param> + /// <param name="defaultTranslation"> + /// The string to return if no translation can be found. Can be null or any string. + /// </param> + /// <param name="replacements"> + /// A collection of <see cref="System.Object"/>s to replace placeholders with + /// </param> + /// <returns> + /// The translation, if one exists, or the supplied default with the placeholders replaced + /// </returns> public static string GetTranslation(string translationID, string defaultTranslation, params object[] replacements) { checkInitialisation(); - string trans = null; - - if (translationsLocal!=null) - { - translationsLocal.TryGetValue(translationID, out trans); - } - - if (trans == null) - { - translationsDefault.TryGetValue(translationID, out trans); - } + string trans = GetTranslationFromTables(translationID); if (trans == null) { - //no translation so fall back to the provided default if not an empty string - if (defaultTranslation!="") - { - trans = defaultTranslation; - } - else - { - trans = "++ Missing Translation "+translationID+" ++"; - } + trans = GetDefaultTranslation(translationID, defaultTranslation); } - if (trans != null) - { - if (replacements!=null && replacements.Length > 0) - { - trans = String.Format(trans, replacements); - } - //else no need to do anything - } + trans = AddVariablesToTranslation(trans, replacements); return trans; + } + + private static string GetTranslationFromTables(string translationID) + { + string translation = null; + + if (translationsLocal!=null) + { + translationsLocal.TryGetValue(translationID, out translation); + } + + if (translation == null) + { + translationsDefault.TryGetValue(translationID, out translation); + } + + return translation; + } + + private static string GetDefaultTranslation(string translationID, string defaultTranslation) + { + return (defaultTranslation != "" && defaultTranslation != null) ? defaultTranslation : GetMissingTranslationMessage(translationID); } - + + private static string GetMissingTranslationMessage(string translationID) + { + return "++ Missing Translation "+translationID+" ++"; + } + + private static string AddVariablesToTranslation(string translation, object[] replacements) + { + if (translation != null && replacements != null && replacements.Length > 0) + { + translation = String.Format(translation, replacements); + } + + return translation; + } + private static void checkInitialisation() { if (translationDir==null) @@ -196,16 +270,36 @@ throw new InvalidOperationException("Translation class has not been initialised"); } } - + + /// <summary> + /// Translate an <see cref="ITranslatable"/> item, with optional string replacement + /// </summary> + /// <param name="item"> + /// A <see cref="ITranslatable"/> to set the text for + /// </param> + /// <param name="replacements"> + /// A collection of <see cref="System.Object"/>s that will be used to fill place-holders + /// </param> public static void Translate(ITranslatable item, params object[] replacements) { - if (item.Text=="" || item.Text=="-") + if (item.Text == "" || item.Text == DIVIDER_STRING) { - //if it doesn't need translating - either no text for the Dev or it's a hyphen for a divider - then bail early + //it doesn't need translating - either there is no text from the developer or it's a hyphen for a divider return; } item.Text = GetTranslation(item.Name, replacements); + } + + /// <summary> + /// Get the current local translation language. This is an arbitrary string used in the translation file's name and will not necessarily match the ISO language code. + /// </summary> + /// <returns> + /// The string used in the file name of the current local translation + /// </returns> + public static string GetTranslationLanguage() + { + return lang; } } }