source: branches/eraser6/CodeReview/Eraser.Util/Localisation.cs @ 1709

Revision 1709, 8.2 KB checked in by lowjoel, 5 years ago (diff)

Replaced the Eraser.Util.File class with extension methods (based on FileSystemInfo? for the most part) to avoid the name collision with System.IO.File; the File.GetCompactPath? function has been replaced with the FileSize? struct.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
Line 
1/*
2 * $Id$
3 * Copyright 2008-2010 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    /// <summary>
37    /// Internationalisation class. Instead of calling GetString on all strings, just
38    /// call S._(string) or S._(string, object) for plurals
39    /// </summary>
40    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "S")]
41    public static class S
42    {
43        /// <summary>
44        /// Translates the localisable string to the set localised string.
45        /// </summary>
46        /// <param name="str">The string to localise.</param>
47        /// <returns>A localised string, or str if no localisation exists.</returns>
48        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")]
49        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "_")]
50        public static string _(string text)
51        {
52            return TranslateText(text, Assembly.GetCallingAssembly());
53        }
54
55        /// <summary>
56        /// Translates the localisable text to the localised text, formatting all
57        /// placeholders using composite formatting. This is shorthand for
58        /// <code>string.Format(S._(text), args)</code>
59        /// </summary>
60        /// <param name="text">The text to localise.</param>
61        /// <param name="args">Arguments for the composite formatting call.</param>
62        /// <returns>The formatted and localised string.</returns>
63        /// <remarks>The localised string is retrieved before formatting.</remarks>
64        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")]
65        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "_")]
66        public static string _(string text, params object[] args)
67        {
68            //Get the localised version of the input string.
69            string localStr = TranslateText(text, Assembly.GetCallingAssembly());
70
71            //Format the string.
72            return string.Format(CultureInfo.CurrentCulture, localStr, args);
73        }
74
75        /// <summary>
76        /// Returns true if the given control is right-to-left reading.
77        /// </summary>
78        /// <param name="control">The control to query.</param>
79        /// <returns>True if the control is right-to-left reading.</returns>
80        public static bool IsRightToLeft(Control control)
81        {
82            if (control == null)
83                throw new ArgumentNullException("control");
84
85            switch (control.RightToLeft)
86            {
87                case RightToLeft.No:
88                    return false;
89                case RightToLeft.Yes:
90                    return true;
91                default:
92                    return IsRightToLeft(control.Parent);
93            }
94        }
95
96        /// <summary>
97        /// Translates the localisable string to the set localised string.
98        /// </summary>
99        /// <param name="text">The string to localise.</param>
100        /// <param name="assembly">The assembly from which localised resource satellite
101        /// assemblies should be loaded from.</param>
102        /// <returns>A localised string, or str if no localisation exists.</returns>
103        public static string TranslateText(string text, Assembly assembly)
104        {
105            //If the string is empty, forget it!
106            if (text.Length == 0)
107                return text;
108
109            //First get the dictionary mapping assemblies and ResourceManagers (i.e. pick out
110            //the dictionary with ResourceManagers representing the current culture.)
111            if (!managers.ContainsKey(Thread.CurrentThread.CurrentUICulture))
112                managers[Thread.CurrentThread.CurrentUICulture] =
113                    new Dictionary<Assembly, ResourceManager>();
114            Dictionary<Assembly, ResourceManager> assemblies = managers[
115                Thread.CurrentThread.CurrentUICulture];
116
117            //Then look for the ResourceManager dealing with the calling assembly's
118            //resources
119            ResourceManager res = null;
120            if (!assemblies.ContainsKey(assembly))
121            {
122                //Load the resource DLL. The resource DLL is located in the <LanguageName-RegionName>
123                //subfolder of the folder containing the main assembly
124                string languageID = string.Empty;
125                Assembly languageAssembly = LoadLanguage(Thread.CurrentThread.CurrentUICulture,
126                    assembly, out languageID);
127
128                //If we found the language assembly to load, then we load it directly, otherwise
129                //fall back to the invariant culture.
130                string resourceName = Path.GetFileNameWithoutExtension(assembly.Location) +
131                    ".Strings" + (languageID.Length != 0 ? ("." + languageID) : "");
132                res = new ResourceManager(resourceName,
133                    languageAssembly != null ? languageAssembly : assembly);
134                assemblies[assembly] = res;
135            }
136            else
137                res = assemblies[assembly];
138
139            string result = res.GetString(Escape(text), Thread.CurrentThread.CurrentUICulture);
140#if DEBUG
141            return string.IsNullOrEmpty(result) ? text : Unescape(result);
142#else
143            return string.IsNullOrEmpty(result) || result == "(Untranslated)" ? text : Unescape(result);
144#endif
145        }
146
147        /// <summary>
148        /// Gets whether the provided translation exists for the provided assembly.
149        /// </summary>
150        /// <param name="culture">The exact language to check for.</param>
151        /// <param name="assembly">The assembly to check for the presence of a localisation.</param>
152        /// <returns>True if the resource assembly for the given culture and assembly exists.</returns>
153        public static bool LocalisationExists(CultureInfo culture, Assembly assembly)
154        {
155            return File.Exists(Path.Combine(
156                Path.Combine(Path.GetDirectoryName(assembly.Location), culture.Name), //Directory
157                Path.GetFileNameWithoutExtension(assembly.Location) + ".resources.dll"));
158        }
159
160        /// <summary>
161        /// Replaces non-printable codes used in the string to translate into translatable placeholders.
162        /// </summary>
163        /// <param name="str">The string to escape</param>
164        /// <returns>An escaped string</returns>
165        private static string Escape(string str)
166        {
167            return str.Replace("\n", "\\n").Replace("\r", "\\r");
168        }
169
170        /// <summary>
171        /// Replaces all escape codes used in the translated string into real character codes.
172        /// </summary>
173        /// <param name="str">The string to unescape</param>
174        /// <returns>An unescaped string</returns>
175        private static string Unescape(string str)
176        {
177            return str.Replace("\\n", "\n").Replace("\\r", "\r");
178        }
179
180        /// <summary>
181        /// Looks in the folder denoted by <paramref name="path"/> for the resource providing
182        /// resources for <paramref name="culture"/>. The name of the resource DLL will be the
183        /// culture name &gt;languagecode2-country/regioncode2&lt;.
184        /// </summary>
185        /// <param name="culture">The culture to load.</param>
186        /// <param name="assembly">The assembly to look for localised resources for.</param>
187        /// <returns>An assembly containing the required resources, or null.</returns>
188        private static Assembly LoadLanguage(CultureInfo culture, Assembly assembly,
189            out string languageID)
190        {
191            languageID = string.Empty;
192            string path = string.Empty;
193            while (culture != CultureInfo.InvariantCulture)
194            {
195                path = Path.Combine(Path.GetDirectoryName(assembly.Location), culture.Name);
196                if (Directory.Exists(path))
197                {
198                    string assemblyPath = Path.Combine(path,
199                        Path.GetFileNameWithoutExtension(assembly.Location) + ".resources.dll");
200                    if (File.Exists(assemblyPath))
201                    {
202                        languageID = culture.Name;
203                        return Assembly.LoadFrom(assemblyPath);
204                    }
205                }
206                culture = culture.Parent;
207            }
208
209            return null;
210        }
211
212        private static Dictionary<CultureInfo, Dictionary<Assembly, ResourceManager>> managers =
213            new Dictionary<CultureInfo, Dictionary<Assembly, ResourceManager>>();
214    }
215}
Note: See TracBrowser for help on using the repository browser.