/* * FuelBoard Copy Editor — .strings format helpers (pure, no DOM). * * The editor can show Localizable.strings either as raw text or as a * structured key/value form. These functions parse the file into blocks * (entries + comments + blanks) and serialize them back, preserving every * comment and blank line byte-for-byte. * * .strings shape (verified against the real file): * "Key" = "Value"; -> entry (single line) * /* Section banner *\/ -> comment * (blank) -> blank * Multi-line comment blocks (like the header) are kept verbatim. Any line * that is not a valid entry, comment, or blank is kept verbatim as "raw" * and the form view refuses to activate (falls back to Raw editing) so we * can never clobber a file we don't understand. */ (function (root, factory) { if (typeof module !== "undefined" && module.exports) { module.exports = factory(); } else { root.StringsFormat = factory(); } })(this, function () { "use strict"; // Display value -> file-escaped value. Backslash FIRST, then quotes. function escapeStr(s) { return s .replace(/\\/g, "\\\\") .replace(/"/g, '\\"') .replace(/\n/g, "\\n") .replace(/\t/g, "\\t"); } // File-escaped value -> display value. Unknown escapes (\uXXXX, \') are // passed through untouched so round-trips never lose data. function unescapeStr(s) { return s.replace(/\\(.)/g, function (m, c) { return c === "n" ? "\n" : c === "t" ? "\t" : c === '"' ? '"' : c === "\\" ? "\\" : m; }); } // Text -> [{type:"blank"|"comment"|"entry"|"raw", ...}] // entry: {type, key, value (unescaped), raw} function parseStrings(text) { var lines = text.split("\n"); var blocks = []; var i = 0; while (i < lines.length) { var raw = lines[i]; var s = raw.trim(); if (s === "") { blocks.push({ type: "blank", raw: "" }); i++; continue; } if (s.indexOf("/*") === 0) { // Comment block — may span lines (the file header does). var block = [raw]; i++; while (i < lines.length && block[block.length - 1].indexOf("*/") === -1) { block.push(lines[i]); i++; } blocks.push({ type: "comment", raw: block.join("\n") }); continue; } var m = s.match(/^"((?:[^"\\]|\\.)*)"\s*=\s*"((?:[^"\\]|\\.)*)"\s*;$/); if (m) { blocks.push({ type: "entry", key: m[1], value: unescapeStr(m[2]), raw: raw }); i++; continue; } // Not something we understand — keep verbatim, flag for the caller. blocks.push({ type: "raw", raw: raw }); i++; } return blocks; } function serializeStrings(blocks) { return blocks .map(function (b) { if (b.type === "entry") { return '"' + b.key + '" = "' + escapeStr(b.value) + '";'; } return b.raw; }) .join("\n"); } return { parseStrings: parseStrings, serializeStrings: serializeStrings, escapeStr: escapeStr, unescapeStr: unescapeStr }; });