/// <summary> /// Extract all the localizable strings from the XML configuration files in the FieldWorks distribution tree. /// </summary> /// <returns>Sorted and merged list of localizable strings</returns> private static List <POString> ExtractLocalizableStrings(string distFilesDir) { var poStrings = new List <POString>(1000); ExtractFromXmlConfigFiles(distFilesDir, poStrings); poStrings.Sort(POString.CompareMsgIds); POString.MergeDuplicateStrings(poStrings); return(poStrings); }
internal static Dictionary <string, POString> ReadPoFile(TextReader srIn, TaskLoggingHelper log) { POString.ResetInputLineNumber(); var dictTrans = new Dictionary <string, POString>(); POString.ReadFromFile(srIn); // read header var poStr = POString.ReadFromFile(srIn); while (poStr != null) { StoreString(dictTrans, poStr, log); poStr = POString.ReadFromFile(srIn); } return(dictTrans); }
/// <summary> /// Store an attribute value with a comment giving its xpath location. /// </summary> private static void StoreAttributeString(XElement xel, string name, string value, List <POString> poStrings, string autoCommentFilePath) { var sTranslate = xel.Attribute("translate")?.Value; if (sTranslate?.Trim().ToLower() == "do not translate") { return; } var sPath = ComputePathComment(xel, name, autoCommentFilePath); var comments = string.IsNullOrEmpty(sTranslate) ? new[] { sPath } : new[] { sPath, EscapeStringForPo(sTranslate) }; var pos = new POString(comments, new[] { EscapeStringForPo(value) }); poStrings.Add(pos); }
/// <summary> /// Store a POString in the dictionary, unless another string with the same msgid already exists. /// </summary> public static void StoreString(Dictionary <string, POString> dictTrans, POString poStr, TaskLoggingHelper log) { if (poStr.IsObsolete) { return; } var msgid = poStr.MsgIdAsString(); if (dictTrans.ContainsKey(msgid)) { log?.LogMessage("The message id '{0}' already exists. Ignoring this occurrence around line {1}.", msgid, POString.InputLineNumber); } else { dictTrans.Add(msgid, poStr); } }
/// <summary> /// Store the string for a <lit> element. /// </summary> private static void StoreLiteralString(XElement xel, List <POString> poStrings, string autoCommentFilePath) { var sTranslate = xel.Attribute("translate")?.Value; if (sTranslate?.Trim().ToLower() == "do not translate") { return; } var sPath = ComputePathComment(xel, null, autoCommentFilePath); var comments = string.IsNullOrEmpty(sTranslate) ? new[] { sPath } : new[] { sPath, EscapeStringForPo(sTranslate) }; var value = EscapeStringForPo(xel.Value); var valueLines = value.Split(NewlineChars, StringSplitOptions.None); var pos = new POString(comments, valueLines); poStrings.Add(pos); }
public bool HasSameMsgId(POString that) { if (MsgId == null && that.MsgId == null) { return(true); } if (MsgId == null || that.MsgId == null || MsgId.Count != that.MsgId.Count) { return(false); } for (var i = 0; i < MsgId.Count; ++i) { var s1 = MsgId[i]; var s2 = that.MsgId[i]; if (s1 != s2) { return(false); } } return(true); }
/// <summary> /// CompareMsgIds two POString objects with the purpose of sorting the PO file by msgid. /// </summary> public static int CompareMsgIds(POString x, POString y) { if (x.MsgId == null && y.MsgId == null) { return(0); } if (x.MsgId == null) { return(-1); } if (y.MsgId == null) { return(1); } // First, ignore case and cruft so that similar strings are close together for (var i = 0; i < x.MsgId.Count && i < y.MsgId.Count; ++i) { var s1 = RemoveCruft(x.MsgId[i]); var s2 = RemoveCruft(y.MsgId[i]); var n = string.Compare(s1, s2, StringComparison.OrdinalIgnoreCase); if (n != 0) { return(n); } } if (x.MsgId.Count < y.MsgId.Count) { return(-1); } if (x.MsgId.Count > y.MsgId.Count) { return(1); } // If the strings are otherwise identical, sort including case and cruft so that // identical strings can be merged without the interference of not-quite-identical strings return(string.Compare(x.MsgId[0], y.MsgId[0], StringComparison.Ordinal)); }
private static void WritePoHeader(TextWriter writer, string fwRoot, string locale) { writer.WriteLine(""); // StreamWriter writes a BOM for UTF-8: put it on a line by itself. var sTime = DateTime.Now.ToLocalTime().ToString("o"); const string fromFwSources = "Created from FieldWorks sources"; POString.WriteComment(fromFwSources, ' ', writer); var copyrightSIL = $"Copyright (c) {DateTime.Now.Year} SIL International"; POString.WriteComment(copyrightSIL, ' ', writer); POString.WriteComment("This software is licensed under the LGPL, version 2.1 or later", ' ', writer); POString.WriteComment("(http://www.gnu.org/licenses/lgpl-2.1.html)", ' ', writer); POString.WriteComment("", ' ', writer); //POString.WriteComment("fuzzy", ',', swOut); the on-line translation site doesn't like this. writer.Write("msgid "); POString.WriteQuotedLine("", writer); writer.Write("msgstr "); POString.WriteQuotedLine("", writer); var sVersion = GetFieldWorksVersion(fwRoot); POString.WriteQuotedLine($@"Project-Id-Version: {sVersion}\n", writer); POString.WriteQuotedLine(@"Report-Msgid-Bugs-To: [email protected]\n", writer); POString.WriteQuotedLine($@"POT-Creation-Date: {sTime}\n", writer); POString.WriteQuotedLine(@"PO-Revision-Date: \n", writer); POString.WriteQuotedLine(@"Last-Translator: Full Name <email@address>\n", writer); POString.WriteQuotedLine(@"Language-Team: Language <email@address>\n", writer); POString.WriteQuotedLine(@"MIME-Version: 1.0\n", writer); POString.WriteQuotedLine(@"Content-Type: text/plain; charset=UTF-8\n", writer); POString.WriteQuotedLine(@"Content-Transfer-Encoding: 8bit\n", writer); if (string.IsNullOrEmpty(locale)) { POString.WriteQuotedLine(@"X-Poedit-Language: \n", writer); POString.WriteQuotedLine(@"X-Poedit-Country: \n", writer); } else { var localeParts = locale.Split('-', '_'); var ci = new CultureInfo(localeParts[0]); var englishName = ci.EnglishName; var country = string.Empty; var variant = string.Empty; if (localeParts.Length > 1) { var ri = new RegionInfo(localeParts[1]); country = ri.EnglishName; } if (localeParts.Length > 2) { ci = new CultureInfo(locale); variant = ci.EnglishName; var idx = variant.IndexOf(englishName, StringComparison.Ordinal); if (idx >= 0) { variant = variant.Remove(idx, englishName.Length); } idx = variant.IndexOf(country, StringComparison.Ordinal); if (idx >= 0) { variant = variant.Remove(idx, country.Length); } variant = variant.Replace('(', ' '); variant = variant.Replace(')', ' '); variant = variant.Trim(',', ' ', '\t', '\n', '\r'); } POString.WriteQuotedLine($@"X-Poedit-Language: {englishName}\n", writer); POString.WriteQuotedLine($@"X-Poedit-Country: {country}\n", writer); if (!string.IsNullOrEmpty(variant)) { POString.WriteQuotedLine($@"X-Poedit-Variant: {variant}\n", writer); } } writer.WriteLine(""); }
public static POString ReadFromFile(TextReader srIn) { // Move past any blank lines, checking for the end of file. var s = srIn.ReadLine(); if (s == null) { return(null); } ++InputLineNumber; s = s.Trim(); while (string.IsNullOrEmpty(s)) { s = srIn.ReadLine(); if (s == null) { return(null); } ++InputLineNumber; s = s.Trim(); } var pos = new POString(); var fMsgId = false; var fMsgStr = false; var fError = false; do { if (s == "#") { pos.AddUserComment(""); } else if (s.StartsWith("# ")) { if (pos.IsObsolete) { pos.UserComments.Add(s.Substring(1)); } else { pos.AddUserComment(s.Substring(2)); } } else if (s.StartsWith("#.")) { if (pos.IsObsolete) { pos.UserComments.Add(s.Substring(1)); } else { pos.AddAutoComment(s.Substring(2).TrimStart()); } } else if (s.StartsWith("#:")) { if (pos.IsObsolete) { pos.UserComments.Add(s.Substring(1)); } else { pos.AddReference(s.Substring(2).TrimStart()); } } else if (s.StartsWith("#,")) { if (pos.IsObsolete) { pos.UserComments.Add(s.Substring(1)); } else { pos.AddFlags(s.Substring(2).TrimStart()); } } else if (s.ToLower().StartsWith("msgid")) { fMsgId = true; if (fMsgStr) { fError = true; } pos.AddMsgIdLine(s); } else if (s.ToLower().StartsWith("msgstr")) { if (!fMsgId) { fError = true; } fMsgId = false; fMsgStr = true; pos.AddMsgStrLine(s); } else if (s.StartsWith("\"")) { if (fMsgId) { pos.AddMsgIdLine(s); } else if (fMsgStr) { pos.AddMsgStrLine(s); } else { fError = true; } } else if (s.StartsWith("#~")) { if (!pos.IsObsolete) { if (pos.UserComments != null) { for (var i = 0; i < pos.UserComments.Count; ++i) { if (pos.UserComments[i] != null) { pos.UserComments[i] = " " + pos.UserComments[i]; } } } pos.AutoComments?.Clear(); pos.References?.Clear(); pos.Flags?.Clear(); pos.MsgId?.Clear(); pos.MsgStr?.Clear(); pos.IsObsolete = true; } pos.AddUserComment(s.Substring(1)); } else if (string.IsNullOrEmpty(s)) { Console.WriteLine("Ignoring invalid empty line at line {0}", InputLineNumber); } else { fError = true; } if (fError) { Console.WriteLine("INVALID PO FILE: ERROR ON LINE " + InputLineNumber); throw new Exception("BAD PO FILE"); } s = srIn.ReadLine(); if (s == null) { break; } ++InputLineNumber; s = s.Trim(); } while (!string.IsNullOrEmpty(s) || pos.MsgId == null || pos.MsgStr == null); // Multiline messages start with an empty line in PO files. Remove it from our data. // (Otherwise we add an extra "\n" line on output.) if (pos.MsgId != null && pos.MsgId.Count > 1 && string.IsNullOrEmpty(pos.MsgId[0])) { pos.MsgId.RemoveAt(0); } if (pos.MsgStr != null && pos.MsgStr.Count > 1 && string.IsNullOrEmpty(pos.MsgStr[0])) { pos.MsgStr.RemoveAt(0); } return(pos); }