mirror of
https://github.com/OMGeeky/andors-trail.git
synced 2026-02-23 15:38:29 +01:00
Updated translation tool to accomodate in-place translation of strings. Major thanks to surgecurrent for the code!
This commit is contained in:
@@ -111,50 +111,99 @@ function pushMessage(res, msg) {
|
||||
return res;
|
||||
}
|
||||
|
||||
function compareAndorsTrailResourceRow(result, fieldList, id, obj1, obj2) {
|
||||
for (var i = 0; i < fieldList._fields.length; ++i) {
|
||||
var f = fieldList._fields[i];
|
||||
var fieldName = fieldList.getFieldName(i);
|
||||
if (f instanceof FieldList) {
|
||||
fieldName = f._name;
|
||||
function isTranslatableField(fieldName) {
|
||||
return fieldName == "name" || fieldName == "logText" || fieldName == "message" || fieldName == "text";
|
||||
}
|
||||
|
||||
function compareAndorsTrailResourceHeader(result, id, header1, header2) {
|
||||
if (header1.length != header2.length) {
|
||||
pushMessage(result, "Row \"" + id + "\" was expected to contain " + f1.length + " sub-entries, but only " + f2.length + " was found.");
|
||||
return;
|
||||
}
|
||||
for (var i = 0; i < header1._fields.length; ++i) {
|
||||
var f1 = header1._fields[i];
|
||||
var fieldName1 = header1.getFieldName(i);
|
||||
var f2 = header2._fields[i];
|
||||
var fieldName2 = header2.getFieldName(i);
|
||||
if (fieldName1 != fieldName2) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + fieldName + "\" was expected to contain \"" + f1 + "\", but \"" + f2 + "\" was found.");
|
||||
}
|
||||
var isTranslatableField = (fieldName == "name" || fieldName == "logText" || fieldName == "message" || fieldName == "text");
|
||||
var f1 = obj1[fieldName];
|
||||
var f2 = obj2[fieldName];
|
||||
|
||||
if (f instanceof FieldList) {
|
||||
if (!f2) { f2 = []; }
|
||||
if (f1.length != f2.length) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + fieldName + "\" was expected to contain " + f1.length + " sub-entries, but only " + f2.length + " was found.");
|
||||
continue;
|
||||
}
|
||||
$.each(f1, function(i, obj) {
|
||||
var id_ = id + ":" + obj[f._fields[0]];
|
||||
compareAndorsTrailResourceRow(result, f, id_, f1[i], f2[i]);
|
||||
});
|
||||
} else {
|
||||
if (isTranslatableField && f1.length > 1) {
|
||||
if (f1 == f2) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + fieldName + "\" does not seem to be translated. Both texts are \"" + f1 + "\".");
|
||||
}
|
||||
} else {
|
||||
if (f1 != f2) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + fieldName + "\" was expected to contain \"" + f1 + "\", but \"" + f2 + "\" was found.");
|
||||
}
|
||||
}
|
||||
var fieldName2 = header2.getFieldName(i);
|
||||
if (f1 instanceof FieldList) {
|
||||
compareAndorsTrailResourceHeader(result, id+":"+fieldName1, f1, f2);
|
||||
}
|
||||
}
|
||||
}
|
||||
function extractTranslatableFields_(id, result, fieldList, prefix, obj) {
|
||||
if (!result) {
|
||||
result = {};
|
||||
result.id = id;
|
||||
result.fields = [];
|
||||
}
|
||||
if (!prefix) {
|
||||
prefix = "";
|
||||
}
|
||||
for (var i = 0; i < fieldList._fields.length; ++i) {
|
||||
var f = fieldList._fields[i];
|
||||
if (f instanceof FieldList) {
|
||||
// f is subfieldlist
|
||||
$.each(obj[f._name], function(j, elem) {
|
||||
extractTranslatableFields_(id, result, f, prefix+f._name+"["+j+"].", elem);
|
||||
});
|
||||
} else {
|
||||
// f is field name
|
||||
if (isTranslatableField(f)) {
|
||||
result.fields.push({
|
||||
"name":prefix+f,
|
||||
"value":obj[f]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
function extractTranslatableFields(id, fieldList, obj) {
|
||||
return extractTranslatableFields_(id, undefined, fieldList, "", obj);
|
||||
}
|
||||
|
||||
function compareAndorsTrailResourceRow(result, fieldList, id, obj1, obj2) {
|
||||
// Assume the headers of both objects are correctly matched
|
||||
trans1 = extractTranslatableFields(id, fieldList, obj1);
|
||||
trans2 = extractTranslatableFields(id, fieldList, obj2);
|
||||
$.each(trans1.fields, function(i, f1) {
|
||||
f2 = trans2.fields[i];
|
||||
if (f1.name != f2.name) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + f1.name + "\" does not match in translated data field\"" + f2.name + "\".");
|
||||
}
|
||||
if (f1.value.length > 1) {
|
||||
if (f1.value == f2.value) {
|
||||
pushMessage(result, "Row \"" + id + "\", field \"" + f1.name + "\" does not seem to be translated. Both texts are \"" + f1.value + "\".");
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function compareAndorsTrailResourceFormat(text1, text2) {
|
||||
var result = { isResource: true, class1: "ok", class2: "ok", messages: [] };
|
||||
var result = {
|
||||
isResource: true,
|
||||
class1: "ok",
|
||||
class2: "ok",
|
||||
messages: [],
|
||||
header: undefined,
|
||||
ds_english: undefined,
|
||||
ds_translated: undefined
|
||||
};
|
||||
|
||||
var header1 = findHeader(text1);
|
||||
if (!header1) { return { isResource: false }; }
|
||||
result.header = header1;
|
||||
|
||||
var header2 = findHeader(text2);
|
||||
if (!header2) { result.class2 = "red"; return result; }
|
||||
|
||||
compareAndorsTrailResourceHeader(result, "", header1, header2);
|
||||
if (result.class2 != "ok") { return result; }
|
||||
|
||||
var ds1 = new DataStore({});
|
||||
var ds2 = new DataStore({});
|
||||
ds1.deserialize(text1);
|
||||
@@ -163,42 +212,153 @@ function compareAndorsTrailResourceFormat(text1, text2) {
|
||||
var obj1 = obj;
|
||||
var obj2 = ds2.get(i);
|
||||
var id1 = obj1[header1._fields[0]];
|
||||
if (!obj2) { return pushMessage(result, "Row " + i + ": expected to find an object with id \"" + id1 + "\", but such row was found."); }
|
||||
if (!obj2) { return pushMessage(result, "Row " + i + ": expected to find an object with id \"" + id1 + "\", but such row was not found."); }
|
||||
var id2 = obj2[header1._fields[0]];
|
||||
if (id2 != id1) { return pushMessage(result, "Row " + i + ": Expected to find id \"" + id1 + "\", but found \"" + id2 + "\" instead."); }
|
||||
|
||||
compareAndorsTrailResourceRow(result, header1, id1, obj1, obj2);
|
||||
});
|
||||
|
||||
result.ds_english = ds1;
|
||||
result.ds_translated = ds2;
|
||||
return result;
|
||||
}
|
||||
|
||||
function applyChangeToObject(trans_obj, fieldName, newValue) {
|
||||
(new Function("x", "v", "x."+fieldName+" = v;"))(trans_obj, newValue);
|
||||
}
|
||||
|
||||
function appendEditRow(editTable, updateHook, trans_obj, id, fieldName, english_text, translated_text) {
|
||||
if (english_text.length <= 1) {
|
||||
// there's no meaningful text
|
||||
return;
|
||||
}
|
||||
var row_id = name+"__row";
|
||||
var cell_id = name+"__cell";
|
||||
var edit_id = name+"__edit";
|
||||
var cell = $("<span />").text(translated_text)
|
||||
.attr("id", cell_id)
|
||||
.attr("class", "clickToEdit");
|
||||
var editor = $("<textarea />").val(translated_text)
|
||||
.attr("id", cell_id)
|
||||
.hide();
|
||||
cell.click(function() {
|
||||
editor.val(cell.text());
|
||||
editor.show().focus();
|
||||
cell.hide();
|
||||
});
|
||||
editor.blur(function() {
|
||||
var new_text = editor.val();
|
||||
cell.text(new_text);
|
||||
editor.hide(); cell.show();
|
||||
applyChangeToObject(trans_obj, fieldName, new_text);
|
||||
updateHook();
|
||||
});
|
||||
editTable.append(
|
||||
$("<tr />")
|
||||
.attr("id", name+"__row")
|
||||
.append($("<td />").text(id))
|
||||
.append($("<td />").text(fieldName))
|
||||
.append($("<td />").text(english_text))
|
||||
.append($("<td />").append(cell).append(editor))
|
||||
);
|
||||
}
|
||||
|
||||
function clearEditTable() {
|
||||
var editTable = $("#editTranslation tbody");
|
||||
editTable.children().remove();
|
||||
$("#export_text").val("");
|
||||
}
|
||||
|
||||
function editTranslationHandler(data2, name, resourceComparison) {
|
||||
var updateHook = function () {
|
||||
var header = resourceComparison.header;
|
||||
var ds2 = resourceComparison.ds_translated;
|
||||
var text2 = ds2.serialize();
|
||||
data2.find("string[name=\"" + name + "\"]").text(text2);
|
||||
};
|
||||
return function () {
|
||||
clearEditTable();
|
||||
var editTable = $("#editTranslation tbody");
|
||||
var header = resourceComparison.header;
|
||||
var ds1 = resourceComparison.ds_english;
|
||||
var ds2 = resourceComparison.ds_translated;
|
||||
$.each(ds1.items, function(i, obj1) {
|
||||
var obj2 = ds2.get(i);
|
||||
var id = obj1["id"];
|
||||
var english_list = extractTranslatableFields(id, header, obj1);
|
||||
var translated_list = extractTranslatableFields(id, header, obj2);
|
||||
|
||||
$.each(english_list.fields, function(j, f1) {
|
||||
var f2 = translated_list.fields[j];
|
||||
appendEditRow(
|
||||
editTable, updateHook, obj2, id,
|
||||
f1.name, f1.value, f2.value
|
||||
);
|
||||
});
|
||||
});
|
||||
};
|
||||
}
|
||||
function initTranslationHandler(data1, data2, name) {
|
||||
var f = function() {
|
||||
if (!confirm("Create new translation unit in this resouce?")) {
|
||||
// if canceled
|
||||
return;
|
||||
}
|
||||
clearEditTable();
|
||||
// create new node in resource xml by copying english ver.
|
||||
var node1 = data1.find("string[name=\"" + name + "\"]");
|
||||
var text1 = node1.text();
|
||||
data2.find("resources").append($("<string name=\""+name+"\" />").text(text1));
|
||||
var resourceComparison = compareAndorsTrailResourceFormat(text1, text1);
|
||||
// repalce this handler with "edit" event handler (and etc.)
|
||||
var editHandler = editTranslationHandler(data2, name, resourceComparison);
|
||||
$(this).parent().attr("class","yellow");
|
||||
$(this).text("Edit").unbind('click', f).click(editHandler);
|
||||
$(this).click();
|
||||
}
|
||||
return f;
|
||||
}
|
||||
function exportHandler(data2) {
|
||||
var xmlString = function (data) {
|
||||
var xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n\n";
|
||||
xml += "<resources>\n";
|
||||
$.each(data.find("string"), function() {
|
||||
xml += "\t<string name=\""+$(this).attr("name")+"\">\n";
|
||||
xml += $(this).text();
|
||||
xml += "\n\t</string>\n\n";
|
||||
});
|
||||
xml += "</resources>\n";
|
||||
return xml;
|
||||
}
|
||||
return function () {
|
||||
$("#export_text").val(xmlString(data2));
|
||||
};
|
||||
}
|
||||
|
||||
function appendOutputRow(outputTable, name, data1, data2) {
|
||||
if ($("#" + name, outputTable).size() > 0) return;
|
||||
|
||||
var text1 = data1.find("string[name=\"" + name + "\"]").text();
|
||||
var text2 = data2.find("string[name=\"" + name + "\"]").text();
|
||||
var class1 = text1 ? "ok" : "red";
|
||||
var class2 = text2 ? "ok" : "red";
|
||||
var tdTranslated = $("<td />");
|
||||
if (text1 && text2) {
|
||||
var resourceComparison = compareAndorsTrailResourceFormat(text1, text2);
|
||||
if (resourceComparison.isResource) {
|
||||
class1 = resourceComparison.class1;
|
||||
class2 = resourceComparison.class2;
|
||||
if (resourceComparison.messages.length > 0) {
|
||||
var errorList = $("<ul />").attr("id", "validationWarnings");
|
||||
$.each(resourceComparison.messages, function(i, msg) {
|
||||
errorList.append($("<li />").text(msg));
|
||||
});
|
||||
var d = $("<span />").attr("id", "showValidationWarnings").text("Expand");
|
||||
d.click(function() {
|
||||
d.hide();
|
||||
errorList.show();
|
||||
});
|
||||
tdTranslated.append(d);
|
||||
tdTranslated.append(errorList);
|
||||
errorList.hide();
|
||||
if (text1) {
|
||||
if (text2) {
|
||||
var resourceComparison = compareAndorsTrailResourceFormat(text1, text2);
|
||||
if (resourceComparison.isResource) {
|
||||
class2 = resourceComparison.class2;
|
||||
if (resourceComparison.messages.length > 0 || class2 == "ok") {
|
||||
// yellow || ok
|
||||
var d = $("<span />").text("Edit");
|
||||
d.click(editTranslationHandler(data2, name, resourceComparison));
|
||||
tdTranslated.append(d);
|
||||
}
|
||||
}
|
||||
} else /* if (class2 == "red") */ {
|
||||
var d = $("<span />").text("Init");
|
||||
d.click(initTranslationHandler(data1, data2, name));
|
||||
tdTranslated.append(d);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -206,17 +366,16 @@ function appendOutputRow(outputTable, name, data1, data2) {
|
||||
$("<tr />")
|
||||
.attr("id", name)
|
||||
.append($("<td />").text(name))
|
||||
.append($("<td />").attr("class", class1))
|
||||
.append(tdTranslated.attr("class", class2))
|
||||
);
|
||||
}
|
||||
|
||||
function validateTranslation_(englishData) {
|
||||
$("#englishData").text(englishData);
|
||||
var compareData1 = $( englishData );
|
||||
var compareData2 = $( $("#compareToInput").val() );
|
||||
var compareData1 = $( $.parseXML(englishData) );
|
||||
var compareData2 = $( $.parseXML($("#compareToInput").val()) );
|
||||
|
||||
var resultTable = $("#validateResultContent table");
|
||||
var resultTable = $("#validateResultContent #result");
|
||||
var outputTable = $("tbody", resultTable );
|
||||
outputTable.empty();
|
||||
|
||||
@@ -229,9 +388,9 @@ function validateTranslation_(englishData) {
|
||||
|
||||
var sectionCount = $("tr", outputTable).size();
|
||||
var errors1 = sectionCount - $("td:nth-child(2).ok", outputTable).size();
|
||||
var errors2 = sectionCount - $("td:nth-child(3).ok", outputTable).size();;
|
||||
$("th #count1", resultTable).text( (errors1 > 0) ? " (" + errors1 + ")" : "" );
|
||||
$("th #count2", resultTable).text( (errors2 > 0) ? " (" + errors2 + ")" : "" );
|
||||
$("th #count2", resultTable).text( (errors1 > 0) ? " (" + errors1 + ")" : "" );
|
||||
|
||||
$("#export").click(exportHandler(compareData2));
|
||||
|
||||
$("#validateResultContent #loading").hide();
|
||||
$("#validateResultContent #result").show();
|
||||
@@ -244,7 +403,6 @@ function validateTranslation(englishData) {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function loadStep3() {
|
||||
var compareToContent = $("#compareToInput").val();
|
||||
if (compareToContent.length <= 0) {
|
||||
@@ -269,5 +427,12 @@ function startTranslationValidator() {
|
||||
loadResourceFile( $("#compareToExisting").val(), function(data) { $("#compareToInput").val(data); } );
|
||||
});
|
||||
$("#next2").button({ icons: {primary:'ui-icon-arrowthick-1-e'} }).click(loadStep3);
|
||||
$("#prev3").button({ icons: {primary:'ui-icon-arrowthick-1-w'} }).click(function() { stepLeft("#validateResult", "#validate2"); });
|
||||
$("#prev3").button({ icons: {primary:'ui-icon-arrowthick-1-w'} }).click(function() {
|
||||
if (confirm("Leaving the result page will discard any modifications on current resource. Leave anyway?")) {
|
||||
stepLeft("#validateResult", "#validate2");
|
||||
clearEditTable();
|
||||
}
|
||||
});
|
||||
$("#export").button({ icons: {primary:'ui-icon-arrowthick-1-e'} })
|
||||
}
|
||||
|
||||
|
||||
@@ -58,6 +58,7 @@ function DataStore(input) {
|
||||
alert("Could not find header row, cannot deserialize");
|
||||
return;
|
||||
}
|
||||
this.fieldList = header;
|
||||
this.items = deserializeObjectList(header, str);
|
||||
this.onDeserialized();
|
||||
}
|
||||
|
||||
@@ -47,6 +47,7 @@ input[readonly] { color: #888; }
|
||||
.validateStep h1 { font-size: 1.1em; }
|
||||
.validateStep h1 .number { font-size: 1.3em; font-weight: bold; text-shadow: rgba(0, 0, 0, 0.3) 1px 1px 1px; }
|
||||
.validateStep .buttons { padding-top: 10px; }
|
||||
|
||||
#validateResultContent table { border-collapse: collapse; margin-bottom: 1ex; }
|
||||
#validateResultContent table th,td { border: 1px #ccc solid; text-align: left; }
|
||||
#validateResultContent table tbody tr:nth-child(even) { background-color: #eee }
|
||||
@@ -55,3 +56,14 @@ input[readonly] { color: #888; }
|
||||
#validateResultContent table .yellow { background-color: yellow; }
|
||||
#showValidationWarnings { cursor: pointer; }
|
||||
#showValidationWarnings:hover { background-color: #d7d7ff; }
|
||||
|
||||
.clickToEdit { cursor: pointer; }
|
||||
.clickToEdit:hover { background-color: #d7d7ff; }
|
||||
|
||||
#validateResultContent { width: 400px; float: left;d }
|
||||
#editorForms { width: 670px; float: right;}
|
||||
#editorForms #translation_edit_list { table-layout: fixed; }
|
||||
#editorForms #translation_edit_list tr:nth-child(1) { width: 17em }
|
||||
#editorForms #translation_edit_list tr:nth-child(2) { width: 8em }
|
||||
#editorForms #translation_edit_list textarea { width: 100%; height: 8em; }
|
||||
|
||||
|
||||
@@ -61,22 +61,46 @@
|
||||
|
||||
<div id="validateResult" class="validateStep" style="display: none;">
|
||||
<h1><span class="number">3.</span> Results</h1>
|
||||
|
||||
<div id="validateResultContent">
|
||||
<div id="loading">Loading & validating</div>
|
||||
<table id="result">
|
||||
<thead><tr>
|
||||
<th>id</th>
|
||||
<th>English<span id="count1" /></th>
|
||||
<th>Translated<span id="count2" /></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
<div>
|
||||
<div id="validateResultContent">
|
||||
<fieldset class="fieldSet">
|
||||
<legend>Validation Result</legend>
|
||||
<div id="loading">Loading & validating</div>
|
||||
<table id="result">
|
||||
<thead><tr>
|
||||
<th>id</th>
|
||||
<th>Result<span id="count2" /></th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div id="editorForms">
|
||||
<fieldset class="fieldSet" id="editTranslation">
|
||||
<legend>Edit translation</legend>
|
||||
<table id="translation_edit_list">
|
||||
<thead><tr>
|
||||
<th>id</th>
|
||||
<th>field</th>
|
||||
<th>English</th>
|
||||
<th>Translated</th>
|
||||
</tr></thead>
|
||||
<tbody>
|
||||
</tbody>
|
||||
</table>
|
||||
</fieldset>
|
||||
</div>
|
||||
<div style="floar:clear;"></div>
|
||||
</div>
|
||||
|
||||
<div class="buttons">
|
||||
<span id="prev3">Back</span>
|
||||
<span id="export">Export</span>
|
||||
</div>
|
||||
<div class="export_area">
|
||||
<p>Translated Content:</p>
|
||||
<textarea id="export_text" rows=8 cols=80></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user