Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Added support for Yahoo Resource Bundles: YRB->JSON converter, integr…

…ation into build scripts.
  • Loading branch information...
commit d3db9bfc4836e6978aa8a4f1602aac81821f5378 1 parent 422b238
@NorbertLindenberg NorbertLindenberg authored
View
41 componentbuild/lib/yrb2json/yrb2json-console.js
@@ -0,0 +1,41 @@
+/**
+ * Converts resource bundles provided in YRB format into JSON format.
+ *
+ * YRB format is documented at
+ * http://developer.yahoo.com/yui/3/intl/index.html#yrb
+ *
+ * Run as:
+ * java -jar js.jar yrb2json-console.js yrb2json.js inputFile outputFile
+ *
+ */
+
+/*jslint rhino: true, onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
+/*global java: false, yrb2json: false */
+
+if (arguments.length !== 3) {
+ print("Usage: java -jar yrb2json-console.js yrb2json.js inputFile outputFile");
+ java.lang.System.exit(1);
+}
+
+load(arguments[0]);
+
+(function (inputFile, outputFile) {
+
+ var yrb, json, writer;
+
+ print("Converting " + inputFile + " to " + outputFile);
+
+ try {
+ yrb = readFile(inputFile, "utf-8");
+ json = yrb2json.convert(yrb);
+
+ outputFile = outputFile.replace(/\//, java.io.File.separator);
+ writer = new java.io.OutputStreamWriter(new java.io.FileOutputStream(outputFile), "utf-8");
+ writer.write(json, 0, json.length);
+ writer.flush();
+ } catch (e) {
+ print(e.toString());
+ java.lang.System.exit(1);
+ }
+}(arguments[1], arguments[2]));
+
View
90 componentbuild/lib/yrb2json/yrb2json-test.js
@@ -0,0 +1,90 @@
+/**
+ * Tests yrb2json.
+ *
+ * Run as:
+ * java -jar js.jar yrb2json-test.js
+ */
+
+/*jslint rhino: true, onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
+/*global java: false, yrb2json: false */
+
+load("yrb2json.js");
+
+(function () {
+
+ var errors, source, expectedJSON, actual, j, invalidInput, input;
+
+ errors = 0;
+ print("Starting yrb2json test.");
+
+ // expected JSON output for test.pres file
+ expectedJSON =
+ '{"MONTHS_SHORT":"1월, 2월, 3월, 4월, 5월, 6월, 7월, 8월, 9월, 10월, 11월, 12월",' +
+ '"MONTHS_LONG":"1월\\n2월\\n3월\\n4월\\n5월\\n6월\\n7월\\n8월\\n9월\\n10월\\n11월\\n12월",' +
+ '"ESCAPES":" \\\\\\n\\t# ",' +
+ '"KEY WITH SPACES":"1월, 2월, 3월, 4월, 5월, 6월, 7월, 8월, 9월, 10월, 11월, 12월",' +
+ '"WITH QUOTES \\"":"\\"",' +
+ '"EMPTY":"",' +
+ '"EMPTY HEREDOC":""}';
+
+ // test.pres has valid data
+ source = readFile("yrb2json-test.pres");
+ if (source) {
+ try {
+ actual = yrb2json.convert(source);
+ if (actual !== expectedJSON) {
+ print("Wrong result for good input:");
+ print("actual : " + actual);
+ print("expected: " + expectedJSON);
+ print(actual.length + "/" + expectedJSON.length);
+ for (j = 0; j < Math.min(actual.length, expectedJSON.length); j++) {
+ if (actual.charAt(j) !== expectedJSON.charAt(j)) {
+ print(j);
+ print(actual.charAt(j) + "/" + expectedJSON.charAt(j));
+ }
+ }
+ errors++;
+ }
+ } catch (e1) {
+ print(e1.toString());
+ errors++;
+ }
+ } else {
+ print("Empty test data - make sure to run test from yrb2json directory.");
+ errors++;
+ }
+
+ invalidInput = {
+ "&": "Missing '=' in line &.",
+ "=": "Empty key not allowed.",
+ "= value": "Empty key not allowed.",
+ "key = <<<": "Incomplete heredoc with id .",
+ "key = value\\": "Illegal escape sequence: unaccompanied \\",
+ "key = v\\u0061lue": "Illegal escape sequence: \\u",
+ "key = value\\\"": "Illegal escape sequence: \\\"",
+ "k\\#ey = value": "Backslash not allowed in key: k\\#ey",
+ "key = <<< end\nend ": "Incomplete heredoc with id end.",
+ "key = <<< end": "Incomplete heredoc with id end.",
+ "key = <<< end\n": "Incomplete heredoc with id end.",
+ "key = <<< end\nend; ": "Incomplete heredoc with id end."
+ };
+ for (input in invalidInput) {
+ if (invalidInput.hasOwnProperty(input)) {
+ try {
+ actual = yrb2json.convert(input);
+ } catch (e2) {
+ if (e2.message !== invalidInput[input]) {
+ print("Didn't get expected exception for " + input + "; exception: " + e2.toString());
+ errors++;
+ }
+ continue;
+ }
+ print("Didn't get expected exception for " + input);
+ errors++;
+ }
+ }
+ print(errors + (errors === 1 ? " error." : " errors."));
+ print("Finished yrb2json test.");
+ java.lang.System.exit(errors);
+}());
+
View
44 componentbuild/lib/yrb2json/yrb2json-test.pres
@@ -0,0 +1,44 @@
+# This is a test data file.
+ # It does not represent useful data.
+ # And it mixes languages
+#
+
+# note that whitespace is stripped, so the value of the
+# following entry really means
+# "1월, 2월, 3월, 4월, 5월, 6월, 7월, 8월, 9월, 10월, 11월, 12월"
+
+MONTHS_SHORT = 1월, 2월, 3월, 4월, 5월, 6월, 7월, 8월, 9월, 10월, 11월, 12월
+
+# this is a twelve-line value
+
+MONTHS_LONG = <<< end
+1월
+2월
+3월
+4월
+5월
+6월
+# yes there are comments within heredocs
+ # and they don't have to start at the beginning of the line
+ # and they can use a tab as whitespace
+7월
+8월
+9월
+10월
+11월
+12월
+end;
+
+# this entry has all escape sequences, with an escaped space at the end
+
+ESCAPES = \ \\\n\t\#\
+
+KEY WITH SPACES = 1월, 2월, 3월, 4월, 5월, 6월, 7월, 8월, 9월, 10월, 11월, 12월
+
+WITH QUOTES " = "
+
+EMPTY =
+
+EMPTY HEREDOC = <<< end
+end
+
View
183 componentbuild/lib/yrb2json/yrb2json.js
@@ -0,0 +1,183 @@
+/**
+ * Converts resource bundles provided in YRB format into JSON format.
+ *
+ * YRB format is documented at
+ * http://developer.yahoo.com/yui/3/intl/index.html#yrb
+ */
+
+/*jslint onevar: true, undef: true, nomen: true, eqeqeq: true, bitwise: true, regexp: true, newcap: true, immed: true */
+
+var yrb2json = (function() {
+
+var result = {};
+
+function isCommentLine(line) {
+ return (line.match(/^[ \t]*#/) !== null);
+}
+
+function isWhitespaceLine(line) {
+ return (line.match(/^[ \t]*$/) !== null);
+}
+
+function isWhitespaceChar(c) {
+ return c === " " || c === "\t";
+}
+
+/**
+ * Checks whether the given key is not empty and doesn't contain backslashes.
+ */
+function checkKey(key) {
+ if (key.length === 0) {
+ throw new Error("Empty key not allowed.");
+ }
+ if (key.indexOf("\\") >= 0) {
+ throw new Error("Backslash not allowed in key: " + key);
+ }
+}
+
+function trimWhitespace(s) {
+ var start = 0, end = s.length;
+ while (start < s.length && isWhitespaceChar(s.charAt(start))) {
+ start++;
+ }
+ while (end > start && isWhitespaceChar(s.charAt(end - 1))) {
+ end--;
+ }
+ return s.substring(start, end);
+}
+
+/**
+ * Unescapes all escape sequences that occur in s. Optionally trims whitespace
+ * from beginning and end of string, taking care not to trim whitespace
+ * that's part of an escape sequence.
+ */
+function unescapeValue(s, trim) {
+ var pos, start, end, result;
+
+ pos = s.indexOf("\\");
+ if (pos >= 0) {
+ result = "";
+ start = 0;
+ if (trim) {
+ while (isWhitespaceChar(s.charAt(start))) {
+ start++;
+ }
+ }
+ while (pos >= 0) {
+ result += s.substring(start, pos);
+ if (pos + 1 >= s.length) {
+ throw new Error("Illegal escape sequence: unaccompanied \\");
+ }
+ switch (s.charAt(pos + 1)) {
+ case "\\":
+ result += "\\";
+ break;
+ case "n":
+ result += "\n";
+ break;
+ case "t":
+ result += "\t";
+ break;
+ case " ":
+ result += " ";
+ break;
+ case "#":
+ result += "#";
+ break;
+ default:
+ throw new Error("Illegal escape sequence: \\" + s.charAt(pos + 1));
+ }
+ start = pos + 2;
+ pos = s.indexOf("\\", start);
+ }
+ end = s.length;
+ if (trim) {
+ while (end > start && isWhitespaceChar(s.charAt(end - 1))) {
+ end--;
+ }
+ }
+ result += s.substring(start, end);
+ } else {
+ result = trim ? trimWhitespace(s) : s;
+ }
+ return result;
+}
+
+/**
+ * Convert a string into JSON representation. Should use stringify once Rhino gets it.
+ */
+function jsonString(s) {
+ return "\"" + s.replace(/\\/g, "\\\\").replace(/\"/g, "\\\"").replace(/\n/g, "\\n").replace(/\t/g, "\\t") + "\"";
+}
+
+function jsonKeyValue(key, value) {
+ return jsonString(key) + ":" + jsonString(value);
+}
+
+/**
+ * Converts the contents of one YRB file into corresponding JSON content.
+ */
+result.convert = function (source) {
+ var lines, properties, hereDocId, i, line, equalsPos, key, remainder,
+ match, value;
+
+ if (source.length > 0 && source.charAt(0) === "\ufeff") {
+ // remove BOM
+ source = source.substring(1);
+ }
+ lines = source.replace(/\r\n/g, '\n').replace(/\r/g, '\n').split('\n');
+
+ properties = [];
+ hereDocId = null; // null indicates we're not inside heredoc section
+
+ for (i = 0; i < lines.length; i++) {
+ line = lines[i];
+ if (isCommentLine(line)) {
+ continue;
+ }
+ if (hereDocId === null) {
+ if (isWhitespaceLine(line)) {
+ continue;
+ }
+
+ // extract the key
+ equalsPos = line.indexOf("=");
+ if (equalsPos < 0) {
+ throw new Error("Missing '=' in line " + line + ".");
+ }
+ key = trimWhitespace(line.substring(0, equalsPos));
+ checkKey(key);
+
+ // decide whether it's simple or heredoc form
+ remainder = line.substring(equalsPos + 1);
+ if ((match = remainder.match(/^[ \t]*<<</)) !== null) {
+ hereDocId = trimWhitespace(remainder.substring(match[0].length));
+ value = null;
+ } else {
+ value = unescapeValue(remainder, true);
+ properties.push(jsonKeyValue(key, value));
+ }
+ } else {
+ if (line === hereDocId || line === hereDocId + ";") {
+ value = value || "";
+ properties.push(jsonKeyValue(key, value));
+ hereDocId = null;
+ } else {
+ if (value === null) {
+ value = unescapeValue(line, false);
+ } else {
+ value += "\n" + unescapeValue(line, false);
+ }
+ }
+ }
+ }
+ if (hereDocId !== null) {
+ throw new Error("Incomplete heredoc with id " + hereDocId + ".");
+ }
+ return '{' + properties.join(",") + "}";
+};
+
+return result;
+
+}());
+
View
26 componentbuild/shared/macrolib.xml
@@ -184,12 +184,28 @@
<property name="@{module}@{lang}_fullname" value="@{module}_@{lang}" />
</else>
</if>
+
+ <if>
+ <available file="@{dir}/${@{module}@{lang}_fullname}.pres" />
+ <then>
+ <mkdir dir="${component.langbuilddir}" />
+ <java jar="${rhino.jar}" fork="true" failonerror="true">
+ <jvmarg value="-Dfile.encoding=utf-8" />
+ <arg file="${yrb2jsonconsole.js}" />
+ <arg file="${yrb2jsonsrc.js}" />
+ <arg file="@{dir}/${@{module}@{lang}_fullname}.pres" />
+ <arg file="${component.langbuilddir}/${@{module}@{lang}_fullname}.js" />
+ </java>
+ <echo level="info">Wrapping ${component.langbuilddir}/${@{module}@{lang}_fullname}.js in YUI.add, Y.Intl.add</echo>
+ <loadfile srcfile="${component.langbuilddir}/${@{module}@{lang}_fullname}.js" property="@{module}@{lang}-strs-loaded" encoding="utf-8" />
+ </then>
+ <else>
+ <echo level="info">Wrapping @{dir}/${@{module}@{lang}_fullname}.js in YUI.add, Y.Intl.add</echo>
+ <loadfile srcfile="@{dir}/${@{module}@{lang}_fullname}.js" property="@{module}@{lang}-strs-loaded" encoding="utf-8" />
+ </else>
+ </if>
- <loadfile srcfile="@{dir}/${@{module}@{lang}_fullname}.js" property="@{module}@{lang}-strs-loaded" />
-
- <echo level="info">Wrapping @{dir}/${@{module}@{lang}_fullname}.js in YUI.add, Y.Intl.add</echo>
-
- <copy file="${builddir}/files/langtemplate.txt" tofile="@{dest}/${@{module}@{lang}_fullname}.js" overwrite="true">
+ <copy file="${builddir}/files/langtemplate.txt" tofile="@{dest}/${@{module}@{lang}_fullname}.js" overwrite="true" outputencoding="utf-8">
<filterset>
<filter token="LANG" value="@{lang}" />
<filter token="LANG_MODULE" value="lang/${@{module}@{lang}_fullname}" />
View
5 componentbuild/shared/properties.xml
@@ -26,6 +26,8 @@
<property name="jslintconsole.js" location="${builddir}/lib/jslint/jslint-console.js" />
<property name="jslintsrc.js" location="${builddir}/lib/jslint/fulljslint.js" />
<property name="yuicompressor.jar" location="${builddir}/lib/yuicompressor/yuicompressor-2.4.jar" />
+ <property name="yrb2jsonconsole.js" location="${builddir}/lib/yrb2json/yrb2json-console.js" />
+ <property name="yrb2jsonsrc.js" location="${builddir}/lib/yrb2json/yrb2json.js" />
<!-- YUI Compressor arguments for JS and CSS files -->
<property name="yuicompressor.css.args" value="--type css --line-break 6000" />
@@ -140,6 +142,9 @@
<!-- The temporary working directory used by the build process -->
<property name="workingdir" location="${component.builddir}/ant" />
+
+ <!-- Directory for language resource bundles in JSON format derived from YRB format -->
+ <property name="component.langbuilddir" value="${component.basedir}/build_lang_tmp" />
<!-- The file which defines the default targets for the particular type of component (module, rollup, css) -->
<condition property="targetbase" value="rollup" else="module">
Please sign in to comment.
Something went wrong with that request. Please try again.