Localizer.cs
1 //
2 // Copyright (c) Microsoft. All rights reserved.
3 // Licensed under the MIT license.
4 //
5 // Microsoft Bot Framework: http://botframework.com
6 //
7 // Bot Builder SDK GitHub:
8 // https://github.com/Microsoft/BotBuilder
9 //
10 // Copyright (c) Microsoft Corporation
11 // All rights reserved.
12 //
13 // MIT License:
14 // Permission is hereby granted, free of charge, to any person obtaining
15 // a copy of this software and associated documentation files (the
16 // "Software"), to deal in the Software without restriction, including
17 // without limitation the rights to use, copy, modify, merge, publish,
18 // distribute, sublicense, and/or sell copies of the Software, and to
19 // permit persons to whom the Software is furnished to do so, subject to
20 // the following conditions:
21 //
22 // The above copyright notice and this permission notice shall be
23 // included in all copies or substantial portions of the Software.
24 //
25 // THE SOFTWARE IS PROVIDED ""AS IS"", WITHOUT WARRANTY OF ANY KIND,
26 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
27 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
28 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
29 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
30 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
31 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 //
33 
34 using System;
35 using System.Collections;
36 using System.Collections.Generic;
37 using System.Globalization;
38 using System.Linq;
39 using System.Resources;
41 
42 namespace Microsoft.Bot.Builder.FormFlow.Advanced
43 {
44  #region Documentation
45  #endregion
47  public class Localizer : ILocalizer
48  {
49  public CultureInfo Culture { get; set; }
50 
51  public void Add(string key, string translation)
52  {
53  if (translation != null)
54  {
55  _translations.Add(key, translation);
56  }
57  }
58 
59  public void Add(string key, IEnumerable<string> list)
60  {
61  if (list.Any())
62  {
63  _arrayTranslations.Add(key, list.ToArray());
64  }
65  }
66 
67  public void Add(string prefix, IReadOnlyDictionary<object, DescribeAttribute> dictionary)
68  {
69  foreach (var entry in dictionary)
70  {
71  if (entry.Value.IsLocalizable)
72  {
73  if (entry.Key.GetType().IsEnum)
74  {
75  var key = entry.Key.GetType().Name + "." + entry.Key;
76  if (!_translations.ContainsKey(key))
77  {
78  Add(key, entry.Value.Description);
79  Add(key, entry.Value.Image);
80  Add(key + nameof(entry.Value.Title), entry.Value.Title);
81  Add(key + nameof(entry.Value.SubTitle), entry.Value.SubTitle);
82  Add(key + nameof(entry.Value.Message), entry.Value.Message);
83  }
84  }
85  else
86  {
87  Add(prefix + SEPARATOR + entry.Key, entry.Value.Description);
88  }
89  }
90  }
91  }
92 
93  public void Add(string prefix, IReadOnlyDictionary<object, TermsAttribute> dictionary)
94  {
95  foreach (var entry in dictionary)
96  {
97  if (entry.Value.IsLocalizable)
98  {
99  if (entry.Key.GetType().IsEnum)
100  {
101  var key = entry.Key.GetType().Name + "." + entry.Key;
102  if (!_arrayTranslations.ContainsKey(key))
103  {
104  _arrayTranslations.Add(key, entry.Value.Alternatives);
105  }
106  }
107  else
108  {
109  _arrayTranslations.Add(prefix + SEPARATOR + entry.Key, entry.Value.Alternatives);
110  }
111  }
112  }
113  }
114 
115  public void Add(string prefix, IReadOnlyDictionary<TemplateUsage, TemplateAttribute> templates)
116  {
117  foreach (var template in templates.Values)
118  {
119  Add(prefix, template);
120  }
121  }
122 
123  public void Add(string prefix, TemplateAttribute template)
124  {
125  if (template.IsLocalizable)
126  {
127  _templateTranslations.Add(MakeList(prefix, template.Usage.ToString()), template.Patterns);
128  }
129  }
130 
131  public bool Lookup(string key, out string value)
132  {
133  return _translations.TryGetValue(key, out value);
134  }
135 
136  public bool LookupValues(string key, out string[] values)
137  {
138  return _arrayTranslations.TryGetValue(key, out values);
139  }
140 
141  public void LookupDictionary(string prefix, IDictionary<object, DescribeAttribute> dictionary)
142  {
143  foreach (var entry in dictionary)
144  {
145  var key = entry.Key;
146  var desc = entry.Value;
147  string skey;
148  if (key.GetType().IsEnum)
149  {
150  skey = key.GetType().Name + "." + key;
151  }
152  else
153  {
154  skey = prefix + SEPARATOR + key;
155  }
156  string value;
157  if (_translations.TryGetValue(skey, out value))
158  {
159  desc.Description = value;
160  }
161  if (_translations.TryGetValue(skey + nameof(desc.Image), out value))
162  {
163  desc.Image = value;
164  }
165  if (_translations.TryGetValue(skey + nameof(desc.Title), out value))
166  {
167  desc.Title = value;
168  }
169  if (_translations.TryGetValue(skey + nameof(desc.SubTitle), out value))
170  {
171  desc.SubTitle = value;
172  }
173  if (_translations.TryGetValue(skey + nameof(desc.Message), out value))
174  {
175  desc.Message = value;
176  }
177  }
178  }
179 
180  public void LookupDictionary(string prefix, IDictionary<object, TermsAttribute> dictionary)
181  {
182  foreach (var key in dictionary.Keys.ToArray())
183  {
184  string skey;
185  if (key.GetType().IsEnum)
186  {
187  skey = key.GetType().Name + "." + key;
188  }
189  else
190  {
191  skey = prefix + SEPARATOR + key;
192  }
193  string[] values;
194  if (_arrayTranslations.TryGetValue(skey, out values))
195  {
196  dictionary[key] = new TermsAttribute(values);
197  }
198  }
199  }
200 
201  public void LookupTemplates(string prefix, IDictionary<TemplateUsage, TemplateAttribute> templates)
202  {
203  foreach (var template in templates.Values)
204  {
205  string[] patterns;
206  if (_templateTranslations.TryGetValue(prefix + SEPARATOR + template.Usage, out patterns))
207  {
208  template.Patterns = patterns;
209  }
210  }
211  }
212 
213  public void Remove(string key)
214  {
215  _translations.Remove(key);
216  _arrayTranslations.Remove(key);
217  _templateTranslations.Remove(key);
218  }
219 
220  public ILocalizer Load(IDictionaryEnumerator reader, out IEnumerable<string> missing, out IEnumerable<string> extra)
221  {
222  var lmissing = new List<string>();
223  var lextra = new List<string>();
224  var newLocalizer = new Localizer();
225  while (reader.MoveNext())
226  {
227  var entry = (DictionaryEntry)reader.Current;
228  var fullKey = (string)entry.Key;
229  var semi = fullKey.LastIndexOf(SEPARATOR[0]);
230  var key = fullKey.Substring(0, semi);
231  var type = fullKey.Substring(semi + 1);
232  var val = (string)entry.Value;
233  if (type == "VALUE")
234  {
235  newLocalizer.Add(key, val);
236  }
237  else if (type == "LIST")
238  {
239  newLocalizer.Add(key, val.SplitList().ToArray());
240  }
241  else if (type == "TEMPLATE")
242  {
243  var elements = key.SplitList();
244  var usage = elements.First();
245  var fields = elements.Skip(1);
246  var patterns = val.SplitList();
247  var template = new TemplateAttribute((TemplateUsage)Enum.Parse(typeof(TemplateUsage), usage), patterns.ToArray());
248  foreach (var field in fields)
249  {
250  newLocalizer.Add(field, template);
251  }
252  }
253  }
254 
255  // Find missing and extra keys
256  lmissing.AddRange(_translations.Keys.Except(newLocalizer._translations.Keys));
257  lmissing.AddRange(_arrayTranslations.Keys.Except(newLocalizer._arrayTranslations.Keys));
258  lmissing.AddRange(_templateTranslations.Keys.Except(newLocalizer._templateTranslations.Keys));
259  lextra.AddRange(newLocalizer._translations.Keys.Except(_translations.Keys));
260  lextra.AddRange(newLocalizer._arrayTranslations.Keys.Except(_arrayTranslations.Keys));
261  lextra.AddRange(newLocalizer._templateTranslations.Keys.Except(_templateTranslations.Keys));
262  missing = lmissing;
263  extra = lextra;
264  return newLocalizer;
265  }
266 
267  public void Save(IResourceWriter writer)
268  {
269  foreach (var entry in _translations)
270  {
271  writer.AddResource(entry.Key + SEPARATOR + "VALUE", entry.Value);
272  }
273 
274  foreach (var entry in _arrayTranslations)
275  {
276  writer.AddResource(entry.Key + SEPARATOR + "LIST", MakeList(entry.Value));
277  }
278 
279  // Switch from field;usage -> patterns
280  // to usage;pattern* -> [fields]
281  var byPattern = new Dictionary<string, List<string>>();
282  foreach (var entry in _templateTranslations)
283  {
284  var names = entry.Key.SplitList().ToArray();
285  var field = names[0];
286  var usage = names[1];
287  var key = MakeList(AddPrefix(usage, entry.Value));
288  List<string> fields;
289  if (byPattern.TryGetValue(key, out fields))
290  {
291  fields.Add(field);
292  }
293  else
294  {
295  byPattern.Add(key, new List<string> { field });
296  }
297  }
298 
299  // Write out TEMPLATE;usage;field* -> pattern*
300  foreach (var entry in byPattern)
301  {
302  var elements = entry.Key.SplitList().ToArray();
303  var usage = elements[0];
304  var patterns = elements.Skip(1);
305  var key = usage + SEPARATOR + MakeList(entry.Value) + SEPARATOR + "TEMPLATE";
306  writer.AddResource(key, MakeList(patterns));
307  }
308  }
309 
310  protected IEnumerable<string> AddPrefix(string prefix, IEnumerable<string> suffix)
311  {
312  return new string[] { prefix }.Union(suffix);
313  }
314 
315  protected Dictionary<string, string> _translations = new Dictionary<string, string>();
316  protected Dictionary<string, string[]> _arrayTranslations = new Dictionary<string, string[]>();
317  protected Dictionary<string, string[]> _templateTranslations = new Dictionary<string, string[]>();
318  }
319 }
void Add(string prefix, IReadOnlyDictionary< object, DescribeAttribute > dictionary)
Adds value from dictionary under object if enumeration and prefix;object otherwise.
Definition: Localizer.cs:67
bool Lookup(string key, out string value)
Translate a key to a translation.
Definition: Localizer.cs:131
Define a template for generating strings.
Definition: Attributes.cs:571
ILocalizer Load(IDictionaryEnumerator reader, out IEnumerable< string > missing, out IEnumerable< string > extra)
Load the localizer from a stream.
Definition: Localizer.cs:220
void Save(IResourceWriter writer)
Save localizer resources to stream.
Definition: Localizer.cs:267
TemplateUsage
All of the built-in templates.
Definition: Attributes.cs:321
Attribute to override the default terms used to match a field, property or enum value to user input...
Definition: Attributes.cs:119
void Remove(string key)
Remove a key from the localizer.
Definition: Localizer.cs:213
void Add(string prefix, IReadOnlyDictionary< TemplateUsage, TemplateAttribute > templates)
Adds patterns from template separated by semi-colons under prefix;usage.
Definition: Localizer.cs:115
bool LookupValues(string key, out string[] values)
Translate a key to an array of values.
Definition: Localizer.cs:136
Namespace for resources.
void Add(string key, string translation)
Add a key and its translation.
Definition: Localizer.cs:51
void LookupDictionary(string prefix, IDictionary< object, DescribeAttribute > dictionary)
Look up prefix;object from dictionary and replace value from localizer.
Definition: Localizer.cs:141
IEnumerable< string > AddPrefix(string prefix, IEnumerable< string > suffix)
Definition: Localizer.cs:310
void Add(string prefix, TemplateAttribute template)
Adds patterns from template separated by semi-colons under prefix;usage.
Definition: Localizer.cs:123
Interface for localizing string resources.
Definition: ILocalizer.cs:44
void Add(string prefix, IReadOnlyDictionary< object, TermsAttribute > dictionary)
Adds values from dictionary separated by semi-colons under object if enumeration and prefix;object ot...
Definition: Localizer.cs:93
void LookupTemplates(string prefix, IDictionary< TemplateUsage, TemplateAttribute > templates)
Looks up prefix;usage and replace patterns in template from localizer.
Definition: Localizer.cs:201
void Add(string key, IEnumerable< string > list)
Add a key and a list of translations separated by semi-colon.
Definition: Localizer.cs:59
Root namespace for the Microsoft Bot Builder SDK.
void LookupDictionary(string prefix, IDictionary< object, TermsAttribute > dictionary)
Look up prefix;object from dictionary and replace values from localizer.
Definition: Localizer.cs:180