269 lines
6.3 KiB
JavaScript
269 lines
6.3 KiB
JavaScript
/*
|
||
SPDX-License-Identifier: GPL-3.0-or-later
|
||
Copyright © 1991-2022 Amebis
|
||
Copyright © 2016 GÉANT
|
||
*/
|
||
|
||
/*@cc_on @*/
|
||
/*@if (! @_escapePO_JS__) @*/
|
||
/*@set @_escapePO_JS__ = true @*/
|
||
|
||
var escapePO_stat = null;
|
||
function escapePO(str)
|
||
{
|
||
if (!escapePO_stat) {
|
||
escapePO_stat = {
|
||
"re_bslash": new RegExp("\\\\", "g"),
|
||
"re_bs": new RegExp("\b", "g"),
|
||
"re_ff": new RegExp("\f", "g"),
|
||
"re_lf": new RegExp("\n", "g"),
|
||
"re_cr": new RegExp("\r", "g"),
|
||
"re_tab": new RegExp("\t", "g"),
|
||
"re_quot": new RegExp("\"", "g")
|
||
};
|
||
}
|
||
|
||
if (str == null) return null;
|
||
switch (typeof(str)) {
|
||
case "string": break;
|
||
case "undefined": return null;
|
||
default: try { str = str.toString(); } catch (err) { return null; }
|
||
}
|
||
|
||
return str.replace(escapePO_stat.re_bslash, "\\\\").replace(escapePO_stat.re_bs, "\\b").replace(escapePO_stat.re_ff, "\\f").replace(escapePO_stat.re_lf, "\\n").replace(escapePO_stat.re_cr, "\\r").replace(escapePO_stat.re_tab, "\\t").replace(escapePO_stat.re_quot, "\\\"");
|
||
}
|
||
|
||
|
||
var unescapePO_stat = null;
|
||
function unescapePO(str)
|
||
{
|
||
if (!unescapePO_stat) {
|
||
unescapePO_stat = {
|
||
"re_esc": new RegExp("\\\\(.)", "g"),
|
||
"fn_esc": function($0, $1) {
|
||
switch ($1) {
|
||
case "\\": return "\\";
|
||
case "b" : return "\b";
|
||
case "f" : return "\f";
|
||
case "n" : return "\n";
|
||
case "r" : return "\r";
|
||
case "t" : return "\t";
|
||
case "\'": return "\'";
|
||
case "\"": return "\"";
|
||
default : return $0;
|
||
}
|
||
}
|
||
};
|
||
}
|
||
|
||
if (str == null) return null;
|
||
switch (typeof(str)) {
|
||
case "string": break;
|
||
case "undefined": return null;
|
||
default: try { str = str.toString(); } catch (err) { return null; }
|
||
}
|
||
|
||
return str.replace(unescapePO_stat.re_esc, unescapePO_stat.fn_esc);
|
||
}
|
||
|
||
|
||
function POCatalog()
|
||
{
|
||
this.charset = "utf-8";
|
||
this.data = new Array();
|
||
|
||
if (arguments.length) {
|
||
var
|
||
path = arguments[0];
|
||
|
||
// Open file.
|
||
var dat = new ActiveXObject("ADODB.Stream");
|
||
dat.Open();
|
||
try {
|
||
// PO catalogue is text file, uses Unix line breaks and UTF-8.
|
||
dat.Type = adTypeText;
|
||
dat.LineSeparator = adLF;
|
||
dat.Charset = this.charset;
|
||
|
||
// Load file.
|
||
dat.LoadFromFile(path);
|
||
|
||
// Prepare regular expressions for catalogue parsing.
|
||
var regexp = {
|
||
"fuzzy": new RegExp("^\\s*#,\\s*fuzzy", "i"),
|
||
"comment": new RegExp("^\\s*#", ""),
|
||
"record": new RegExp("^\\s*(\\w*)\\s*\"(.*)\"\\s*$", "")
|
||
};
|
||
|
||
var getNext = function() {
|
||
var
|
||
id = null,
|
||
rec = new Array();
|
||
|
||
while (!dat.EOS) {
|
||
var
|
||
s = Trim(dat.ReadText(adReadLine)),
|
||
m;
|
||
|
||
// The line is empty => end of record.
|
||
if (s == "" && "msgid" in rec)
|
||
break;
|
||
|
||
// The record is marked as "fuzzy".
|
||
if (s.search(regexp.fuzzy) != -1) {
|
||
rec["fuzzy"] = true;
|
||
continue;
|
||
}
|
||
|
||
// Concatenate comments.
|
||
if (s.search(regexp.comment) != -1) {
|
||
if ("#" in rec)
|
||
rec["#"] += s + "\n";
|
||
else
|
||
rec["#"] = s + "\n";
|
||
continue;
|
||
}
|
||
|
||
m = s.match(regexp.record);
|
||
if (m) {
|
||
// Record found.
|
||
if (m[1] != "")
|
||
id = new String(m[1]);
|
||
if (id in rec)
|
||
rec[id] += new String(m[2]);
|
||
else
|
||
rec[id] = new String(m[2]);
|
||
continue;
|
||
}
|
||
}
|
||
|
||
// Unescape messages now that they are concatenated.
|
||
for (id in rec)
|
||
rec[id] = unescapePO(rec[id]);
|
||
return "msgid" in rec ? rec : null;
|
||
}
|
||
|
||
// Read header record.
|
||
var rec = getNext();
|
||
if (rec == null)
|
||
return;
|
||
if (rec.msgid == "") {
|
||
// Parse charset.
|
||
m = rec.msgstr.match(new RegExp("^\\s*Content-Type\\s*:\\s*([-\\w]+/[-\\w]+)\\s*(;\\s*charset\\s*=\\s*([-\\w]+))?$", "im"));
|
||
if (m && m.length >= 4) {
|
||
this.charset = m[3];
|
||
|
||
// Rewind and reconfigure code page.
|
||
dat.Position = 0;
|
||
dat.Charset = this.charset;
|
||
|
||
// Re-read header.
|
||
rec = getNext();
|
||
}
|
||
}
|
||
|
||
// Load header and all the records.
|
||
this.push(rec.msgid, rec.msgstr, rec.fuzzy, "#" in rec ? rec["#"] : null);
|
||
while ((rec = getNext()) != null)
|
||
this.push(rec.msgid, rec.msgstr, rec.fuzzy, "#" in rec ? rec["#"] : null);
|
||
} finally {
|
||
dat.Close();
|
||
}
|
||
}
|
||
}
|
||
|
||
|
||
POCatalog.prototype.push = function(key, trans, fuzzy, comment)
|
||
{
|
||
var rec = {
|
||
"msgstr" : trans,
|
||
"fuzzy" : fuzzy ? true : false
|
||
};
|
||
if (comment)
|
||
rec["#"] = comment;
|
||
|
||
this.data[key] = rec;
|
||
}
|
||
|
||
|
||
POCatalog.prototype.search = function(key)
|
||
{
|
||
return (key in this.data) ? this.data[key] : null;
|
||
}
|
||
|
||
|
||
POCatalog.prototype.translate = function(key)
|
||
{
|
||
if (!(key in this.data)) {
|
||
// No translation found.
|
||
return key;
|
||
}
|
||
|
||
var rec = this.data[key];
|
||
if (rec.msgstr == "" || rec.fuzzy) {
|
||
// Translation is blank (untranslated) or fuzzy.
|
||
return key;
|
||
}
|
||
|
||
return rec.msgstr;
|
||
}
|
||
|
||
|
||
POCatalog.prototype.save = function(path)
|
||
{
|
||
// Open PO file in memory.
|
||
var dat = new ActiveXObject("ADODB.Stream");
|
||
dat.Open();
|
||
try {
|
||
// PO is text file, uses Unix line breaks and given encoding.
|
||
dat.Type = adTypeText;
|
||
dat.LineSeparator = adLF;
|
||
dat.Charset = this.charset;
|
||
|
||
var writeRec = function(key, rec) {
|
||
if (rec.fuzzy)
|
||
dat.WriteText("#, fuzzy", adWriteLine);
|
||
|
||
if ("#" in rec)
|
||
dat.WriteText(rec["#"], adWriteLine);
|
||
|
||
dat.WriteText("msgid \"" + escapePO(key ) + "\"", adWriteLine);
|
||
dat.WriteText("msgstr \"" + escapePO(rec.msgstr) + "\"", adWriteLine);
|
||
dat.WriteText("", adWriteLine);
|
||
}
|
||
|
||
// Write header first.
|
||
if ("" in this.data)
|
||
writeRec("", this.data[""]);
|
||
|
||
// Write records, skip header.
|
||
for (var key in this.data) {
|
||
if (key == "") continue;
|
||
writeRec(key, this.data[key]);
|
||
}
|
||
|
||
if (this.charset.toLowerCase() == "utf-8") {
|
||
// Write to file without UTF-8 BOM.
|
||
var dat_nobom = new ActiveXObject("ADODB.Stream");
|
||
dat_nobom.Type = adTypeBinary;
|
||
dat_nobom.Mode = adModeReadWrite;
|
||
dat_nobom.Open();
|
||
try {
|
||
// Skip BOM (first three bytes) and copy the rest.
|
||
dat.Position = 3;
|
||
dat.CopyTo(dat_nobom);
|
||
|
||
dat_nobom.SaveToFile(path, adSaveCreateOverWrite);
|
||
} finally {
|
||
dat_nobom.Close();
|
||
}
|
||
} else {
|
||
dat.SaveToFile(path, adSaveCreateOverWrite);
|
||
}
|
||
} finally {
|
||
dat.Close();
|
||
}
|
||
}
|
||
|
||
/*@end @*/
|