#region Disclaimer / License // Copyright (C) 2015, The Duplicati Team // http://www.duplicati.com, info@duplicati.com // // This library is free software; you can redistribute it and/or // modify it under the terms of the GNU Lesser General Public // License as published by the Free Software Foundation; either // version 2.1 of the License, or (at your option) any later version. // // This library is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // Lesser General Public License for more details. // // You should have received a copy of the GNU Lesser General Public // License along with this library; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA // #endregion using System; using System.Windows.Forms; using System.Collections.Generic; using System.Linq; using System.Xml.Linq; using System.IO; using System.Net; namespace LocalizationTool { static class Program { /// /// The main entry point for the application. /// [STAThread] static void Main(string[] args) { if (args.Length == 0) { PrintUsage(); return; } //All relative paths are to the app dir System.IO.Directory.SetCurrentDirectory(Application.StartupPath); string loc = args.Length >= 2 ? args[1] : null; switch (args[0].ToLower()) { case "clean": case "cleanup": Clean(loc); break; case "build": Compile(loc); break; case "update": Update(loc); break; case "create": if (args.Length != 2) { Console.WriteLine("missing locale identifier"); PrintUsage(); return; } Create(loc); break; case "report": Report(loc); break; case "export": Export(loc); break; case "exportdiff": if (args.Length != 3) { Console.WriteLine("missing locale identifier or input CSV file"); PrintUsage(); return; } ExportDiff(loc, args[2]); break; case "import": if (args.Length != 3) { Console.WriteLine("missing locale identifier or input CSV file"); PrintUsage(); return; } Import(loc, args[2]); break; case "guiupdate": Application.EnableVisualStyles(); Application.DoEvents(); Report(loc); new UpdateGUI(System.IO.Path.Combine(Application.StartupPath, "report." + loc + ".xml")).ShowDialog(); break; case "webpull": WebPull(loc, false); break; case "webupdate": WebPull(loc, true); break; default: PrintUsage(); return; } } private static void PrintUsage() { Console.WriteLine("Usage: "); Console.WriteLine("LocalizationTool.exe CLEAN [locale identifier]"); Console.WriteLine("LocalizationTool.exe BUILD [locale identifier]"); Console.WriteLine("LocalizationTool.exe UPDATE [locale identifier]"); Console.WriteLine("LocalizationTool.exe REPORT [locale identifier]"); Console.WriteLine("LocalizationTool.exe CREATE "); Console.WriteLine("LocalizationTool.exe GUIUPDATE "); Console.WriteLine("LocalizationTool.exe EXPORT [locale identifier]"); Console.WriteLine("LocalizationTool.exe IMPORT "); Console.WriteLine("LocalizationTool.exe EXPORTDIFF "); Console.WriteLine("LocalizationTool.exe WEBPULL [locale identifier]"); Console.WriteLine("LocalizationTool.exe WEBUPDATE [locale identifier]"); } private struct CSVEntry { public string Filename; public string Fieldkey; public string Origvalue; public string Value; public string Status; public string[] extraFields; public CSVEntry(List fields) { Filename = fields[0]; Fieldkey = fields[1]; Status = fields[2]; Origvalue = fields[3]; Value = fields[4]; if (fields.Count > 5) { extraFields = new string[fields.Count - 5]; fields.CopyTo(5, extraFields, 0, extraFields.Length); } else extraFields = null; } } private static bool IsValidCulture(string c) { try { System.Globalization.CultureInfo.GetCultureInfo(c); return true; } catch { return false; } } /// /// Downloads updated CSV files from the web and generates diff files, and optionally updates the local CSV files /// /// The culture to use /// True if the elements should be updated as well private static void WebPull(string loc, bool update) { var locs = from n in Directory.GetFiles(Application.StartupPath, "report.*.url") where IsValidCulture(Path.GetFileNameWithoutExtension(n).Substring("report.".Length)) select Path.GetFileNameWithoutExtension(n).Substring("report.".Length); if (!string.IsNullOrEmpty(loc)) { locs = locs.Where(c => string.Equals(c, loc, StringComparison.CurrentCultureIgnoreCase)); if (locs.Count() == 0) { Console.WriteLine("No URL file found for culture {0}", loc); return; } } if (locs.Count() == 0) { Console.WriteLine("No culture.url files found"); return; } foreach (var culture in locs) { var url = string.Format("report.{0}.url", culture); var file = string.Format("report.{0}.new.csv", culture); if (File.Exists(file)) File.Delete(file); Console.Write("Downloading updated CSV for {0} ... ", culture); try { new WebClient().DownloadFile(File.ReadAllText(url).Trim(), file); } catch (Exception ex) { Console.WriteLine("Failed!"); Console.WriteLine(" -> {0}", ex.Message); continue; } Console.WriteLine("Done!"); if (update) { Console.Write("Updating local files for {0} .", culture); ExportDiff(culture, file); Console.Write("."); Import(culture, file); Console.Write("."); Export(culture); Console.WriteLine(" Done!"); } } } /// /// Imports a CSV file into a dictionary format, /// Outer key is filename, inner key is fieldname, inner value is translated text /// /// The CSV file to read from /// The current culture string /// Dictionary where outer key is filename, inner key is fieldname, inner value is translated text private static Dictionary> ImportCSV(string file, string loc, Func filter) { Dictionary> values = new Dictionary>(); using (CSVReader r = new CSVReader(file)) { List fields = null; while ((fields = r.AdvanceLine()) != null) if (fields.Count >= 5) { CSVEntry e = new CSVEntry(fields); e.Filename = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, loc), e.Filename); if (filter != null && filter(e)) continue; if (!values.ContainsKey(e.Filename)) values.Add(e.Filename, new Dictionary()); Dictionary l = values[e.Filename]; l[e.Fieldkey] = e; } } return values; } private static void Import(string loc, string p) { //Outer key is filename, inner key is fieldname, inner value is translated text Dictionary> values = ImportCSV(p, loc, e => e.Value.Trim().Length == 0 || e.Value == e.Origvalue); if (Duplicati.Library.Utility.Utility.IsClientLinux) { Dictionary> remap = new Dictionary>(); foreach(string key in values.Keys) remap[key.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString ())] = values[key]; values = remap; } string folder = System.IO.Path.Combine(Application.StartupPath, loc); if (!System.IO.Directory.Exists(folder)) Create(loc); else Update(loc); XNamespace xmlns = ""; foreach (ResXFileInfo inf in GetResXList(loc)) { if (System.IO.File.Exists(inf.TargetFile) && values.ContainsKey(inf.TargetFile)) { XDocument targetDoc = XDocument.Load(inf.TargetFile); XNode insertTarget = targetDoc.Element("root").LastNode; var targetVals = targetDoc.Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile); var sourceVals = values[inf.TargetFile]; values.Remove(inf.TargetFile); bool updated = false; foreach (var item in sourceVals) if (targetVals.ContainsKey(item.Key)) { if (targetVals[item.Key].Element("value").Value.Replace("\r\n", "\n") != item.Value.Value.Replace("\r\n", "\n")) { updated = true; targetVals[item.Key].Element("value").Value = item.Value.Value; } } else { updated = true; insertTarget.AddAfterSelf(new XElement("data", new XAttribute("name", item.Key), new XAttribute(xmlns + "space", "preserve"), new XElement("value", item.Value.Value) )); } if (updated) targetDoc.Save(inf.TargetFile); } } if (values.Count != 0) Console.WriteLine("The following files were translated but did not exist: " + string.Join(Environment.NewLine, values.Keys.ToArray())); } public static IEnumerable GetLocaleFolders(string locid) { if (string.IsNullOrEmpty(locid)) return System.IO.Directory.GetDirectories(Application.StartupPath).Where(c => { try { System.Globalization.CultureInfo.GetCultureInfo(System.IO.Path.GetFileName(c)); return true; } catch { return false; } }).Select(c => System.IO.Path.GetFileName(c)); else return new string[] { locid }; } private static void Export(string cultures) { Update(cultures); Report(cultures); foreach (string culture in GetLocaleFolders(cultures)) { XDocument doc = XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".xml")); string outfile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".csv"); Dictionary> elems = new Dictionary>(); foreach (var file in doc.Element("root").Elements("file")) { string filename = file.Attribute("filename").Value.Substring(Duplicati.Library.Utility.Utility.AppendDirSeparator(Application.StartupPath).Length); filename = filename.Substring(culture.Length + 1); Dictionary e = new Dictionary(); elems[filename] = e; foreach (var item in file.Element("updated").Elements("item")) { CSVEntry c = new CSVEntry(); c.Filename = filename; c.Fieldkey = item.Attribute("name").Value; c.Status = item.Parent.Name.LocalName; c.Origvalue = item.Element("original").Value; c.Value = item.Element("translated").Value; e.Add(c.Fieldkey, c); } foreach (var item in file.Element("missing").Elements("item")) { CSVEntry c = new CSVEntry(); c.Filename = filename; c.Fieldkey = item.Attribute("name").Value; c.Status = item.Parent.Name.LocalName; c.Origvalue = item.Value; c.Value = ""; e.Add(c.Fieldkey, c); } foreach (var item in file.Element("not-updated").Elements("item")) { CSVEntry c = new CSVEntry(); c.Filename = filename; c.Fieldkey = item.Attribute("name").Value; c.Status = item.Parent.Name.LocalName; c.Origvalue = item.Value; c.Value = ""; e.Add(c.Fieldkey, c); } foreach (var item in file.Element("unused").Elements("item")) { CSVEntry c = new CSVEntry(); c.Filename = filename; c.Fieldkey = item.Attribute("name").Value; c.Status = item.Parent.Name.LocalName; c.Origvalue = ""; c.Value = item.Value; e.Add(c.Fieldkey, c); } } WriteCSVFile(outfile, culture, elems, null); } } private static void ExportDiff(string culture, string inputfile) { inputfile = System.IO.Path.GetFullPath(inputfile); string currentFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".csv"); string diffFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".diff.csv"); if (currentFile.Equals(inputfile, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision)) throw new Exception("Input file will be overwritten, please use another file"); Export(culture); //Outer key is filename, inner key is fieldname, inner value is translated text Dictionary> inputValues = ImportCSV(inputfile, culture, null); Dictionary> currentValues = ImportCSV(currentFile, culture, null); Dictionary> added = new Dictionary>(); Dictionary> removed = new Dictionary>(); foreach (var f in currentValues) { Dictionary other; inputValues.TryGetValue(f.Key, out other); if (other == null) { added.Add(f.Key, f.Value); } else { Dictionary a = new Dictionary(); foreach (KeyValuePair s in f.Value) { if (other.ContainsKey(s.Key)) other.Remove(s.Key); else a.Add(s.Key, s.Value); } if (a.Count > 0) added.Add(f.Key, a); if (other.Count > 0) removed.Add(f.Key, other); } } Dictionary> overrides = new Dictionary>(); Dictionary> diffElems = new Dictionary>(); foreach (KeyValuePair> f in added) foreach (KeyValuePair s in f.Value) if (s.Value.Origvalue == s.Value.Value || s.Value.Value.Trim().Length == 0) { if (!overrides.ContainsKey(f.Key)) overrides.Add(f.Key, new Dictionary()); if (!diffElems.ContainsKey(f.Key)) diffElems.Add(f.Key, new Dictionary()); overrides[f.Key].Add(s.Key, "not-updated"); diffElems[f.Key].Add(s.Key, s.Value); } foreach (KeyValuePair> f in removed) foreach (KeyValuePair s in f.Value) { if (!overrides.ContainsKey(f.Key)) overrides.Add(f.Key, new Dictionary()); if (!diffElems.ContainsKey(f.Key)) diffElems.Add(f.Key, new Dictionary()); overrides[f.Key].Add(s.Key, "unused"); diffElems[f.Key].Add(s.Key, s.Value); } WriteCSVFile(diffFile, culture, diffElems, overrides); //Re-read the file inputValues = ImportCSV(inputfile, culture, null); //Add the new entries foreach (KeyValuePair> f in added) { if (!inputValues.ContainsKey(f.Key)) inputValues.Add(f.Key, new Dictionary()); Dictionary o = inputValues[f.Key]; foreach (KeyValuePair s in f.Value) o.Add(s.Key, s.Value); } foreach (KeyValuePair> f in currentValues) { Dictionary o = inputValues[f.Key]; foreach (KeyValuePair s in f.Value) { CSVEntry c = o[s.Key]; if (s.Value.Status == "unused") c.Status = "unused"; else if (c.Origvalue == c.Value) c.Status = "not-updated"; else if (string.IsNullOrEmpty(c.Value) || c.Value.Trim().Length == 0) c.Status = "missing"; else c.Status = "updated"; o[s.Key] = c; } } //Update the removed entries foreach (KeyValuePair> f in removed) { Dictionary o = inputValues[f.Key]; foreach (KeyValuePair s in f.Value) { CSVEntry c = o[s.Key]; c.Status = "unused"; o[s.Key] = c; } } //Write the output file diffFile = System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".updated.csv"); WriteCSVFile(diffFile, culture, inputValues, null); } private static void WriteCSVFile(string filename, string culture, Dictionary> entries, Dictionary> status_overrides) { if (status_overrides == null) status_overrides = new Dictionary>(); List>> sortlist = new List>>(); foreach (KeyValuePair> f in entries) foreach (KeyValuePair s in f.Value) sortlist.Add(new KeyValuePair>(f.Key, s)); var sortedlist = sortlist.OrderBy(x => x.Key + System.IO.Path.PathSeparator + x.Value.Key); using (System.IO.StreamWriter sw = new System.IO.StreamWriter(filename, false, new System.Text.UTF8Encoding(false))) { string filenameprefix = System.IO.Path.Combine(Application.StartupPath, culture); int pfl = filenameprefix.Length + 1; foreach (KeyValuePair> f in sortedlist) { string status = f.Value.Value.Status; if (status_overrides.ContainsKey(f.Key) && status_overrides[f.Key].ContainsKey(f.Value.Key)) status = status_overrides[f.Key][f.Value.Key]; string path = f.Key; if (f.Key.StartsWith(filenameprefix, Duplicati.Library.Utility.Utility.ClientFilenameStringComparision)) path = f.Key.Substring(Math.Min(pfl, f.Key.Length)); if (Duplicati.Library.Utility.Utility.IsClientLinux) path = path.Replace(System.IO.Path.DirectorySeparatorChar.ToString(), "\\"); WriteCSVLine(sw, path, f.Value.Key, status, f.Value.Value.Origvalue, f.Value.Value.Value, f.Value.Value.extraFields); } } } private static string CSV_SEPARATOR = ","; private static void WriteCSVLine(System.IO.StreamWriter sw, string filename, string key, string status, string originalText, string translatedText, string[] extraFields) { sw.Write(EscapeCSVString(filename)); sw.Write(CSV_SEPARATOR); sw.Write(EscapeCSVString(key)); sw.Write(CSV_SEPARATOR); sw.Write(EscapeCSVString(status)); sw.Write(CSV_SEPARATOR); sw.Write(EscapeCSVString(originalText)); sw.Write(CSV_SEPARATOR); sw.Write(EscapeCSVString(translatedText)); if (extraFields != null && extraFields.Length > 0) foreach (string s in extraFields) { sw.Write(CSV_SEPARATOR); sw.Write(EscapeCSVString(s)); } sw.Write("\r\n"); } private static string EscapeCSVString(string value) { //.Replace("\\", "\\\\").Replace("\r\n", "\\n").Replace("\r", "\\n").Replace("\n", "\\n").Replace("\t", "\\t") return "\"" + value.Replace("\"", "\"\"") + "\""; } private static void Report(string cultures) { foreach (string culture in GetLocaleFolders(cultures)) { Dictionary ignores = new Dictionary(); string ignorefile = System.IO.Path.Combine(Application.StartupPath, "ignore." + culture + ".xml"); if (System.IO.File.Exists(ignorefile)) ignores = XDocument.Load(ignorefile).Element("root").Elements("ignore").Select(c => c.Value).ToSafeDictionary(c => c.ToLower().Trim(), ignorefile); //Make sure we ignore empty strings ignores[""] = ""; //Ignore common leftover values for (int i = 1; i < 20; i++) { string _i = i.ToString(); ignores["textbox" + _i] = ""; ignores["combobox" + _i] = ""; ignores["checkbox" + _i] = ""; ignores["label" + _i] = ""; ignores["radiobutton" + _i] = ""; ignores["button" + _i] = ""; ignores["toolstrip" + _i] = ""; ignores["toolstripbutton" + _i] = ""; ignores["statusstrip" + _i] = ""; } XDocument report = new XDocument( new XElement("root") ); XElement reportRoot = report.Element("root"); IEnumerable files = GetResXList(culture); var missingFiles = files.Where(c => !System.IO.File.Exists(c.TargetFile)); var existingFiles = files.Where(c => System.IO.File.Exists(c.TargetFile)); var extraFiles = from x in Duplicati.Library.Utility.Utility.EnumerateFiles(System.IO.Path.Combine(Application.StartupPath, culture)) where x.EndsWith("." + culture + ".resx") && !files.Select(c => c.TargetFile).Contains(x) select x; reportRoot.Add( new XElement("files", new XElement("missing", from x in missingFiles select new XElement("file", x.TargetFile) ), new XElement("unused", from x in extraFiles select new XElement("file", x) ) ) ); foreach(ResXFileInfo inf in existingFiles) { IEnumerable sourceElements = XDocument.Load(inf.SourceFile).Element("root").Elements("data"); IEnumerable targetElements = XDocument.Load(inf.TargetFile).Element("root").Elements("data"); if (inf.IsForm) { //Look only for strings Func filter = c => !c.Attribute("name").Value.StartsWith(">>") && c.Attribute("mimetype") == null && ( c.Attribute("type") == null || c.Attribute("type").Value == "System.String, mscorlib" || c.Attribute("type").Value == "System.String" ); sourceElements = sourceElements.Where(filter); targetElements = targetElements.Where(filter); } //Filter the source and target before proceeding sourceElements = sourceElements.Where(c => !ignores.ContainsKey(c.Element("value").Value.Trim().ToLower()) && !c.Element("value").Value.Trim().ToLower().StartsWith("..\\resources\\") && !c.Element("value").Value.Trim().ToLower().StartsWith("../resources/")); targetElements = targetElements.Where(c => !ignores.ContainsKey(c.Element("value").Value.Trim().ToLower()) && !c.Element("value").Value.Trim().ToLower().StartsWith("..\\resources\\") && !c.Element("value").Value.Trim().ToLower().StartsWith("../resources/")); var sourceVals = sourceElements.ToSafeDictionary(c => c.Attribute("name").Value, inf.SourceFile); var targetVals = targetElements.ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile); var missing = sourceVals.Where(c => !targetVals.ContainsKey(c.Key)).ToList(); var unused = targetVals.Where(c => !sourceVals.ContainsKey(c.Key)).ToList(); var notUpdated = sourceVals.Where(c => targetVals.ContainsKey(c.Key) && targetVals[c.Key].Element("value").Value == c.Value.Element("value").Value).ToList(); var updated = sourceVals.Where(c => !missing.Contains(c) && !unused.Contains(c) && !notUpdated.Contains(c) && targetVals.ContainsKey(c.Key) ); var yy = sourceVals.Values.Where(x => x.Value.Contains("System.Draw")).Concat(targetVals.Values.Where(x => x.Value.Contains("System.Draw"))); if (yy.Count() != 0) { } reportRoot.Add( new XElement("file", new XAttribute("filename", inf.TargetFile), new XElement("missing", from x in missing select new XElement("item", new XAttribute("name", x.Key), x.Value.Element("value").Value ) ), new XElement("unused", from x in unused select new XElement("item", new XAttribute("name", x.Key), x.Value.Element("value").Value ) ), new XElement("not-updated", from x in notUpdated select new XElement("item", new XAttribute("name", x.Key), x.Value.Element("value").Value ) ), new XElement("updated", from x in updated select new XElement("item", new XAttribute("name", x.Key), new XElement("original", x.Value.Element("value").Value ), new XElement("translated", targetVals[x.Key].Element("value").Value ) ) ) ) ); } report.Save(System.IO.Path.Combine(Application.StartupPath, "report." + culture + ".xml")); } } private static void Create(string culture) { System.Globalization.CultureInfo ci = System.Globalization.CultureInfo.GetCultureInfo(culture); //Validate string folder = System.IO.Path.Combine(Application.StartupPath, culture); if (!System.IO.Directory.Exists(folder)) System.IO.Directory.CreateDirectory(folder); Update(culture); } public class ResXFileInfo { public string SourceFile { get; set; } public string TargetFile { get; set; } public string NetrualTargetFile { get; set; } public bool IsForm { get; set; } } public static IEnumerable GetResXList(string culture) { List res = new List(); foreach (XElement conf in XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "configuration.xml")).Element("root").Elements("configuration")) { string sourcefolder = conf.Element("sourcefolder").Value; if (Duplicati.Library.Utility.Utility.IsClientLinux) sourcefolder = sourcefolder.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString()); string outputfolder = System.IO.Path.GetFullPath(Duplicati.Library.Utility.Utility.AppendDirSeparator(System.IO.Path.Combine(Application.StartupPath, culture))); sourcefolder = Duplicati.Library.Utility.Utility.AppendDirSeparator(System.IO.Path.GetFullPath(sourcefolder)); foreach (XElement fn in conf.Elements("assembly")) { string subfolder = fn.Attribute("folder").Value; if (Duplicati.Library.Utility.Utility.IsClientLinux) subfolder = subfolder.Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString()); foreach (string s in Duplicati.Library.Utility.Utility.EnumerateFiles(System.IO.Path.Combine(sourcefolder, subfolder))) { if (s.ToLower().StartsWith(Application.StartupPath.ToLower())) continue; if (s.EndsWith(".resx")) { string targetNameNeutral = System.IO.Path.Combine(outputfolder, s.Substring(sourcefolder.Length)); if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(targetNameNeutral))) System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(targetNameNeutral)); string targetFilename = targetNameNeutral.Substring(0, targetNameNeutral.Length - "resx".Length) + culture + ".resx"; if (res.Where(x => x.TargetFile == targetFilename).FirstOrDefault() == null) res.Add(new ResXFileInfo() { SourceFile = s, IsForm = System.IO.File.Exists(System.IO.Path.ChangeExtension(s, ".cs")), NetrualTargetFile = targetNameNeutral, TargetFile = targetFilename }); } } } } return res; } private static void Update(string cultures) { foreach (string culture in GetLocaleFolders(cultures)) { foreach (ResXFileInfo inf in GetResXList(culture)) { if (inf.IsForm) System.IO.File.Copy(inf.SourceFile, inf.NetrualTargetFile, true); //Copy the updated resx if (System.IO.File.Exists(inf.TargetFile)) { //Merge, forms are auto-merged, in that they depend on the neutral .resx file if (!inf.IsForm) { XDocument targetDoc = XDocument.Load(inf.TargetFile); XNode insertTarget = targetDoc.Element("root").LastNode; var sourceVals = XDocument.Load(inf.SourceFile).Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.SourceFile); var targetVals = targetDoc.Element("root").Elements("data").ToSafeDictionary(c => c.Attribute("name").Value, inf.TargetFile); bool updated = false; foreach (var item in sourceVals) if (!targetVals.ContainsKey(item.Key)) { updated = true; insertTarget.AddAfterSelf(new XElement(item.Value)); } if (updated) targetDoc.Save(inf.TargetFile); } } else { if (inf.IsForm) using (System.IO.StreamWriter sw = new System.IO.StreamWriter(inf.TargetFile)) sw.Write(Properties.Resources.Empty_resx); else { if (Duplicati.Library.Utility.Utility.IsClientLinux) { var doc = XDocument.Load(inf.SourceFile); var entries = from x in doc.Element("root").Elements("data") where x.Attribute("type") != null && x.Attribute("type").Value == "System.Resources.ResXFileRef, System.Windows.Forms" select x; foreach(var e in entries) { string name = e.Element("value").Value; int ixpos = name.IndexOf(";"); string path = name.Substring(0, ixpos); path = path.Replace ("\\", System.IO.Path.DirectorySeparatorChar.ToString()); e.Element("value").Value = path + name.Substring(ixpos); } doc.Save(inf.TargetFile); } else { System.IO.File.Copy(inf.SourceFile, inf.TargetFile); } } } foreach (var item in from y in (from x in XDocument.Load(inf.SourceFile).Element("root").Elements("data") where x.Attribute("type") != null && x.Attribute("type").Value == "System.Resources.ResXFileRef, System.Windows.Forms" select x) let relname = y.Element("value").Value.Substring(0, y.Element("value").Value.IndexOf(";")) let sourceRes = System.IO.Path.GetFullPath(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(inf.SourceFile), relname)).Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString()) let targetRes = System.IO.Path.GetFullPath(System.IO.Path.Combine(System.IO.Path.GetDirectoryName(inf.TargetFile), relname)).Replace("\\", System.IO.Path.DirectorySeparatorChar.ToString()) select new { sourceRes, targetRes } ) { if (!System.IO.Directory.Exists(System.IO.Path.GetDirectoryName(item.targetRes))) System.IO.Directory.CreateDirectory(System.IO.Path.GetDirectoryName(item.targetRes)); if (!System.IO.File.Exists(item.targetRes)) { if (Duplicati.Library.Utility.Utility.IsClientLinux && !System.IO.File.Exists(item.sourceRes)) { string basefolder = System.IO.Path.GetDirectoryName(item.sourceRes); string sourcefile = System.IO.Directory.GetFiles (basefolder).FirstOrDefault (x => item.sourceRes.Equals(x, StringComparison.InvariantCultureIgnoreCase)); if (sourcefile == null) Console.WriteLine ("Source file not found: " + item.sourceRes); else System.IO.File.Copy (sourcefile, item.targetRes); } else { System.IO.File.Copy(item.sourceRes, item.targetRes); } } } } string batchfilename = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), "gui update.bat"); System.IO.File.WriteAllText(batchfilename, Properties.Resources.Batchjob_bat, System.Text.Encoding.Default); } } private static void Clean(string cultureReq) { string root = Duplicati.Library.Utility.Utility.AppendDirSeparator(Application.StartupPath); if (!string.IsNullOrEmpty(cultureReq)) root = System.IO.Path.Combine(root, cultureReq); foreach (string s in Duplicati.Library.Utility.Utility.EnumerateFiles(Application.StartupPath)) if (System.IO.Path.GetDirectoryName(s) != Application.StartupPath && (System.IO.Path.GetExtension(s) == ".resources" || System.IO.Path.GetExtension(s) == ".dll")) System.IO.File.Delete(s); else if (System.IO.Path.GetExtension(s) == ".resx") { //Remove the extra files required for resgen.exe string culture = s.Substring(root.Length); culture = culture.Substring(0, culture.IndexOf(System.IO.Path.DirectorySeparatorChar)); if (!s.ToLower().EndsWith("." + culture.ToLower() + ".resx") && System.IO.File.Exists(s.Substring(0, s.Length - "resx".Length) + culture + ".resx")) System.IO.File.Delete(s); } } private static void Compile(string cultureReq) { XDocument doc = XDocument.Load(System.IO.Path.Combine(Application.StartupPath, "configuration.xml")); foreach (XElement conf in doc.Element("root").Elements("configuration")) { string keyfile = conf.Element("keyfile") == null ? null : conf.Element("keyfile").Value; string versionassembly = conf.Element("versionassembly").Value; string outputfolder = conf.Element("outputfolder").Value; string productname = conf.Element("productname").Value; string fxVer = conf.Element("frameworkVersion") == null ? "4.0" : conf.Element("frameworkVersion").Value; Console.WriteLine("Compiling for framework: " + fxVer); foreach (XElement n in conf.Elements("assembly")) { string assemblyName = n.Attribute("name").Value; string folder = n.Attribute("folder").Value; string @namespace = n.Attribute("namespace") == null ? assemblyName : n.Attribute("namespace").Value; foreach (string culture in GetLocaleFolders(cultureReq)) { List excludes = (from x in n.Elements("exclude") let fullpath = System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), x.Value) select fullpath).ToList(); string outfolder = System.IO.Path.GetFullPath(System.IO.Path.Combine(outputfolder, culture)); if (!System.IO.Directory.Exists(outfolder)) System.IO.Directory.CreateDirectory(outfolder); ResXCompiler.CompileResxFiles(System.IO.Path.Combine(System.IO.Path.Combine(Application.StartupPath, culture), folder), excludes, @namespace, System.IO.Path.Combine(outfolder, assemblyName + ".resources.dll"), System.IO.Path.GetFullPath(versionassembly), keyfile, culture, productname, fxVer); } } } } } }