public bool Process(string[] allFiles, string outputFile, out string error) { error = null; // 0) set up the LauretteTree, which will hold the hierarchy of our key-value pairs LauretteTree tree = new LauretteTree(); // 1) run through all files and process each one foreach (string file in allFiles) { if (!ProcessFile(file, tree, out error)) { return(false); } } // Generate the source code StringBuilder sb = new StringBuilder(); AppendTabFormat(sb, 0, "// Autogenerated by Laurette ( https://github.com/KittyMac/laurette )\n\n"); AppendTabFormat(sb, 0, "using System.Collections.Generic;\n\n"); if (tree.AllLanguageCodes.Count == 0) { // where there no .strings files? AppendTabFormat(sb, 0, "// Laurette was not provided with any valid .strings files; please ensure your\n"); AppendTabFormat(sb, 0, "// project contains .strings files in the correct format and try again\n\n\n"); AppendTabFormat(sb, 0, "public struct Localizations {{\n"); AppendTabFormat(sb, 1, "public static string GetLanguageCode() {{ return null; }}\n\n"); AppendTabFormat(sb, 1, "public static string TranslateKey(string key) {{\n"); AppendTabFormat(sb, 2, "return null;\n"); AppendTabFormat(sb, 1, "}}\n"); AppendTabFormat(sb, 0, "}}\n"); File.WriteAllText(outputFile, sb.ToString()); return(true); } tree.Traverse((branch, isStart, isLeaf, depth) => { if (isLeaf) { bool isFormatString = false; int numArguments = 0; foreach (string languageCode in tree.AllLanguageCodes) { string value = branch.Translate(languageCode); if (value.Contains("{0}")) { isFormatString = true; while (value.Contains("{" + numArguments + "}")) { numArguments++; } break; } } if (isFormatString) { StringBuilder formatString = new StringBuilder(); StringBuilder argsString = new StringBuilder(); for (int i = 0; i < numArguments; i++) { formatString.Append(string.Format("object arg{0}, ", i)); argsString.Append(string.Format("arg{0}, ", i)); } formatString.Length = formatString.Length - 2; argsString.Length = argsString.Length - 2; AppendTabFormat(sb, depth, "/// <summary>English: \"{0}\"</summary>\n", branch.Translate("en").Replace("\n", "\\n").Replace("\r", "\\r")); AppendTabFormat(sb, depth, "public static string {0}({1}) {{\n", branch.Name, formatString.ToString()); depth++; AppendTabFormat(sb, depth, "return string.Format({0}[(int)currentLanguage], {1});\n", branch.Path, argsString.ToString()); depth--; AppendTabFormat(sb, depth, "}}\n"); } else { AppendTabFormat(sb, depth, "/// <summary>English: \"{0}\"</summary>\n", branch.Translate("en").Replace("\n", "\\n").Replace("\r", "\\r")); AppendTabFormat(sb, depth, "public static string {0} {{\n", branch.Name); depth++; AppendTabFormat(sb, depth, "get {{\n"); depth++; AppendTabFormat(sb, depth, "return {0}[(int)currentLanguage];\n", branch.Path); depth--; AppendTabFormat(sb, depth, "}}\n"); depth--; AppendTabFormat(sb, depth, "}}\n"); } } else { if (isStart) { AppendTabFormat(sb, depth, "public struct {0} {{\n", branch.Name); // Create the LanguageCode enum if (depth == 0) { depth++; AppendTabFormat(sb, depth, "public enum LanguageCode {{\n"); depth++; foreach (string languageCode in tree.AllLanguageCodes) { AppendTabFormat(sb, depth, "{0},\n", languageCode); } depth--; AppendTabFormat(sb, depth, "}}\n\n"); AppendTabFormat(sb, depth, "private static LanguageCode currentLanguage = LanguageCode.{0};\n\n", tree.AllLanguageCodes[0]); AppendTabFormat(sb, depth, "public static string GetLanguageCode() {{ return currentLanguage.ToString(); }}\n\n"); AppendTabFormat(sb, depth, "public static void SetLanguageCode(LanguageCode code) {{ currentLanguage = code; }}\n\n"); AppendTabFormat(sb, depth, "public static void SetLanguageCode(string codeAsString) {{ currentLanguage = (LanguageCode)System.Enum.Parse(typeof(LanguageCode), codeAsString); }}\n\n"); depth--; } else { AppendTabFormat(sb, depth + 1, "/// <summary>{0}</summary>\n", branch.KeyPath); AppendTabFormat(sb, depth + 1, "public static string KeyPath {{ get {{ return \"{0}\"; }} }}\n", branch.KeyPath); } } else { AppendTabFormat(sb, depth, "}}\n"); } } }); sb.Length = sb.Length - 2; sb.AppendFormat("\n\n\n"); AppendTabFormat(sb, 1, "private static Dictionary<string,string[]> keyLUT = new Dictionary<string,string[]>();\n\n"); AppendTabFormat(sb, 1, "public static string TranslateKey(string key) {{\n"); AppendTabFormat(sb, 2, "if(keyLUT.Count == 0) {{\n"); tree.Traverse((branch3, isStart3, isLeaf3, depth3) => { if (isLeaf3) { AppendTabFormat(sb, 3, "keyLUT[\"{0}\"] = {1};\n", branch3.OriginalKey, branch3.Path); } }); AppendTabFormat(sb, 2, "}}\n"); AppendTabFormat(sb, 2, "if(keyLUT.ContainsKey(key) == false) {{ return null; }};\n"); AppendTabFormat(sb, 2, "return keyLUT[key][(int)currentLanguage];\n"); AppendTabFormat(sb, 1, "}}\n"); tree.Traverse((branch2, isStart2, isLeaf2, depth2) => { if (isLeaf2) { AppendTabFormat(sb, 1, "static readonly string[] {0} = new string[] {{\n", branch2.Path); foreach (string languageCode in tree.AllLanguageCodes) { string value = branch2.Translate(languageCode).Replace("\n", "\\n").Replace("\r", "\\r"); AppendTabFormat(sb, 2, "\"{0}\",\n", value); } AppendTabFormat(sb, 1, "}};\n"); } }); sb.AppendFormat("}}"); File.WriteAllText(outputFile, sb.ToString()); return(true); }
private bool ProcessFile(string filePath, LauretteTree tree, out string error) { error = null; // 0) Read in the .strings file string stringsFileAsString = File.ReadAllText(filePath); if (stringsFileAsString == null) { error = string.Format("Unable to read file: {0}", filePath); return(false); } // extract the two-character language code from the path; it should be the directory the .strings file it in string languageCode = Path.GetFileName(Path.GetDirectoryName(filePath)); // 1) Process the strings file, pulling out all of the key-value pairs // old, doesn't handle escaped quotes: "\"([^\"]+)\"\\s*=\\s*\"([^\"]+)\"" // new, handles escaped quotes: "([^"]+)"\s*=\s((?<![\\])['"])((?:.(?!(?<![\\])\2))*.?)\2; MatchCollection matches = Regex.Matches(stringsFileAsString, "\"([^\"]+)\"\\s*=\\s((?<![\\\\])['\"])((?:.(?!(?<![\\\\])\\2))*.?)\\2;", RegexOptions.Singleline); foreach (Match match in matches) { string value = match.Groups [3].Value; string key = match.Groups [1].Value; string originalKey = key; // handle special cases for the value before processing: key = key.Trim(".".ToCharArray()); while (key.Contains(" ")) { key = key.Replace(" ", "_"); } while (key.Contains("..")) { key = key.Replace("..", "."); } for (char i = (char)0; i < (char)128; i++) { if (Char.IsLetter(i) == false && i != '.') { key = key.Replace("" + i, "_"); } } string[] parts = key.Split(".".ToCharArray()); for (int i = 0; i < parts.Length; i++) { string part = parts [i]; if (Char.IsLetter(part, 0) == false) { part = "_" + part; } if (Array.IndexOf(invalidKeywords, part) >= 0) { part = "_" + part; } parts [i] = part; } key = string.Join(".", parts); tree.Add(originalKey, key, value, languageCode); } return(true); }