view Lang/Translation.cs @ 3:3dab3db95e4a

Fix SimpleSet so that the enumerator works over the values in the set
author IBBoard <dev@ibboard.co.uk>
date Tue, 23 Dec 2008 14:21:03 +0000
parents 961030992bd2
children f9ec2be467fe
line wrap: on
line source

using System;
using System.IO;
using System.Xml;
using System.Xml.Schema;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Reflection;
using System.ComponentModel;
using IBBoard.IO;
using IBBoard.Logging;
using IBBoard.Xml;

namespace IBBoard.Lang
{
	/// <summary>
	/// Summary description for Translation.
	/// </summary>
	public class Translation
	{		
		private static string lang = "";
		private static DirectoryInfo translationDir = null;
		private static Dictionary<string, string> translationsLocal = null;
		private static Dictionary<string, string> translationsDefault = null;

		public static void InitialiseTranslations(string appPath, string language)
		{
			string translationPath = appPath+Constants.DirectoryString+"translations";

			if (Directory.Exists(translationPath))
			{
				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 void ValidationEventMethod(object sender, ValidationEventArgs e)
		{
			//TODO: Fire a validation failure event
    		LogNotifier.Error(typeof(Translation), "Validation Error", e.Exception);
		}

		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");

					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
			{
				lang = translationLang;
			}
		}

		public static string GetTranslation(string translationID, params object[] replacements)
		{
			return GetTranslation(translationID, false, replacements);
		}

		public static string GetTranslation(string translationID, bool returnNullOnFail, params object[] replacements)
		{
			return GetTranslation(translationID, returnNullOnFail ? null : "", replacements);
		}

		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);
			}
			
			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+" ++";
				}
			}

			if (trans != null)
			{
				if (replacements!=null && replacements.Length > 0)
				{
					trans = String.Format(trans, replacements);
				}
				//else no need to do anything
			}

			return trans;
		}

		private static void checkInitialisation()
		{
			if (translationDir==null)
			{
				throw new InvalidOperationException("Translation class has not been initialised");
			}
		}

		public static void Translate(ITranslatable item, params object[] replacements)
		{
			if (item.Text=="" || item.Text=="-")
			{
				//if it doesn't need translating - either no text for the Dev or it's a hyphen for a divider - then bail early
				return;
			}
			
			item.Text = GetTranslation(item.Name, replacements);
		}

		public static void TranslateControl(Control ctrl, params object[] replacements)
		{
			TranslateControl(ctrl, true, replacements);
		}

		public static void TranslateControl(Control ctrl, bool cascadeTranslate, params object[] replacements)
		{
			if (ctrl is ITranslatable)
			{
				Translate((ITranslatable)ctrl, replacements);
			}

			if (cascadeTranslate)
			{
				cascadeControlTranslation(ctrl, replacements);
			}
		}

		private static void cascadeControlTranslation(Control ctrl, params object[] replacements)
		{
			if (ctrl is ToolBar)
			{
				foreach(ToolBarButton bttn in ((ToolBar)ctrl).Buttons)
				{
					TranslateComponent(bttn, true, replacements);
				}
			}
			else 
			{
				foreach (Control subctr in ctrl.Controls)
				{
					TranslateControl(subctr, true, replacements);
				}
			}
		}

		public static void TranslateComponent(Component comp, params object[] replacements)
		{
			TranslateComponent(comp, true, replacements);
		}

		public static void TranslateComponent(Component comp, bool cascadeTranslate, params object[] replacements)
		{
			if (comp is ITranslatable)
			{
				Translate((ITranslatable)comp, replacements);
			}
			else if (comp is FileDialog)
			{
				//HACK: We can't override SWF dialogs in .Net 1.1, so put in a special condition check for them
				FileDialog dialog = (FileDialog)comp;

				if (dialog.Title.StartsWith("Translatable:"))
				{
					dialog.Title = GetTranslation(dialog.Title.Substring(13), replacements);
				}
			}

			if (cascadeTranslate)
			{
				cascadeComponentTranslations(comp, cascadeTranslate, replacements);
			}
		}

		private static void cascadeComponentTranslations(Component comp, params object[] replacements)
		{
			if (comp is Menu)
			{
				foreach(MenuItem mi in ((Menu)comp).MenuItems)
				{
					TranslateComponent(mi, true, replacements);
				}
			}
		}
	}
}