diff options
21 files changed, 672 insertions, 37 deletions
diff --git a/Mono.Addins/ChangeLog b/Mono.Addins/ChangeLog index 512ddc7..c78ea5b 100644 --- a/Mono.Addins/ChangeLog +++ b/Mono.Addins/ChangeLog @@ -1,3 +1,23 @@ +2007-11-17 Lluis Sanchez Gual <lluis@novell.com> + + * Mono.Addins.Localization, Mono.Addins.Localization/GettextDomain.cs, + Mono.Addins.Localization/IAddinLocalizer.cs, + Mono.Addins.Localization/NullLocalizer.cs, + Mono.Addins.Localization/StringTableLocalizer.cs, + Mono.Addins.Localization/StringResourceLocalizer.cs, + Mono.Addins.Localization/IAddinLocalizerFactory.cs, + Mono.Addins.Localization/GettextLocalizer.cs, Mono.Addins.mdp, + Mono.Addins/RuntimeAddin.cs, Mono.Addins/ExtensionTree.cs, + Mono.Addins/NodeAttributeAttribute.cs, Mono.Addins/AddinManager.cs, + Mono.Addins/ExtensionNode.cs, Mono.Addins/AddinLocalizer.cs, + Makefile.am, Mono.Addins.Description/NodeTypeAttribute.cs, + Mono.Addins.Description/AddinDescription.cs, + Mono.Addins.Description/ExtensionNodeType.cs: Implemented support for + add-in localization. + * Mono.Addins/NodeElement.cs, + Mono.Addins.Description/ExtensionNodeDescriptionCollection.cs: Added + ChildNodes property to NodeElement. + 2007-10-29 Lluis Sanchez Gual <lluis@novell.com> * Mono.Addins.mdp: Update paths. diff --git a/Mono.Addins/Makefile.am b/Mono.Addins/Makefile.am index 2c2fbfb..fd9ab58 100644 --- a/Mono.Addins/Makefile.am +++ b/Mono.Addins/Makefile.am @@ -49,6 +49,13 @@ FILES = \ Mono.Addins.Description/NodeTypeAttributeCollection.cs \ Mono.Addins.Description/ObjectDescription.cs \ Mono.Addins.Description/ObjectDescriptionCollection.cs \ + Mono.Addins.Localization/GettextDomain.cs \ + Mono.Addins.Localization/GettextLocalizer.cs \ + Mono.Addins.Localization/IAddinLocalizer.cs \ + Mono.Addins.Localization/IAddinLocalizerFactory.cs \ + Mono.Addins.Localization/NullLocalizer.cs \ + Mono.Addins.Localization/StringResourceLocalizer.cs \ + Mono.Addins.Localization/StringTableLocalizer.cs \ Mono.Addins.Serialization/BinaryXmlReader.cs \ Mono.Addins.Serialization/BinaryXmlTypeMap.cs \ Mono.Addins.Serialization/BinaryXmlWriter.cs \ @@ -59,6 +66,7 @@ FILES = \ Mono.Addins/AddinErrorEventArgs.cs \ Mono.Addins/AddinEventArgs.cs \ Mono.Addins/AddinInfo.cs \ + Mono.Addins/AddinLocalizer.cs \ Mono.Addins/AddinManager.cs \ Mono.Addins/AddinRegistry.cs \ Mono.Addins/AddinRootAttribute.cs \ diff --git a/Mono.Addins/Mono.Addins.Description/AddinDescription.cs b/Mono.Addins/Mono.Addins.Description/AddinDescription.cs index b15f52d..2ffc008 100644 --- a/Mono.Addins/Mono.Addins.Description/AddinDescription.cs +++ b/Mono.Addins/Mono.Addins.Description/AddinDescription.cs @@ -68,6 +68,7 @@ namespace Mono.Addins.Description ExtensionNodeSetCollection nodeSets; ConditionTypeDescriptionCollection conditionTypes; ExtensionPointCollection extensionPoints; + ExtensionNodeDescription localizer; object[] fileInfo; internal static BinaryXmlTypeMap typeMap; @@ -278,6 +279,11 @@ namespace Mono.Addins.Description } } + public ExtensionNodeDescription Localizer { + get { return localizer; } + set { localizer = value; } + } + public ExtensionPoint AddExtensionPoint (string path) { ExtensionPoint ep = new ExtensionPoint (); @@ -467,7 +473,16 @@ namespace Mono.Addins.Description elem.SetAttribute ("category", category); else elem.RemoveAttribute ("category"); - + + if (localizer == null || localizer.Element == null) { + // Remove old element if it exists + XmlElement oldLoc = (XmlElement) elem.SelectSingleNode ("Localizer"); + if (oldLoc != null) + elem.RemoveChild (oldLoc); + } + if (localizer != null) + localizer.SaveXml (elem); + if (mainModule != null) { mainModule.Element = elem; mainModule.SaveXml (elem); @@ -525,6 +540,10 @@ namespace Mono.Addins.Description s = elem.GetAttribute ("defaultEnabled"); config.defaultEnabled = s.Length == 0 || s == "true" || s == "yes"; + XmlElement localizerElem = (XmlElement) elem.SelectSingleNode ("Localizer"); + if (localizerElem != null) + config.localizer = new ExtensionNodeDescription (localizerElem); + if (config.id.Length > 0) config.hasUserId = true; @@ -596,6 +615,10 @@ namespace Mono.Addins.Description } } + if (localizer != null && localizer.GetAttribute ("type").Length == 0) { + errors.Add ("The attribute 'type' in the Location element is required."); + } + return errors; } @@ -658,6 +681,7 @@ namespace Mono.Addins.Description writer.WriteValue ("ExtensionPoints", ExtensionPoints); writer.WriteValue ("ConditionTypes", ConditionTypes); writer.WriteValue ("FilesInfo", fileInfo); + writer.WriteValue ("Localizer", localizer); } void IBinaryXmlElement.Read (BinaryXmlReader reader) @@ -684,6 +708,7 @@ namespace Mono.Addins.Description extensionPoints = (ExtensionPointCollection) reader.ReadValue ("ExtensionPoints", new ExtensionPointCollection (this)); conditionTypes = (ConditionTypeDescriptionCollection) reader.ReadValue ("ConditionTypes", new ConditionTypeDescriptionCollection (this)); fileInfo = (object[]) reader.ReadValue ("FilesInfo", null); + localizer = (ExtensionNodeDescription) reader.ReadValue ("Localizer"); if (mainModule != null) mainModule.SetParent (this); diff --git a/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescriptionCollection.cs b/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescriptionCollection.cs index 67bf52f..1f81051 100644 --- a/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescriptionCollection.cs +++ b/Mono.Addins/Mono.Addins.Description/ExtensionNodeDescriptionCollection.cs @@ -32,7 +32,7 @@ using System.Collections; namespace Mono.Addins.Description { - public class ExtensionNodeDescriptionCollection: ObjectDescriptionCollection + public class ExtensionNodeDescriptionCollection: ObjectDescriptionCollection, NodeElementCollection { public ExtensionNodeDescriptionCollection () { @@ -54,5 +54,9 @@ namespace Mono.Addins.Description return null; } } + + NodeElement NodeElementCollection.this [int n] { + get { return (NodeElement) List [n]; } + } } } diff --git a/Mono.Addins/Mono.Addins.Description/ExtensionNodeType.cs b/Mono.Addins/Mono.Addins.Description/ExtensionNodeType.cs index 5054e9e..cb2ffa4 100644 --- a/Mono.Addins/Mono.Addins.Description/ExtensionNodeType.cs +++ b/Mono.Addins/Mono.Addins.Description/ExtensionNodeType.cs @@ -31,6 +31,7 @@ using System; using System.Xml; using System.Collections; using System.Collections.Specialized; +using System.Reflection; using Mono.Addins.Serialization; namespace Mono.Addins.Description @@ -50,7 +51,12 @@ namespace Mono.Addins.Description // Cached serializable fields [NonSerialized] internal Hashtable Fields; - internal string[] RequiredFields; + + internal class FieldData { + public FieldInfo Field; + public bool Required; + public bool Localizable; + } // Addin where this extension type is implemented internal string AddinId { diff --git a/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs b/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs index 1a33441..52a0a6b 100644 --- a/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs +++ b/Mono.Addins/Mono.Addins.Description/NodeTypeAttribute.cs @@ -39,6 +39,7 @@ namespace Mono.Addins.Description string name; string type; bool required; + bool localizable; string description; public NodeTypeAttribute() @@ -55,6 +56,11 @@ namespace Mono.Addins.Description set { required = value; } } + public bool Localizable { + get { return localizable; } + set { localizable = value; } + } + public string Type { get { return type != null ? type : string.Empty; } set { type = value; } @@ -75,6 +81,7 @@ namespace Mono.Addins.Description name = elem.GetAttribute ("name"); type = elem.GetAttribute ("type"); required = elem.GetAttribute ("required").ToLower () == "true"; + localizable = elem.GetAttribute ("localizable").ToLower () == "true"; description = ReadXmlDescription (); } @@ -93,6 +100,11 @@ namespace Mono.Addins.Description else Element.RemoveAttribute ("required"); + if (localizable) + Element.SetAttribute ("localizable", "True"); + else + Element.RemoveAttribute ("localizable"); + SaveXmlDescription (description); } @@ -102,6 +114,7 @@ namespace Mono.Addins.Description writer.WriteValue ("type", type); writer.WriteValue ("required", required); writer.WriteValue ("description", description); + writer.WriteValue ("localizable", localizable); } internal override void Read (BinaryXmlReader reader) @@ -111,6 +124,7 @@ namespace Mono.Addins.Description required = reader.ReadBooleanValue ("required"); if (!reader.IgnoreDescriptionData) description = reader.ReadStringValue ("description"); + localizable = reader.ReadBooleanValue ("localizable"); } } } diff --git a/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs b/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs new file mode 100644 index 0000000..8b9d8cb --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/GettextDomain.cs @@ -0,0 +1,119 @@ +// +// GettextDomain.cs: Wrappers for the libintl library. +// +// Authors: +// Edd Dumbill (edd@usefulinc.com) +// Jonathan Pryor (jonpryor@vt.edu) +// Lluis Sanchez Gual (lluis@novell.com) +// +// (C) 2004 Edd Dumbill +// (C) 2005-2006 Jonathan Pryor +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using System.Runtime.InteropServices; +using System.IO; +using System.Text; + +namespace Mono.Addins.Localization +{ + class GettextDomain + { + [DllImport("intl")] + static extern IntPtr bindtextdomain (IntPtr domainname, IntPtr dirname); + [DllImport("intl")] + static extern IntPtr bind_textdomain_codeset (IntPtr domainname, IntPtr codeset); + [DllImport("intl")] + static extern IntPtr dgettext (IntPtr domainname, IntPtr instring); + + IntPtr ipackage; + + public void Init (String package, string localedir) + { + if (localedir == null) { + localedir = System.Reflection.Assembly.GetEntryAssembly ().CodeBase; + FileInfo f = new FileInfo (localedir); + string prefix = f.Directory.Parent.Parent.Parent.ToString (); + prefix = Path.Combine (Path.Combine (prefix, "share"), "locale"); + } + + ipackage = StringToPtr (package); + IntPtr ilocaledir = StringToPtr (localedir); + IntPtr iutf8 = StringToPtr ("UTF-8"); + + try { + if (bindtextdomain (ipackage, ilocaledir) == IntPtr.Zero) + throw new InvalidOperationException ("Gettext localizer: bindtextdomain failed"); + if (bind_textdomain_codeset (ipackage, iutf8) == IntPtr.Zero) + throw new InvalidOperationException ("Gettext localizer: bind_textdomain_codeset failed"); + } + finally { + Marshal.FreeHGlobal (ilocaledir); + Marshal.FreeHGlobal (iutf8); + } + } + + ~GettextDomain () + { + Marshal.FreeHGlobal (ipackage); + } + + public String GetString (String s) + { + IntPtr ints = StringToPtr (s); + try { + // gettext(3) returns the input pointer if no translation is found + IntPtr r = dgettext (ipackage, ints); + if (r != ints) + return PtrToString (r); + return s; + } + finally { + Marshal.FreeHGlobal (ints); + } + } + + static IntPtr StringToPtr (string s) + { + if (s == null) + return IntPtr.Zero; + byte[] marshal = Encoding.UTF8.GetBytes (s); + IntPtr mem = Marshal.AllocHGlobal (marshal.Length + 1); + Marshal.Copy (marshal, 0, mem, marshal.Length); + Marshal.WriteByte (mem, marshal.Length, 0); + return mem; + } + + static string PtrToString (IntPtr ptr) + { + if (ptr == IntPtr.Zero) + return null; + int sz = 0; + while (Marshal.ReadByte (ptr, sz) != 0) + sz++; + byte[] bytes = new byte [sz - 1]; + Marshal.Copy (ptr, bytes, 0, sz - 1); + return Encoding.UTF8.GetString (bytes); + } + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs b/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs new file mode 100644 index 0000000..f24aaf4 --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/GettextLocalizer.cs @@ -0,0 +1,56 @@ +// GettextLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class GettextLocalizer: IAddinLocalizerFactory, IAddinLocalizer + { + GettextDomain domain; + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + string pkg = element.GetAttribute ("catalog"); + if (pkg.Length == 0) + pkg = addin.Id; + string dir = element.GetAttribute ("location"); + if (dir.Length == 0) + dir = "locale"; + dir = addin.GetFilePath (dir); + domain = new GettextDomain (); + domain.Init (pkg, dir); + return this; + } + + public string GetString (string msgid) + { + return domain.GetString (msgid); + } + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs b/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs new file mode 100644 index 0000000..eb38adc --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/IAddinLocalizer.cs @@ -0,0 +1,37 @@ +// +// IAddinLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins.Localization +{ + public interface IAddinLocalizer + { + string GetString (string msgid); + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs b/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs new file mode 100644 index 0000000..c54583f --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/IAddinLocalizerFactory.cs @@ -0,0 +1,37 @@ +// +// IAddinLocalizerFactory.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; + +namespace Mono.Addins.Localization +{ + public interface IAddinLocalizerFactory + { + IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element); + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs b/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs new file mode 100644 index 0000000..d412c8e --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/NullLocalizer.cs @@ -0,0 +1,41 @@ +// NullLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; + +namespace Mono.Addins.Localization +{ + class NullLocalizer: IAddinLocalizer + { + public static AddinLocalizer Instance = new AddinLocalizer (new NullLocalizer ()); + + public string GetString (string msgid) + { + return msgid; + } + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs b/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs new file mode 100644 index 0000000..9fe0544 --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/StringResourceLocalizer.cs @@ -0,0 +1,52 @@ +// StringResourceLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class StringResourceLocalizer: IAddinLocalizerFactory, IAddinLocalizer + { + RuntimeAddin addin; + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + this.addin = addin; + return this; + } + + public string GetString (string msgid) + { + string s = addin.GetResourceString (msgid, false, System.Threading.Thread.CurrentThread.CurrentCulture); + if (s == null) + return msgid; + else + return s; + } + } +} diff --git a/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs b/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs new file mode 100644 index 0000000..613ae51 --- /dev/null +++ b/Mono.Addins/Mono.Addins.Localization/StringTableLocalizer.cs @@ -0,0 +1,95 @@ +// StringTableLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// +// + +using System; +using System.Collections; +using Mono.Addins; + +namespace Mono.Addins.Localization +{ + class StringTableLocalizer: IAddinLocalizerFactory, IAddinLocalizer + { + Hashtable locales = new Hashtable (); + static Hashtable nullLocale = new Hashtable (); + + public IAddinLocalizer CreateLocalizer (RuntimeAddin addin, NodeElement element) + { + foreach (NodeElement nloc in element.ChildNodes) { + if (nloc.NodeName != "Locale") + throw new InvalidOperationException ("Invalid element found: '" + nloc.NodeName + "'. Expected: 'Locale'"); + string ln = nloc.GetAttribute ("id"); + if (ln.Length == 0) + throw new InvalidOperationException ("Locale id not specified"); + ln = ln.Replace ('_','-'); + Hashtable messages = new Hashtable (); + foreach (NodeElement nmsg in nloc.ChildNodes) { + if (nmsg.NodeName != "Msg") + throw new InvalidOperationException ("Locale '" + ln + "': Invalid element found: '" + nmsg.NodeName + "'. Expected: 'Msg'"); + string id = nmsg.GetAttribute ("id"); + if (id.Length == 0) + throw new InvalidOperationException ("Locale '" + ln + "': Message id not specified"); + messages [id] = nmsg.GetAttribute ("str"); + } + locales [ln] = messages; + } + return this; + } + + public string GetString (string id) + { + string cname = System.Threading.Thread.CurrentThread.CurrentCulture.Name; + Hashtable loc = (Hashtable) locales [cname]; + if (loc == null) { + string sn = cname.Substring (0, 2); + loc = (Hashtable) locales [sn]; + if (loc != null) + locales [cname] = loc; + else { + locales [cname] = nullLocale; + return id; + } + } + string msg = (string) loc [id]; + if (msg == null) { + if (cname.Length > 2) { + // Try again without the country + cname = cname.Substring (0, 2); + loc = (Hashtable) locales [cname]; + if (loc != null) { + msg = (string) loc [id]; + if (msg != null) + return msg; + } + } + return id; + } + else + return msg; + } + } +} + diff --git a/Mono.Addins/Mono.Addins.mdp b/Mono.Addins/Mono.Addins.mdp index 624829b..545554e 100644 --- a/Mono.Addins/Mono.Addins.mdp +++ b/Mono.Addins/Mono.Addins.mdp @@ -82,6 +82,14 @@ <File name="Mono.Addins.Description/ExtensionNodeTypeCollection.cs" subtype="Code" buildaction="Compile" /> <File name="Mono.Addins/InstanceExtensionNode.cs" subtype="Code" buildaction="Compile" /> <File name="Mono.Addins/IAddinInstaller.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/IAddinLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins/AddinLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/IAddinLocalizerFactory.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/NullLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/StringTableLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/StringResourceLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/GettextLocalizer.cs" subtype="Code" buildaction="Compile" /> + <File name="Mono.Addins.Localization/GettextDomain.cs" subtype="Code" buildaction="Compile" /> </Contents> <References> <ProjectReference type="Gac" localcopy="True" refto="System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> diff --git a/Mono.Addins/Mono.Addins/AddinLocalizer.cs b/Mono.Addins/Mono.Addins/AddinLocalizer.cs new file mode 100644 index 0000000..c8809a2 --- /dev/null +++ b/Mono.Addins/Mono.Addins/AddinLocalizer.cs @@ -0,0 +1,53 @@ +// +// AddinLocalizer.cs +// +// Author: +// Lluis Sanchez Gual +// +// Copyright (C) 2007 Novell, Inc (http://www.novell.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +using System; +using Mono.Addins.Localization; + +namespace Mono.Addins +{ + public class AddinLocalizer + { + IAddinLocalizer localizer; + + internal AddinLocalizer (IAddinLocalizer localizer) + { + this.localizer = localizer; + } + + public string GetString (string msgid) + { + return localizer.GetString (msgid); + } + + public string GetString (string msgid, params string[] args) + { + return string.Format (localizer.GetString (msgid), args); + } + } +} diff --git a/Mono.Addins/Mono.Addins/AddinManager.cs b/Mono.Addins/Mono.Addins/AddinManager.cs index 4f7ae4b..dca2bea 100644 --- a/Mono.Addins/Mono.Addins/AddinManager.cs +++ b/Mono.Addins/Mono.Addins/AddinManager.cs @@ -31,7 +31,7 @@ using System.IO; using System.Reflection; using System.Collections; -using Mono.Addins; +using Mono.Addins.Localization; namespace Mono.Addins { @@ -43,7 +43,8 @@ namespace Mono.Addins static string startupDirectory; static bool initialized; static IAddinInstaller installer; - + static AddinLocalizer defaultLocalizer; + public static event AddinErrorEventHandler AddinLoadError; public static event AddinEventHandler AddinLoaded; public static event AddinEventHandler AddinUnloaded; @@ -94,6 +95,14 @@ namespace Mono.Addins initialized = false; } + public static void InitializeDefaultLocalizer (IAddinLocalizer localizer) + { + if (localizer != null) + defaultLocalizer = new AddinLocalizer (localizer); + else + defaultLocalizer = null; + } + internal static string StartupDirectory { get { return startupDirectory; } } @@ -107,6 +116,15 @@ namespace Mono.Addins set { installer = value; } } + public static AddinLocalizer DefaultLocalizer { + get { + if (defaultLocalizer != null) + return defaultLocalizer; + else + return NullLocalizer.Instance; + } + } + internal static AddinSessionService SessionService { get { if (sessionService == null) @@ -250,8 +268,11 @@ namespace Mono.Addins { if (AddinLoadError != null) AddinLoadError (null, new AddinErrorEventArgs (message, addinId, exception)); - else + else { Console.WriteLine (message); + if (exception != null) + Console.WriteLine (exception); + } } internal static void ReportAddinLoad (string id) diff --git a/Mono.Addins/Mono.Addins/ExtensionNode.cs b/Mono.Addins/Mono.Addins/ExtensionNode.cs index 8e7b458..ff80e1b 100644 --- a/Mono.Addins/Mono.Addins/ExtensionNode.cs +++ b/Mono.Addins/Mono.Addins/ExtensionNode.cs @@ -200,29 +200,25 @@ namespace Mono.Addins return; NodeAttribute[] attributes = elem.Attributes; - string[] required = nodeType.RequiredFields != null ? (string[]) nodeType.RequiredFields.Clone () : null; - int nreq = required != null ? required.Length : 0; + Hashtable fields = (Hashtable) nodeType.Fields.Clone (); foreach (NodeAttribute at in attributes) { - FieldInfo f = (FieldInfo) nodeType.Fields [at.name]; + ExtensionNodeType.FieldData f = (ExtensionNodeType.FieldData) fields [at.name]; if (f == null) continue; - - if (required != null) { - int i = Array.IndexOf (required, at.name); - if (i != -1) { - required [i] = null; - nreq--; - } - } + + fields.Remove (at.name); object val; - if (f.FieldType == typeof(string)) { - val = at.value; + if (f.Field.FieldType == typeof(string)) { + if (f.Localizable) + val = Addin.Localizer.GetString (at.value); + else + val = at.value; } - else if (f.FieldType == typeof(string[])) { + else if (f.Field.FieldType == typeof(string[])) { string[] ss = at.value.Split (','); if (ss.Length == 0 && ss[0].Length == 0) val = new string [0]; @@ -232,23 +228,27 @@ namespace Mono.Addins val = ss; } } - else if (f.FieldType.IsEnum) { - val = Enum.Parse (f.FieldType, at.value); + else if (f.Field.FieldType.IsEnum) { + val = Enum.Parse (f.Field.FieldType, at.value); } else { try { - val = Convert.ChangeType (at.Value, f.FieldType); + val = Convert.ChangeType (at.Value, f.Field.FieldType); } catch (InvalidCastException) { - throw new InvalidOperationException ("Property type not supported by [NodeAttribute]: " + f.DeclaringType + "." + f.Name); + throw new InvalidOperationException ("Property type not supported by [NodeAttribute]: " + f.Field.DeclaringType + "." + f.Field.Name); } } - f.SetValue (this, val); + f.Field.SetValue (this, val); } - if (nreq > 0) { - foreach (string s in required) - if (s != null) - throw new InvalidOperationException ("Required attribute '" + s + "' not found."); + + if (fields.Count > 0) { + // Check if one of the remaining fields is mandatory + foreach (DictionaryEntry e in fields) { + ExtensionNodeType.FieldData f = (ExtensionNodeType.FieldData) e.Value; + if (f.Required) + throw new InvalidOperationException ("Required attribute '" + e.Key + "' not found."); + } } } diff --git a/Mono.Addins/Mono.Addins/ExtensionTree.cs b/Mono.Addins/Mono.Addins/ExtensionTree.cs index 7194a46..ef7cd0e 100644 --- a/Mono.Addins/Mono.Addins/ExtensionTree.cs +++ b/Mono.Addins/Mono.Addins/ExtensionTree.cs @@ -208,27 +208,27 @@ namespace Mono.Addins } Hashtable fields = new Hashtable (); - ArrayList reqFields = new ArrayList (); // Check if the type has NodeAttribute attributes applied to fields. foreach (FieldInfo field in ntype.Type.GetFields (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) { NodeAttributeAttribute at = (NodeAttributeAttribute) Attribute.GetCustomAttribute (field, typeof(NodeAttributeAttribute), true); if (at != null) { + ExtensionNodeType.FieldData fdata = new ExtensionNodeType.FieldData (); + fdata.Field = field; + fdata.Required = at.Required; + fdata.Localizable = at.Localizable; + string name; if (at.Name != null && at.Name.Length > 0) name = at.Name; else name = field.Name; - if (at.Required) reqFields.Add (name); - fields [name] = field; + fields [name] = fdata; } } - if (fields.Count > 0) { + if (fields.Count > 0) ntype.Fields = fields; - if (reqFields.Count > 0) - ntype.RequiredFields = (string[]) reqFields.ToArray (typeof(string)); - } return true; } diff --git a/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs b/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs index 7f175ba..9a5fc70 100644 --- a/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs +++ b/Mono.Addins/Mono.Addins/NodeAttributeAttribute.cs @@ -36,6 +36,7 @@ namespace Mono.Addins { string name; bool required; + bool localizable; Type type; string description; @@ -107,5 +108,10 @@ namespace Mono.Addins get { return description != null ? description : string.Empty; } set { description = value; } } + + public bool Localizable { + get { return localizable; } + set { localizable = value; } + } } } diff --git a/Mono.Addins/Mono.Addins/NodeElement.cs b/Mono.Addins/Mono.Addins/NodeElement.cs index 228205c..4599482 100644 --- a/Mono.Addins/Mono.Addins/NodeElement.cs +++ b/Mono.Addins/Mono.Addins/NodeElement.cs @@ -28,6 +28,7 @@ using System; +using System.Collections; namespace Mono.Addins { @@ -36,6 +37,7 @@ namespace Mono.Addins string NodeName { get; } string GetAttribute (string key); NodeAttribute[] Attributes { get; } + NodeElementCollection ChildNodes { get; } } public class NodeAttribute @@ -55,4 +57,9 @@ namespace Mono.Addins get { return value; } } } + + public interface NodeElementCollection: IList, ICollection, IEnumerable + { + new NodeElement this [int n] { get; } + } } diff --git a/Mono.Addins/Mono.Addins/RuntimeAddin.cs b/Mono.Addins/Mono.Addins/RuntimeAddin.cs index d815a14..dbfa219 100644 --- a/Mono.Addins/Mono.Addins/RuntimeAddin.cs +++ b/Mono.Addins/Mono.Addins/RuntimeAddin.cs @@ -33,10 +33,12 @@ using System.Collections; using System.IO; using System.Reflection; using System.Xml; -using Mono.Addins.Description; using System.Resources; using System.Globalization; +using Mono.Addins.Description; +using Mono.Addins.Localization; + namespace Mono.Addins { public class RuntimeAddin @@ -49,6 +51,7 @@ namespace Mono.Addins Assembly[] assemblies; RuntimeAddin[] depAddins; ResourceManager[] resourceManagers; + AddinLocalizer localizer; internal RuntimeAddin() { @@ -228,6 +231,15 @@ namespace Mono.Addins return null; } + public AddinLocalizer Localizer { + get { + if (localizer != null) + return localizer; + else + return AddinManager.DefaultLocalizer; + } + } + internal AddinDescription Load (Addin iad) { ainfo = iad; @@ -251,6 +263,20 @@ namespace Mono.Addins depAddins = (RuntimeAddin[]) plugList.ToArray (typeof(RuntimeAddin)); assemblies = (Assembly[]) asmList.ToArray (typeof(Assembly)); + if (description.Localizer != null) { + string cls = description.Localizer.GetAttribute ("type"); + + // First try getting one of the stock localizers. If none of found try getting the type. + object fob = CreateInstance ("Mono.Addins.Localization." + cls + "Localizer", false); + if (fob == null) + fob = CreateInstance (cls, true); + + IAddinLocalizerFactory factory = fob as IAddinLocalizerFactory; + if (factory == null) + throw new InvalidOperationException ("Localizer factory type '" + cls + "' must implement IAddinLocalizerFactory"); + localizer = new AddinLocalizer (factory.CreateLocalizer (this, description.Localizer)); + } + return description; } |