source: trunk/eraser/Eraser.Util/Localisation.cs @ 2960

Revision 2958, 9.2 KB checked in by gtrant, 5 weeks ago (diff)

Updated Copyright to 2014
Updated versions to 6.2
Fix for date stamp on erased artefacts

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Rev URL
Line 
1/*
2 * $Id$
3 * Copyright 2008-2014 The Eraser Project
4 * Original Author: Joel Low <lowjoel@users.sourceforge.net>
5 * Modified By:
6 *
7 * This file is part of Eraser.
8 *
9 * Eraser is free software: you can redistribute it and/or modify it under the
10 * terms of the GNU General Public License as published by the Free Software
11 * Foundation, either version 3 of the License, or (at your option) any later
12 * version.
13 *
14 * Eraser is distributed in the hope that it will be useful, but WITHOUT ANY
15 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
16 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
17 *
18 * A copy of the GNU General Public License can be found at
19 * <http://www.gnu.org/licenses/>.
20 */
21
22using System;
23using System.Collections.Generic;
24using System.Text;
25
26using System.IO;
27using System.Reflection;
28using System.Globalization;
29using System.Windows.Forms;
30using System.Diagnostics;
31using System.Resources;
32using System.Threading;
33
34namespace Eraser.Util
35{
36    public static class Localisation
37    {
38        /// <summary>
39        /// Returns true if the given control is right-to-left reading.
40        /// </summary>
41        /// <param name="control">The control to query.</param>
42        /// <returns>True if the control is right-to-left reading.</returns>
43        public static bool IsRightToLeft(Control control)
44        {
45            while (control != null)
46            {
47                switch (control.RightToLeft)
48                {
49                    case RightToLeft.No:
50                        return false;
51                    case RightToLeft.Yes:
52                        return true;
53                    default:
54                        control = control.Parent;
55                        break;
56                }
57            }
58
59            if (Application.OpenForms.Count > 0)
60            {
61                return IsRightToLeft(Application.OpenForms[0]);
62            }
63            else
64            {
65                using (Form form = new Form())
66                    return IsRightToLeft(form);
67            }
68        }
69
70        /// <summary>
71        /// Translates the localisable string to the set localised string.
72        /// </summary>
73        /// <param name="text">The string to localise.</param>
74        /// <param name="assembly">The assembly from which localised resource satellite
75        /// assemblies should be loaded from.</param>
76        /// <returns>A localised string, or str if no localisation exists.</returns>
77        public static string TranslateText(string text, Assembly assembly)
78        {
79            //If the string is empty, forget it!
80            if (text.Length == 0)
81                return text;
82
83            //First get the dictionary mapping assemblies and ResourceManagers (i.e. pick out
84            //the dictionary with ResourceManagers representing the current culture.)
85            if (!managers.ContainsKey(Thread.CurrentThread.CurrentUICulture))
86                managers[Thread.CurrentThread.CurrentUICulture] =
87                    new Dictionary<Assembly, ResourceManager>();
88            Dictionary<Assembly, ResourceManager> assemblies = managers[
89                Thread.CurrentThread.CurrentUICulture];
90
91            //Then look for the ResourceManager dealing with the calling assembly's
92            //resources
93            ResourceManager res = null;
94            if (!assemblies.ContainsKey(assembly))
95            {
96                //Load the resource DLL. The resource DLL is located in the <LanguageName-RegionName>
97                //subfolder of the folder containing the main assembly
98                string languageID = string.Empty;
99                Assembly languageAssembly = LoadLanguage(Thread.CurrentThread.CurrentUICulture,
100                    assembly, out languageID);
101
102                //If we found the language assembly to load, then we load it directly, otherwise
103                //fall back to the invariant culture.
104                string resourceName = Path.GetFileNameWithoutExtension(assembly.Location) +
105                    ".Strings" + (languageID.Length != 0 ? ("." + languageID) : "");
106                res = new ResourceManager(resourceName,
107                    languageAssembly != null ? languageAssembly : assembly);
108                assemblies[assembly] = res;
109            }
110            else
111                res = assemblies[assembly];
112
113            string result = null;
114            try
115            {
116                result = res.GetString(Escape(text), Thread.CurrentThread.CurrentUICulture);
117            }
118            catch (MissingManifestResourceException)
119            {
120            }
121#if DEBUG
122            return string.IsNullOrEmpty(result) ? text : Unescape(result);
123#else
124            return string.IsNullOrEmpty(result) || result == "(Untranslated)" ? text : Unescape(result);
125#endif
126        }
127
128        /// <summary>
129        /// Gets whether the provided translation exists for the provided assembly.
130        /// </summary>
131        /// <param name="culture">The exact language to check for.</param>
132        /// <param name="assembly">The assembly to check for the presence of a localisation.</param>
133        /// <returns>True if the resource assembly for the given culture and assembly exists.</returns>
134        public static bool LocalisationExists(CultureInfo culture, Assembly assembly)
135        {
136            return File.Exists(Path.Combine(
137                Path.Combine(Path.GetDirectoryName(assembly.Location), culture.Name), //Directory
138                Path.GetFileNameWithoutExtension(assembly.Location) + ".resources.dll"));
139        }
140
141        /// <summary>
142        /// Retrieves all present language plugins
143        /// </summary>
144        /// <returns>A list, with an instance of each Language class</returns>
145        public static IList<CultureInfo> Localisations
146        {
147            get
148            {
149                List<CultureInfo> result = new List<CultureInfo>();
150                Assembly assembly = Assembly.GetEntryAssembly();
151                foreach (CultureInfo info in CultureInfo.GetCultures(CultureTypes.AllCultures))
152                {
153                    if (string.IsNullOrEmpty(info.Name))
154                        continue;
155                    else if (LocalisationExists(info, assembly))
156                        result.Add(info);
157                }
158
159                //Last resort
160                if (result.Count == 0)
161                    result.Add(CultureInfo.GetCultureInfo("EN"));
162                return result.AsReadOnly();
163            }
164        }
165
166        /// <summary>
167        /// Replaces non-printable codes used in the string to translate into translatable placeholders.
168        /// </summary>
169        /// <param name="str">The string to escape</param>
170        /// <returns>An escaped string</returns>
171        private static string Escape(string str)
172        {
173            return str.Replace("\n", "\\n").Replace("\r", "\\r");
174        }
175
176        /// <summary>
177        /// Replaces all escape codes used in the translated string into real character codes.
178        /// </summary>
179        /// <param name="str">The string to unescape</param>
180        /// <returns>An unescaped string</returns>
181        private static string Unescape(string str)
182        {
183            return str.Replace("\\n", "\n").Replace("\\r", "\r");
184        }
185
186        /// <summary>
187        /// Looks in the folder denoted by <paramref name="path"/> for the resource providing
188        /// resources for <paramref name="culture"/>. The name of the resource DLL will be the
189        /// culture name &gt;languagecode2-country/regioncode2&lt;.
190        /// </summary>
191        /// <param name="culture">The culture to load.</param>
192        /// <param name="assembly">The assembly to look for localised resources for.</param>
193        /// <returns>An assembly containing the required resources, or null.</returns>
194        private static Assembly LoadLanguage(CultureInfo culture, Assembly assembly,
195            out string languageID)
196        {
197            languageID = string.Empty;
198            string path = string.Empty;
199            while (culture != CultureInfo.InvariantCulture)
200            {
201                path = Path.Combine(Path.GetDirectoryName(assembly.Location), culture.Name);
202                if (Directory.Exists(path))
203                {
204                    string assemblyPath = Path.Combine(path,
205                        Path.GetFileNameWithoutExtension(assembly.Location) + ".resources.dll");
206                    if (File.Exists(assemblyPath))
207                    {
208                        languageID = culture.Name;
209                        return Assembly.LoadFrom(assemblyPath);
210                    }
211                }
212                culture = culture.Parent;
213            }
214
215            return null;
216        }
217
218        private static Dictionary<CultureInfo, Dictionary<Assembly, ResourceManager>> managers =
219            new Dictionary<CultureInfo, Dictionary<Assembly, ResourceManager>>();
220    }
221
222    /// <summary>
223    /// Internationalisation class. Instead of calling GetString on all strings, just
224    /// call S._(string) or S._(string, object) for plurals
225    /// </summary>
226    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "S")]
227    public static class S
228    {
229        /// <summary>
230        /// Translates the localisable string to the set localised string.
231        /// </summary>
232        /// <param name="str">The string to localise.</param>
233        /// <returns>A localised string, or str if no localisation exists.</returns>
234        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")]
235        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "_")]
236        public static string _(string text)
237        {
238            return Localisation.TranslateText(text, Assembly.GetCallingAssembly());
239        }
240
241        /// <summary>
242        /// Translates the localisable text to the localised text, formatting all
243        /// placeholders using composite formatting. This is shorthand for
244        /// <code>string.Format(S._(text), args)</code>
245        /// </summary>
246        /// <param name="text">The text to localise.</param>
247        /// <param name="args">Arguments for the composite formatting call.</param>
248        /// <returns>The formatted and localised string.</returns>
249        /// <remarks>The localised string is retrieved before formatting.</remarks>
250        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")]
251        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "_")]
252        public static string _(string text, params object[] args)
253        {
254            //Get the localised version of the input string.
255            string localStr = Localisation.TranslateText(text, Assembly.GetCallingAssembly());
256
257            //Format the string.
258            return string.Format(CultureInfo.CurrentCulture, localStr, args);
259        }
260    }
261}
Note: See TracBrowser for help on using the repository browser.