GoogleTranslation_Queries.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.Linq;
  5. using System.Text;
  6. using System.Text.RegularExpressions;
  7. namespace I2.Loc
  8. {
  9. using TranslationDictionary = Dictionary<string, TranslationQuery>;
  10. public struct TranslationQuery
  11. {
  12. public string OrigText;
  13. public string Text; // Text without Tags
  14. public string LanguageCode;
  15. public string[] TargetLanguagesCode;
  16. public string[] Results; // This is filled google returns the translations
  17. public string[] Tags; // This are the sections of the orignal text that shoudn't be translated
  18. }
  19. public static partial class GoogleTranslation
  20. {
  21. public static void CreateQueries(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
  22. {
  23. if (!text.Contains("[i2s_"))
  24. {
  25. CreateQueries_Plurals(text, LanguageCodeFrom, LanguageCodeTo, dict);
  26. return;
  27. }
  28. var variants = SpecializationManager.GetSpecializations(text);
  29. foreach (var kvp in variants)
  30. {
  31. CreateQueries_Plurals(kvp.Value, LanguageCodeFrom, LanguageCodeTo, dict);
  32. }
  33. }
  34. static void CreateQueries_Plurals(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
  35. {
  36. bool hasPluralParams = text.Contains("{[#");
  37. bool hasPluralTypes = text.Contains("[i2p_");
  38. if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes)
  39. {
  40. AddQuery(text, LanguageCodeFrom, LanguageCodeTo, dict);
  41. return;
  42. }
  43. bool forcePluralParam = hasPluralParams;
  44. for (var i = (ePluralType)0; i <= ePluralType.Plural; ++i)
  45. {
  46. var pluralType = i.ToString();
  47. if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType))
  48. continue;
  49. var newText = GetPluralText(text, pluralType);
  50. int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i);
  51. var parameter = GetPluralParameter(newText, forcePluralParam);
  52. if (!string.IsNullOrEmpty(parameter))
  53. newText = newText.Replace(parameter, testNumber.ToString());
  54. //Debug.Log("Translate: " + newText);
  55. AddQuery(newText, LanguageCodeFrom, LanguageCodeTo, dict);
  56. }
  57. }
  58. public static void AddQuery(string text, string LanguageCodeFrom, string LanguageCodeTo, TranslationDictionary dict)
  59. {
  60. if (string.IsNullOrEmpty(text))
  61. return;
  62. if (!dict.ContainsKey(text))
  63. {
  64. var query = new TranslationQuery { OrigText = text, LanguageCode = LanguageCodeFrom, TargetLanguagesCode = new[] { LanguageCodeTo } };
  65. query.Text = text;
  66. ParseNonTranslatableElements(ref query);
  67. dict[text] = query;
  68. }
  69. else
  70. {
  71. var query = dict[text];
  72. if (Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo) < 0)
  73. {
  74. query.TargetLanguagesCode = query.TargetLanguagesCode.Concat(new[] { LanguageCodeTo }).Distinct().ToArray();
  75. }
  76. dict[text] = query;
  77. }
  78. }
  79. static string GetTranslation(string text, string LanguageCodeTo, TranslationDictionary dict)
  80. {
  81. if (!dict.ContainsKey(text))
  82. return null;
  83. var query = dict[text];
  84. int langIdx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo);
  85. if (langIdx < 0)
  86. return "";
  87. if (query.Results == null)
  88. return "";
  89. return query.Results[langIdx];
  90. }
  91. static TranslationQuery FindQueryFromOrigText(string origText, TranslationDictionary dict)
  92. {
  93. foreach (var kvp in dict)
  94. {
  95. if (kvp.Value.OrigText == origText)
  96. return kvp.Value;
  97. }
  98. return default(TranslationQuery);
  99. }
  100. public static bool HasParameters( string text )
  101. {
  102. int idx = text.IndexOf("{[", StringComparison.Ordinal);
  103. if (idx < 0) return false;
  104. return text.IndexOf("]}", idx, StringComparison.Ordinal) > 0;
  105. }
  106. public static string GetPluralParameter(string text, bool forceTag) // force tag requires that the parameter has the form {[#param]}
  107. {
  108. // Try finding the "plural parameter" that has the form {[#name]}
  109. // this allows using the second parameter as plural: "Player {[name1]} has {[#value]} points"
  110. int idx0 = text.IndexOf("{[#", StringComparison.Ordinal);
  111. if (idx0 < 0)
  112. {
  113. if (forceTag) return null;
  114. idx0 = text.IndexOf("{[", StringComparison.Ordinal); // fallback to the first paremeter if no one has the # tag
  115. }
  116. if (idx0 < 0)
  117. return null;
  118. int idx1 = text.IndexOf("]}", idx0+2, StringComparison.Ordinal);
  119. if (idx1 < 0)
  120. return null; // no closing parameter tag
  121. return text.Substring(idx0, idx1 - idx0 + 2);
  122. }
  123. public static string GetPluralText( string text, string pluralType )
  124. {
  125. pluralType = "[i2p_" + pluralType + "]";
  126. int idx0 = text.IndexOf(pluralType, StringComparison.Ordinal);
  127. if (idx0>=0)
  128. {
  129. idx0 += pluralType.Length;
  130. int idx1 = text.IndexOf("[i2p_",idx0, StringComparison.Ordinal);
  131. if (idx1 < 0) idx1 = text.Length;
  132. return text.Substring(idx0, idx1 - idx0);
  133. }
  134. // PluralType not found, fallback to the first one
  135. idx0 = text.IndexOf("[i2p_", StringComparison.Ordinal);
  136. if (idx0 < 0)
  137. return text; // No plural tags: "my text"
  138. if (idx0>0)
  139. return text.Substring(0, idx0); // Case: "my text[i2p_zero]hello"
  140. // Case: "[i2p_plural]my text[i2p_zero]hello"
  141. idx0 = text.IndexOf("]", StringComparison.Ordinal);
  142. if (idx0 < 0) return text; // starts like a plural, but has none
  143. idx0 += 1;
  144. int idx2 = text.IndexOf("[i2p_", idx0, StringComparison.Ordinal);
  145. if (idx2 < 0) idx2 = text.Length;
  146. return text.Substring(idx0, idx2 - idx0);
  147. }
  148. static int FindClosingTag(string tag, MatchCollection matches, int startIndex)
  149. {
  150. for (int i = startIndex, imax = matches.Count; i < imax; ++i)
  151. {
  152. var newTag = I2Utils.GetCaptureMatch(matches[i]);
  153. if (newTag[0]=='/' && tag.StartsWith(newTag.Substring(1), StringComparison.Ordinal))
  154. return i;
  155. }
  156. return -1;
  157. }
  158. static string GetGoogleNoTranslateTag(int tagNumber)
  159. {
  160. //return " I2NT" + tagNumber;
  161. if (tagNumber < 70)
  162. return "++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++".Substring(0, tagNumber+1);
  163. string s = "";
  164. for (int i = -1; i < tagNumber; ++i)
  165. s += "+";
  166. return s;
  167. }
  168. static void ParseNonTranslatableElements( ref TranslationQuery query )
  169. {
  170. //\[i2nt].*\[\/i2nt]
  171. var matches = Regex.Matches( query.Text, @"\{\[(.*?)]}|\[(.*?)]|\<(.*?)>");
  172. if (matches == null || matches.Count == 0)
  173. return;
  174. string finalText = query.Text;
  175. List<string> finalTags = new List<string>();
  176. for (int i=0, imax=matches.Count; i<imax; ++i)
  177. {
  178. var tag = I2Utils.GetCaptureMatch( matches[i] );
  179. int iClosingTag = FindClosingTag(tag, matches, i); // find [/tag] or </tag>
  180. if (iClosingTag < 0)
  181. {
  182. // Its not a tag, its a parameter
  183. var fulltag = matches[i].ToString();
  184. if (fulltag.StartsWith("{[", StringComparison.Ordinal) && fulltag.EndsWith("]}", StringComparison.Ordinal))
  185. {
  186. finalText = finalText.Replace(fulltag, GetGoogleNoTranslateTag(finalTags.Count)+" "); // 0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google
  187. //finalText = finalText.Replace(fulltag, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString()); // 0x2600 is the start of the UNICODE Miscellaneous Symbols table, so they are not going to be translated by google
  188. finalTags.Add(fulltag);
  189. }
  190. continue;
  191. }
  192. if (tag == "i2nt")
  193. {
  194. var tag1 = query.Text.Substring(matches[i].Index, matches[iClosingTag].Index-matches[i].Index + matches[iClosingTag].Length);
  195. finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" ");
  196. //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
  197. finalTags.Add(tag1);
  198. }
  199. else
  200. {
  201. var tag1 = matches[i].ToString();
  202. finalText = finalText.Replace(tag1, GetGoogleNoTranslateTag(finalTags.Count)+" ");
  203. //finalText = finalText.Replace(tag1, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
  204. finalTags.Add(tag1);
  205. var tag2 = matches[iClosingTag].ToString();
  206. finalText = finalText.Replace(tag2, GetGoogleNoTranslateTag(finalTags.Count)+" ");
  207. //finalText = finalText.Replace(tag2, /*"{[" + finalTags.Count + "]}"*/ ((char)(0x2600 + finalTags.Count)).ToString());
  208. finalTags.Add(tag2);
  209. }
  210. }
  211. query.Text = finalText;
  212. query.Tags = finalTags.ToArray();
  213. }
  214. public static string GetQueryResult(string text, string LanguageCodeTo, TranslationDictionary dict)
  215. {
  216. if (!dict.ContainsKey(text))
  217. return null;
  218. var query = dict[text];
  219. if (query.Results == null || query.Results.Length < 0)
  220. return null;
  221. if (string.IsNullOrEmpty(LanguageCodeTo))
  222. return query.Results[0];
  223. int idx = Array.IndexOf(query.TargetLanguagesCode, LanguageCodeTo);
  224. if (idx < 0)
  225. return null;
  226. return query.Results[idx];
  227. }
  228. public static string RebuildTranslation(string text, TranslationDictionary dict, string LanguageCodeTo)
  229. {
  230. if (!text.Contains("[i2s_"))
  231. {
  232. return RebuildTranslation_Plural(text, dict, LanguageCodeTo);
  233. }
  234. var variants = SpecializationManager.GetSpecializations(text);
  235. var results = new Dictionary<string,string>(StringComparer.Ordinal);
  236. foreach (var kvp in variants)
  237. {
  238. results[kvp.Key] = RebuildTranslation_Plural(kvp.Value, dict, LanguageCodeTo);
  239. }
  240. return SpecializationManager.SetSpecializedText(results);
  241. }
  242. static string RebuildTranslation_Plural( string text, TranslationDictionary dict, string LanguageCodeTo )
  243. {
  244. bool hasPluralParams = text.Contains("{[#");
  245. bool hasPluralTypes = text.Contains("[i2p_");
  246. if (!HasParameters(text) || !hasPluralParams && !hasPluralTypes)
  247. {
  248. return GetTranslation (text, LanguageCodeTo, dict);
  249. }
  250. var sb = new StringBuilder();
  251. string pluralTranslation = null;
  252. bool forcePluralParam = hasPluralParams;
  253. for (var i = ePluralType.Plural; i >= 0; --i)
  254. {
  255. var pluralType = i.ToString();
  256. if (!GoogleLanguages.LanguageHasPluralType(LanguageCodeTo, pluralType))
  257. continue;
  258. var newText = GetPluralText(text, pluralType);
  259. int testNumber = GoogleLanguages.GetPluralTestNumber(LanguageCodeTo, i);
  260. var parameter = GetPluralParameter(newText, forcePluralParam);
  261. if (!string.IsNullOrEmpty(parameter))
  262. newText = newText.Replace(parameter, testNumber.ToString());
  263. var translation = GetTranslation(newText, LanguageCodeTo, dict);
  264. //Debug.LogFormat("from: {0} -> {1}", newText, translation);
  265. if (!string.IsNullOrEmpty(parameter))
  266. translation = translation.Replace(testNumber.ToString(), parameter);
  267. if (i==ePluralType.Plural)
  268. {
  269. pluralTranslation = translation;
  270. }
  271. else
  272. {
  273. if (translation == pluralTranslation)
  274. continue;
  275. sb.AppendFormat("[i2p_{0}]", pluralType);
  276. }
  277. sb.Append(translation);
  278. }
  279. return sb.ToString ();
  280. }
  281. public static string UppercaseFirst(string s)
  282. {
  283. if (string.IsNullOrEmpty(s))
  284. {
  285. return string.Empty;
  286. }
  287. char[] a = s.ToLower().ToCharArray();
  288. a[0] = char.ToUpper(a[0]);
  289. return new string(a);
  290. }
  291. public static string TitleCase(string s)
  292. {
  293. if (string.IsNullOrEmpty(s))
  294. {
  295. return string.Empty;
  296. }
  297. #if NETFX_CORE
  298. var sb = new StringBuilder(s);
  299. sb[0] = char.ToUpper(sb[0]);
  300. for (int i = 1, imax=s.Length; i<imax; ++i)
  301. {
  302. if (char.IsWhiteSpace(sb[i - 1]))
  303. sb[i] = char.ToUpper(sb[i]);
  304. else
  305. sb[i] = char.ToLower(sb[i]);
  306. }
  307. return sb.ToString();
  308. #else
  309. return CultureInfo.CurrentCulture.TextInfo.ToTitleCase(s);
  310. #endif
  311. }
  312. }
  313. }