95 lines
3.0 KiB
JavaScript
95 lines
3.0 KiB
JavaScript
/*
|
|
* 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 };
|
|
});
|