diff --git a/build/js/live-editor.output_sql.js b/build/js/live-editor.output_sql.js index 75122ab95..3f4aba724 100644 --- a/build/js/live-editor.output_sql.js +++ b/build/js/live-editor.output_sql.js @@ -1,7 +1,11 @@ this["Handlebars"] = this["Handlebars"] || {}; this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {}; this["Handlebars"]["templates"]["sql-results"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { - return "

Database Schema

\n"; + var helper; + + return "

" + + container.escapeExpression(((helper = (helper = helpers.databaseMsg || (depth0 != null ? depth0.databaseMsg : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"databaseMsg","hash":{},"data":data}) : helper))) + + "

\n"; },"3":function(container,depth0,helpers,partials,data) { var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}); @@ -41,7 +45,11 @@ this["Handlebars"]["templates"]["sql-results"] = Handlebars.template({"1":functi },"9":function(container,depth0,helpers,partials,data) { return "(PK)"; },"11":function(container,depth0,helpers,partials,data) { - return "

Results

\n"; + var helper; + + return "

" + + container.escapeExpression(((helper = (helper = helpers.resultsMsg || (depth0 != null ? depth0.resultsMsg : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"resultsMsg","hash":{},"data":data}) : helper))) + + "

\n"; },"13":function(container,depth0,helpers,partials,data) { var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); @@ -797,55 +805,91 @@ window.SQLOutput = Backbone.View.extend({ * error message. SQLlite error messages aren't always very descriptive, * this should make common syntax errors easier to understand. */ - getErrorMessage: function getErrorMessage(errorMessage, statement) { - errorMessage = errorMessage || ""; + getErrorMessage: function getErrorMessage(sqliteError, statement) { + sqliteError = sqliteError || ""; statement = statement || ""; statement = statement.toUpperCase(); - var isSyntaxError = errorMessage.indexOf(": syntax error") > -1; + var errorMessage = sqliteError; + + // First, we translate SQLite errors into friendly i18n-able messages + + var colTypesError = sqliteError.indexOf("valid column types") > -1; + if (colTypesError) { + errorMessage = i18n._("Please use one of the valid column types " + "when creating a table: ") + "\"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\"."; + } + var uniqStr = "UNIQUE constraint failed:"; + var uniqError = sqliteError.indexOf(uniqStr) > -1; + if (uniqError) { + var colName = sqliteError.split(uniqStr)[1].trim(); + errorMessage = i18n._("\"UNIQUE\" constraint failed on column \"%(colName)s\".", { colName: colName }); + } + var notNullStr = "NOT NULL constraint failed:"; + var notNullError = sqliteError.indexOf(notNullStr) > -1; + if (notNullError) { + var colName = sqliteError.split(notNullStr)[1].trim(); + errorMessage = i18n._("\"NOT NULL\" constraint failed on column \"%(colName)s\".", { colName: colName }); + } + var dupColStr = "duplicate column name:"; + var dupColError = sqliteError.indexOf(dupColStr) > -1; + if (dupColError) { + var colName = errorMessage.split(dupColStr)[1].trim(); + errorMessage = i18n._("You have multiple columns named \"%(colName)s\" - " + "column names must be unique.", { colName: colName }); + } + var unknownColStr = "no such column:"; + var unknownColError = sqliteError.indexOf(unknownColStr) > -1; + if (unknownColError) { + var colName = sqliteError.split(unknownColStr)[1].trim(); + errorMessage = i18n._("We can't find the column named \"%(colName)s\".", { colName: colName }); + } + var noTablesError = sqliteError.indexOf("no tables specified") > -1; + if (noTablesError) { + errorMessage = i18n._("You didn't specify any tables for your \"SELECT\"."); + } + // Generic syntax error messages take form: 'near \"%T\": syntax error' + var syntaxErrStr = ": syntax error"; + var isSyntaxError = sqliteError.indexOf(syntaxErrStr) > -1; if (isSyntaxError) { - errorMessage = i18n._("There's a syntax error " + errorMessage.split(":")[0]); + var nearPhrase = errorMessage.split(syntaxErrStr)[0]; + errorMessage = i18n._("There's a syntax error near %(nearThing)s.", { nearThing: nearPhrase.substr(5) }); } - // Possible SELECT with missing FROM - if (errorMessage.indexOf("no such column:") !== -1 && statement.indexOf("SELECT") !== -1 && statement.indexOf("FROM") === -1) { - errorMessage += ". " + i18n._("Are you missing a FROM clause?"); - // Possible INSERT with missing INTO + // Now that we've translated the base error messages, + // we add on additional helper messages for common mistakes + if (unknownColError && statement.indexOf("SELECT") !== -1 && statement.indexOf("FROM") === -1) { + errorMessage += " " + i18n._("Are you perhaps missing a \"FROM\" clause?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && statement.indexOf("VALUES") !== -1 && statement.indexOf("INTO") === -1) { - errorMessage += ". " + i18n._("Are you missing the INTO keyword?"); - // Possible INSERT INTO with missing VALUES + errorMessage += " " + i18n._("Are you missing the \"INTO\" keyword?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && statement.indexOf("INTO") !== -1 && statement.indexOf("VALUES") === -1) { - errorMessage += ". " + i18n._("Are you missing the VALUES keyword?"); + errorMessage += " " + i18n._("Are you missing the \"VALUES\" keyword?"); } else if (statement.indexOf("INTERGER") !== -1) { - errorMessage += ". " + i18n._(" Is INTEGER spelled correctly?"); + errorMessage += " " + i18n._("Is \"INTEGER\" spelled correctly?"); } else if (isSyntaxError && statement.indexOf("CREATE") !== -1 && statement.search(/CREATE TABLE \w+\s\w+/) > -1) { - errorMessage += ". " + i18n._("You can't have a space in your table name."); + errorMessage += " " + i18n._("You can't have a space in your table name."); } else if (isSyntaxError && statement.indexOf("CREATE TABLE (") > -1) { - errorMessage += ". " + i18n._("Are you missing the table name?"); + errorMessage += " " + i18n._("Are you missing the table name?"); } else if (isSyntaxError && statement.indexOf("PRIMARY KEY INTEGER") !== -1) { - errorMessage += ". " + i18n._("Did you mean to put PRIMARY KEY after INTEGER?"); + errorMessage += " " + i18n._("Perhaps you meant to put \"PRIMARY KEY\" after \"INTEGER\"?"); } else if (isSyntaxError && statement.indexOf("(") !== -1 && statement.indexOf(")") === -1) { - errorMessage += ". " + i18n._("Are you missing a parenthesis?"); + errorMessage += " " + i18n._("Are you missing a parenthesis?"); } else if (isSyntaxError && statement.indexOf("CREATE") !== -1 && statement.indexOf("TABLE") === -1 && (statement.indexOf("INDEX") === -1 || statement.indexOf("TRIGGER") === -1 || statement.indexOf("VIEW") === -1)) { - errorMessage += ". " + i18n._("You may be missing what to create. For " + "example, CREATE TABLE..."); + errorMessage += " " + i18n._("You may be missing what to create. For " + "example, \"CREATE TABLE...\""); } else if (isSyntaxError && statement.indexOf("UPDATE") !== -1 && statement.indexOf("SET") === -1) { - errorMessage += ". " + i18n._("Are you missing the SET keyword?"); + errorMessage += " " + i18n._("Are you missing the \"SET\" keyword?"); } else if (isSyntaxError && statement.search(/[^SUM]\s*\(.*\)\n*\s*\w+/) > -1 || statement.search(/\n+\s*SELECT/) > -1 || statement.search(/\)\n+\s*INSERT/) > -1) { - errorMessage += ". " + i18n._("Do you have a semi-colon after each statement?"); + errorMessage += " " + i18n._("Do you have a semi-colon after each statement?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && statement.search(/[^INSERT],\d*\s*[a-zA-Z]+/) > -1) { - errorMessage += ". " + i18n._("Are you missing quotes around text values?"); + errorMessage += " " + i18n._("Are you missing quotes around text values?"); } else if (isSyntaxError && statement.search(/,\s*\)/) > -1) { - errorMessage += ". " + i18n._("Do you have an extra comma?"); + errorMessage += " " + i18n._("Do you have an extra comma?"); } else if (isSyntaxError && statement.indexOf("INSERT,") > -1) { - errorMessage += ". " + i18n._("There shouldn't be a comma after INSERT."); - } else if (errorMessage.indexOf("column types") > -1 && statement.search(/(\w+\s*,\s*((TEXT)|(INTEGER))+)/) > -1) { - errorMessage += ". " + i18n._("Do you have an extra comma between the name and type?"); - } else if (errorMessage.indexOf("column types") > -1 && statement.search(/(\w+\s+\w+\s*((TEXT)|(INTEGER)|(REAL))+)/) > -1) { + errorMessage += " " + i18n._("There shouldn't be a comma after \"INSERT\"."); + } else if (colTypesError && statement.search(/(\w+\s*,\s*((TEXT)|(INTEGER))+)/) > -1) { + errorMessage += " " + i18n._("Do you have an extra comma between the name and type?"); + } else if (colTypesError && statement.search(/(\w+\s+\w+\s*((TEXT)|(INTEGER)|(REAL))+)/) > -1) { errorMessage = i18n._("You can't have a space in your column name."); - } else if (errorMessage.indexOf("UNIQUE constraint failed") !== -1) { - errorMessage += ". " + i18n._("Are you specifying a different value for each row?"); - } else if (errorMessage.indexOf("duplicate column name:") !== -1) { - errorMessage = i18n._("You have multiple columns named `%(name)s` - " + "column names must be unique.", { name: errorMessage.split(":")[1].trim() }); + } else if (uniqError) { + errorMessage += " " + i18n._("Are you specifying a different value for each row?"); } return errorMessage; }, @@ -1022,7 +1066,9 @@ window.SQLOutput = Backbone.View.extend({ var output = Handlebars.templates["sql-results"]({ tables: tables, - results: results + results: results, + databaseMsg: i18n._("Database Schema"), + resultsMsg: i18n._("Query results") }); var doc = this.getDocument(); diff --git a/build/js/live-editor.tooltips.js b/build/js/live-editor.tooltips.js index e50591ecc..626d2652a 100644 --- a/build/js/live-editor.tooltips.js +++ b/build/js/live-editor.tooltips.js @@ -2027,7 +2027,9 @@ TooltipEngine.classes.colorPicker = TooltipBase.extend({ this.$el = $(Handlebars.templates["mediapicker-modal"]({ imagesDir: this.options.imagesDir, soundsDir: this.options.soundsDir, - classes: this.options.files + classes: this.options.files, + closeMsg: i18n._("Close"), + okMsg: i18n._("Ok") })); this.$el.appendTo("body").hide(); }, @@ -2125,7 +2127,7 @@ TooltipEngine.classes.colorPicker = TooltipBase.extend({ render: function render() { var self = this; - this.$el = $(Handlebars.templates["mediapicker-preview"]({ isAudio: false })).appendTo("body").hide(); + this.$el = $(Handlebars.templates["mediapicker-preview"]({ isAudio: false, pickMsg: i18n._("Pick file:") })).appendTo("body").hide(); this.$(".thumb").on("load", function () { $(this).closest(".thumb-shell").find(".thumb-error").hide(); @@ -2226,7 +2228,7 @@ TooltipEngine.classes.colorPicker = TooltipBase.extend({ render: function render() { var self = this; - this.$el = $(Handlebars.templates["mediapicker-preview"]({ isAudio: true })).appendTo("body").hide(); + this.$el = $(Handlebars.templates["mediapicker-preview"]({ isAudio: true, pickMsg: i18n._("Pick file:") })).appendTo("body").hide(); this.$("button").on("click", function () { self.modal.show(); @@ -2789,20 +2791,17 @@ this["Handlebars"]["templates"]["mediapicker-preview"] = Handlebars.template({"1 },"5":function(container,depth0,helpers,partials,data) { return " \n
\n"; },"7":function(container,depth0,helpers,partials,data) { - return " \n
\n \n
\n
\n"; -},"9":function(container,depth0,helpers,partials,data) { - return "Pick file:"; + return " \n
\n \n
\n
\n"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), buffer = - "
\n
\n \n" + + "\">\n
\n\n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isAudio : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(7, data, 0),"data":data})) != null ? stack1 : "") - + "\n \n
\n
\n
"; + + "\n \n
\n
\n"; },"useData":true});; this["Handlebars"] = this["Handlebars"] || {}; this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {}; @@ -2925,25 +2924,18 @@ this["Handlebars"]["templates"]["mediapicker-modal"] = Handlebars.template({"1": + "\">" + alias3(((helper = (helper = helpers.groupName || (depth0 != null ? depth0.groupName : depth0)) != null ? helper : alias2),(typeof helper === "function" ? helper.call(alias1,{"name":"groupName","hash":{},"data":data}) : helper))) + "\n"; -},"19":function(container,depth0,helpers,partials,data) { - return "Close"; -},"21":function(container,depth0,helpers,partials,data) { - return "Ok"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=helpers.blockHelperMissing, buffer = - "
\n
\n\n
\n \n \n
\n"; },"useData":true,"useDepths":true});; (function () { var ESC = 27; diff --git a/build/js/live-editor.ui.js b/build/js/live-editor.ui.js index 7f53695bb..b95decc6f 100644 --- a/build/js/live-editor.ui.js +++ b/build/js/live-editor.ui.js @@ -2,29 +2,21 @@ this["Handlebars"] = this["Handlebars"] || {}; this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {}; this["Handlebars"]["templates"]["tipbar"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { return "×"; -},"3":function(container,depth0,helpers,partials,data) { - return "Previous error"; -},"5":function(container,depth0,helpers,partials,data) { - return "Next error"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=helpers.blockHelperMissing, alias5=container.escapeExpression, buffer = + var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression, buffer = "
\n
\n
\n\n
\n \n
" - + alias5(((helper = (helper = helpers.ohNoesMsg || (depth0 != null ? depth0.ohNoesMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"ohNoesMsg","hash":{},"data":data}) : helper))) + return buffer + "\n
" + + alias4(((helper = (helper = helpers.ohNoesMsg || (depth0 != null ? depth0.ohNoesMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"ohNoesMsg","hash":{},"data":data}) : helper))) + "
\n
\n \n
\n \n \n \n \n \n \n \n
\n
\n
"; + + alias4(((helper = (helper = helpers.showMeMsg || (depth0 != null ? depth0.showMeMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"showMeMsg","hash":{},"data":data}) : helper))) + + "
\n
\n \n \n \n \n \n \n \n
\n \n"; },"useData":true});; /** * This is called tipbar for historical reasons. @@ -46,7 +38,9 @@ window.TipBar = Backbone.View.extend({ this.$overlay = $("
").appendTo(this.$el); this.$el.append(Handlebars.templates["tipbar"]({ ohNoesMsg: i18n._("Oh noes!"), - showMeMsg: i18n._("Show me where") + showMeMsg: i18n._("Show me where"), + prevMsg: i18n._("Previous error"), + nextMsg: i18n._("Next error") })); this.$bar = this.$el.find(".tipbar"); }, diff --git a/build/tmpl/mediapicker-modal.js b/build/tmpl/mediapicker-modal.js index 905959ced..86315d30f 100644 --- a/build/tmpl/mediapicker-modal.js +++ b/build/tmpl/mediapicker-modal.js @@ -119,23 +119,16 @@ this["Handlebars"]["templates"]["mediapicker-modal"] = Handlebars.template({"1": + "\">" + alias3(((helper = (helper = helpers.groupName || (depth0 != null ? depth0.groupName : depth0)) != null ? helper : alias2),(typeof helper === "function" ? helper.call(alias1,{"name":"groupName","hash":{},"data":data}) : helper))) + "\n"; -},"19":function(container,depth0,helpers,partials,data) { - return "Close"; -},"21":function(container,depth0,helpers,partials,data) { - return "Ok"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data,blockParams,depths) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=helpers.blockHelperMissing, buffer = - "
\n
\n\n
\n \n \n
\n"; },"useData":true,"useDepths":true});; \ No newline at end of file diff --git a/build/tmpl/mediapicker-preview.js b/build/tmpl/mediapicker-preview.js index e10c6d464..d33da8c18 100644 --- a/build/tmpl/mediapicker-preview.js +++ b/build/tmpl/mediapicker-preview.js @@ -7,18 +7,15 @@ this["Handlebars"]["templates"]["mediapicker-preview"] = Handlebars.template({"1 },"5":function(container,depth0,helpers,partials,data) { return " \n
\n"; },"7":function(container,depth0,helpers,partials,data) { - return " \n
\n \n
\n
\n"; -},"9":function(container,depth0,helpers,partials,data) { - return "Pick file:"; + return " \n
\n \n
\n
\n"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), buffer = - "
\n
\n \n" + + "\">\n
\n\n" + ((stack1 = helpers["if"].call(alias1,(depth0 != null ? depth0.isAudio : depth0),{"name":"if","hash":{},"fn":container.program(5, data, 0),"inverse":container.program(7, data, 0),"data":data})) != null ? stack1 : "") - + "\n \n
\n
\n
"; + + "\n \n
\n
\n"; },"useData":true});; \ No newline at end of file diff --git a/build/tmpl/sql-results.js b/build/tmpl/sql-results.js index eac2faf6e..a49c480f2 100644 --- a/build/tmpl/sql-results.js +++ b/build/tmpl/sql-results.js @@ -1,7 +1,11 @@ this["Handlebars"] = this["Handlebars"] || {}; this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {}; this["Handlebars"]["templates"]["sql-results"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { - return "

Database Schema

\n"; + var helper; + + return "

" + + container.escapeExpression(((helper = (helper = helpers.databaseMsg || (depth0 != null ? depth0.databaseMsg : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"databaseMsg","hash":{},"data":data}) : helper))) + + "

\n"; },"3":function(container,depth0,helpers,partials,data) { var stack1, helper, alias1=depth0 != null ? depth0 : (container.nullContext || {}); @@ -41,7 +45,11 @@ this["Handlebars"]["templates"]["sql-results"] = Handlebars.template({"1":functi },"9":function(container,depth0,helpers,partials,data) { return "(PK)"; },"11":function(container,depth0,helpers,partials,data) { - return "

Results

\n"; + var helper; + + return "

" + + container.escapeExpression(((helper = (helper = helpers.resultsMsg || (depth0 != null ? depth0.resultsMsg : depth0)) != null ? helper : helpers.helperMissing),(typeof helper === "function" ? helper.call(depth0 != null ? depth0 : (container.nullContext || {}),{"name":"resultsMsg","hash":{},"data":data}) : helper))) + + "

\n"; },"13":function(container,depth0,helpers,partials,data) { var stack1, alias1=depth0 != null ? depth0 : (container.nullContext || {}); diff --git a/build/tmpl/tipbar.js b/build/tmpl/tipbar.js index 99471e4e6..5432ebf6c 100644 --- a/build/tmpl/tipbar.js +++ b/build/tmpl/tipbar.js @@ -2,27 +2,19 @@ this["Handlebars"] = this["Handlebars"] || {}; this["Handlebars"]["templates"] = this["Handlebars"]["templates"] || {}; this["Handlebars"]["templates"]["tipbar"] = Handlebars.template({"1":function(container,depth0,helpers,partials,data) { return "×"; -},"3":function(container,depth0,helpers,partials,data) { - return "Previous error"; -},"5":function(container,depth0,helpers,partials,data) { - return "Next error"; },"compiler":[7,">= 4.0.0"],"main":function(container,depth0,helpers,partials,data) { - var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=helpers.blockHelperMissing, alias5=container.escapeExpression, buffer = + var stack1, helper, options, alias1=depth0 != null ? depth0 : (container.nullContext || {}), alias2=helpers.helperMissing, alias3="function", alias4=container.escapeExpression, buffer = "
\n
\n
\n\n
\n \n
" - + alias5(((helper = (helper = helpers.ohNoesMsg || (depth0 != null ? depth0.ohNoesMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"ohNoesMsg","hash":{},"data":data}) : helper))) + return buffer + "\n
" + + alias4(((helper = (helper = helpers.ohNoesMsg || (depth0 != null ? depth0.ohNoesMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"ohNoesMsg","hash":{},"data":data}) : helper))) + "
\n
\n \n
\n \n \n \n \n \n \n \n
\n
\n
"; + + alias4(((helper = (helper = helpers.showMeMsg || (depth0 != null ? depth0.showMeMsg : depth0)) != null ? helper : alias2),(typeof helper === alias3 ? helper.call(alias1,{"name":"showMeMsg","hash":{},"data":data}) : helper))) + + "
\n
\n \n \n \n \n \n \n \n
\n \n"; },"useData":true});; \ No newline at end of file diff --git a/js/output/sql/sql-output.js b/js/output/sql/sql-output.js index 0d36f3cd3..5ed7518c8 100644 --- a/js/output/sql/sql-output.js +++ b/js/output/sql/sql-output.js @@ -60,105 +60,145 @@ window.SQLOutput = Backbone.View.extend({ * error message. SQLlite error messages aren't always very descriptive, * this should make common syntax errors easier to understand. */ - getErrorMessage: function(errorMessage, statement) { - errorMessage = errorMessage || ""; + getErrorMessage: function(sqliteError, statement) { + sqliteError = sqliteError || ""; statement = statement || ""; statement = statement.toUpperCase(); - var isSyntaxError = errorMessage.indexOf(": syntax error") > -1; + let errorMessage = sqliteError; + + // First, we translate SQLite errors into friendly i18n-able messages + + const colTypesError = sqliteError.indexOf("valid column types") > -1; + if (colTypesError) { + errorMessage = i18n._("Please use one of the valid column types " + + "when creating a table: ") + + "\"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\"."; + } + const uniqStr = "UNIQUE constraint failed:"; + const uniqError = sqliteError.indexOf(uniqStr) > -1; + if (uniqError) { + const colName = sqliteError.split(uniqStr)[1].trim(); + errorMessage = i18n._("\"UNIQUE\" constraint failed on column \"%(colName)s\".", + {colName}); + } + const notNullStr = "NOT NULL constraint failed:" + const notNullError = sqliteError.indexOf(notNullStr) > -1; + if (notNullError) { + const colName = sqliteError.split(notNullStr)[1].trim(); + errorMessage = i18n._("\"NOT NULL\" constraint failed on column \"%(colName)s\".", + {colName}); + } + const dupColStr = "duplicate column name:"; + const dupColError = sqliteError.indexOf(dupColStr) > -1; + if (dupColError) { + const colName = errorMessage.split(dupColStr)[1].trim(); + errorMessage = i18n._("You have multiple columns named \"%(colName)s\" - " + + "column names must be unique.", {colName}); + } + const unknownColStr = "no such column:"; + const unknownColError = sqliteError.indexOf(unknownColStr) > -1; + if (unknownColError) { + const colName = sqliteError.split(unknownColStr)[1].trim(); + errorMessage = i18n._("We can't find the column named \"%(colName)s\".", + {colName}); + } + const noTablesError = sqliteError.indexOf("no tables specified") > -1; + if (noTablesError) { + errorMessage = i18n._("You didn't specify any tables for your \"SELECT\"."); + } + // Generic syntax error messages take form: 'near \"%T\": syntax error' + const syntaxErrStr = ": syntax error"; + const isSyntaxError = sqliteError.indexOf(syntaxErrStr) > -1; if (isSyntaxError) { - errorMessage = i18n._("There's a syntax error " + - errorMessage.split(":")[0]); + const nearPhrase = errorMessage.split(syntaxErrStr)[0]; + errorMessage = i18n._("There's a syntax error near %(nearThing)s.", + {nearThing: nearPhrase.substr(5)}); } - // Possible SELECT with missing FROM - if (errorMessage.indexOf("no such column:") !== -1 && + // Now that we've translated the base error messages, + // we add on additional helper messages for common mistakes + if (unknownColError && statement.indexOf("SELECT") !== -1 && statement.indexOf("FROM") === -1) { - errorMessage += ". " + i18n._("Are you missing a FROM clause?"); - // Possible INSERT with missing INTO + errorMessage += " " + i18n._("Are you perhaps missing a \"FROM\" clause?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && statement.indexOf("VALUES") !== -1 && statement.indexOf("INTO") === -1) { - errorMessage += ". " + i18n._("Are you missing the INTO keyword?"); - // Possible INSERT INTO with missing VALUES + errorMessage += " " + i18n._("Are you missing the \"INTO\" keyword?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && statement.indexOf("INTO") !== -1 && statement.indexOf("VALUES") === -1) { - errorMessage += ". " + - i18n._("Are you missing the VALUES keyword?"); + errorMessage += " " + + i18n._("Are you missing the \"VALUES\" keyword?"); } else if (statement.indexOf("INTERGER") !== -1) { - errorMessage += ". " + - i18n._(" Is INTEGER spelled correctly?"); + errorMessage += " " + + i18n._("Is \"INTEGER\" spelled correctly?"); } else if (isSyntaxError && statement.indexOf("CREATE") !== -1 && statement.search(/CREATE TABLE \w+\s\w+/) > -1) { - errorMessage += ". " + + errorMessage += " " + i18n._("You can't have a space in your table name."); } else if (isSyntaxError && statement.indexOf("CREATE TABLE (") > -1) { - errorMessage += ". " + + errorMessage += " " + i18n._("Are you missing the table name?"); } else if (isSyntaxError && statement.indexOf("PRIMARY KEY INTEGER") !== -1) { - errorMessage += ". " + - i18n._("Did you mean to put PRIMARY KEY after INTEGER?"); + errorMessage += " " + + i18n._("Perhaps you meant to put \"PRIMARY KEY\" after \"INTEGER\"?"); } else if (isSyntaxError && statement.indexOf("(") !== -1 && statement.indexOf(")") === -1) { - errorMessage += ". " + + errorMessage += " " + i18n._("Are you missing a parenthesis?"); } else if (isSyntaxError && - statement.indexOf("CREATE") !== -1 && + statement.indexOf("CREATE") !== -1 && statement.indexOf("TABLE") === -1 && ( statement.indexOf("INDEX") === -1 || statement.indexOf("TRIGGER") === -1 || statement.indexOf("VIEW") === -1)) { - errorMessage += ". " + + errorMessage += " " + i18n._("You may be missing what to create. For " + - "example, CREATE TABLE..."); + "example, \"CREATE TABLE...\""); } else if (isSyntaxError && statement.indexOf("UPDATE") !== -1 && statement.indexOf("SET") === -1) { - errorMessage += ". " + - i18n._("Are you missing the SET keyword?"); + errorMessage += " " + + i18n._("Are you missing the \"SET\" keyword?"); } else if (isSyntaxError && statement.search(/[^SUM]\s*\(.*\)\n*\s*\w+/) > -1 || statement.search(/\n+\s*SELECT/) > -1 || statement.search(/\)\n+\s*INSERT/) > -1 ) { - errorMessage += ". " + + errorMessage += " " + i18n._("Do you have a semi-colon after each statement?"); } else if (isSyntaxError && statement.indexOf("INSERT") !== -1 && - statement.search(/[^INSERT],\d*\s*[a-zA-Z]+/) > -1 + statement.search(/[^INSERT],\d*\s*[a-zA-Z]+/) > -1 ) { - errorMessage += ". " + + errorMessage += " " + i18n._("Are you missing quotes around text values?"); } else if (isSyntaxError && statement.search(/,\s*\)/) > -1) { - errorMessage += ". " + + errorMessage += " " + i18n._("Do you have an extra comma?"); } else if (isSyntaxError && statement.indexOf("INSERT,") > -1 ) { - errorMessage += ". " + - i18n._("There shouldn't be a comma after INSERT."); - } else if (errorMessage.indexOf("column types") > -1 && + errorMessage += " " + + i18n._("There shouldn't be a comma after \"INSERT\"."); + } else if (colTypesError && statement.search(/(\w+\s*,\s*((TEXT)|(INTEGER))+)/) > -1) { - errorMessage += ". " + + errorMessage += " " + i18n._("Do you have an extra comma between the name and type?"); - } else if (errorMessage.indexOf("column types") > -1 && + } else if (colTypesError && statement.search(/(\w+\s+\w+\s*((TEXT)|(INTEGER)|(REAL))+)/) > -1) { errorMessage = i18n._("You can't have a space in your column name."); - } else if (errorMessage.indexOf("UNIQUE constraint failed") !== -1) { - errorMessage += ". " + + } else if (uniqError) { + errorMessage += " " + i18n._("Are you specifying a different value for each row?"); - } else if (errorMessage.indexOf("duplicate column name:") !== -1) { - errorMessage = i18n._("You have multiple columns named `%(name)s` - " + - "column names must be unique.", - {name: errorMessage.split(":")[1].trim()}); } return errorMessage; }, @@ -350,7 +390,9 @@ window.SQLOutput = Backbone.View.extend({ var output = Handlebars.templates["sql-results"]({ tables: tables, - results: results + results: results, + databaseMsg: i18n._("Database Schema"), + resultsMsg: i18n._("Query results"), }); var doc = this.getDocument(); diff --git a/js/ui/tipbar.js b/js/ui/tipbar.js index d4c401d28..48437e4b1 100644 --- a/js/ui/tipbar.js +++ b/js/ui/tipbar.js @@ -18,7 +18,9 @@ window.TipBar = Backbone.View.extend({ this.$overlay = $("
").appendTo(this.$el); this.$el.append(Handlebars.templates["tipbar"]({ ohNoesMsg: i18n._("Oh noes!"), - showMeMsg: i18n._("Show me where") + showMeMsg: i18n._("Show me where"), + prevMsg: i18n._("Previous error"), + nextMsg: i18n._("Next error"), })); this.$bar = this.$el.find(".tipbar"); }, diff --git a/js/ui/tooltips/image-modal.js b/js/ui/tooltips/image-modal.js index 8a1cf222c..51ab594fa 100644 --- a/js/ui/tooltips/image-modal.js +++ b/js/ui/tooltips/image-modal.js @@ -132,7 +132,9 @@ this.$el = $(Handlebars.templates["mediapicker-modal"]({ imagesDir: this.options.imagesDir, soundsDir: this.options.soundsDir, - classes: this.options.files + classes: this.options.files, + closeMsg: i18n._("Close"), + okMsg: i18n._("Ok"), })); this.$el.appendTo("body").hide(); }, @@ -233,7 +235,7 @@ render: function() { var self = this; this.$el = $(Handlebars.templates["mediapicker-preview"]( - {isAudio: false})) + {isAudio: false, pickMsg: i18n._("Pick file:")})) .appendTo("body").hide(); this.$(".thumb") @@ -346,7 +348,7 @@ render: function() { var self = this; this.$el = $(Handlebars.templates["mediapicker-preview"]( - {isAudio: true})) + {isAudio: true, pickMsg: i18n._("Pick file:")})) .appendTo("body").hide(); this.$("button").on("click", function() { diff --git a/tests/output/sql/output_test.js b/tests/output/sql/output_test.js index 3d499ef63..eb397d94f 100644 --- a/tests/output/sql/output_test.js +++ b/tests/output/sql/output_test.js @@ -18,11 +18,14 @@ describe("Linting", function() { "CREATE TABLE characters (name TEXT, age INTEGER, x NUMERIC, y\n" + "REAL, z NONE);"); failingTest("Creating a table with invalid column types", - "CREATE TABLE characters (name FDSFSD)"); + "CREATE TABLE characters (name FDSFSD)", + ["Please use one of the valid column types when creating a table: \"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\"."]); failingTest("Creating a table with duplicate column names", - "CREATE TABLE characters (name TEXT, name TEXT)"); + "CREATE TABLE characters (name TEXT, name TEXT)", + ["You have multiple columns named \"name\" - column names must be unique."]); failingTest("Creating a table with no columns", - "CREATE TABLE characters;"); + "CREATE TABLE characters;", + ["There's a syntax error near \"characters\"."]); // Linting for comments test("Parsing of SQL single lint comments", @@ -39,15 +42,20 @@ describe("Linting", function() { "INSERT INTO characters VALUES (\"Rick\");" + "INSERT INTO characters VALUES (\"Morty\");"); failingTest("Inserting rows missing VALUES", - "INSERT INTO characters (\"Jerry\");"); + "INSERT INTO characters (\"Jerry\");", + ["There's a syntax error near \")\". Are you missing the \"VALUES\" keyword?"]); failingTest("Inserting rows missing INTO", - "INSERT characters VALUES (\"Beth\");"); + "INSERT characters VALUES (\"Beth\");", + ["There's a syntax error near \"characters\". Are you missing the \"INTO\" keyword?"]); failingTest("Inserting rows missing VALUES and INTO", - "INSERT characters (\"Summer\");"); + "INSERT characters (\"Summer\");", + ["There's a syntax error near \"characters\"."]); failingTest("Inserting rows unquoted", - "INSERT characters (Summer);"); + "INSERT characters (Summer);", + ["There's a syntax error near \"characters\"."]); failingTest("Inserting rows mismatching quote", - "INSERT characters ('Summer);"); + "INSERT characters ('Summer);", + ["There's a syntax error near \"characters\"."]); // Linting for select statements test("Simple select", @@ -60,7 +68,8 @@ describe("Linting", function() { "CREATE TABLE characters (name TEXT);" + "INSERT INTO characters VALUES (\"Rick\");" + "SELECT name FROM characters;" + - "SELECT * WHERE name = 'hi';"); + "SELECT * WHERE name = 'hi';", + ["You didn't specify any tables for your \"SELECT\"."]); // Deleting rows test("Deleting rows", @@ -76,7 +85,8 @@ describe("Linting", function() { failingTest("Deleting rows missing FROM", "CREATE TABLE characters (name TEXT);" + "INSERT INTO characters VALUES (\"Morty\");" + - "DELETE characters WHERE name = \"Morty\";"); + "DELETE characters WHERE name = \"Morty\";", + ["There's a syntax error near \"characters\"."]); // Updating rows test("Updating rows", @@ -86,7 +96,8 @@ describe("Linting", function() { failingTest("Updating rows missing SET", "CREATE TABLE characters (name TEXT);" + "INSERT INTO characters VALUES (\"Morty clone\");" + - "UPDATE characters name = \"Morty\" WHERE name = 'Morty clone';"); + "UPDATE characters name = \"Morty\" WHERE name = 'Morty clone';", + ["There's a syntax error near \"name\". Are you missing the \"SET\" keyword?"]); // Joins test("Inner joins", @@ -114,7 +125,8 @@ describe("Linting", function() { failingTest("NOT NULL constraints violated", "CREATE TABLE episodes(id INTEGER NOT NULL, name TEXT);" + "INSERT INTO episodes VALUES (1, 'Pilot');" + - "INSERT INTO episodes VALUES (NULL, 'Lawnmower Dog');"); + "INSERT INTO episodes VALUES (NULL, 'Lawnmower Dog');", + ["\"NOT NULL\" constraint failed on column \"episodes.id\"."]); // Primary Key constraint test("Primary key constraints failing", @@ -124,7 +136,8 @@ describe("Linting", function() { failingTest("Primary key constraints failing", "CREATE TABLE episodes(id INTEGER PRIMARY KEY, name TEXT);" + "INSERT INTO episodes VALUES (9, 'Something Ricked This Way Comes');" + - "INSERT INTO episodes VALUES (9, 'Something Rick This Way Comes');"); + "INSERT INTO episodes VALUES (9, 'Something Rick This Way Comes');", + ["\"UNIQUE\" constraint failed on column \"episodes.id\". Are you specifying a different value for each row?"]); // Foreign key constraint violation test("Foreign key constraint", @@ -147,91 +160,91 @@ describe("Linting", function() { failingTest("Testing for extra context, missing FROM", "CREATE TABLE characters (name TEXT);" + "SELECT name WHERE name = 3;", - ["Are you missing a FROM clause?"]); + ["We can't find the column named \"name\". Are you perhaps missing a \"FROM\" clause?"]); failingTest("Testing for misspelling INTEGER", "CREATE TABLE books(id INTERGER PRIMARY KEY, name TEXT);", - ["Is INTEGER spelled correctly?"]); + ["Please use one of the valid column types when creating a table: \"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\". Is \"INTEGER\" spelled correctly?"]); failingTest("Testing for wrong order of PRIMARY KEY", "CREATE TABLE books (id PRIMARY KEY INTEGER,name TEXT,rating INTEGER );", - ["Did you mean to put PRIMARY KEY after INTEGER?"]); + ["There's a syntax error near \"INTEGER\". Perhaps you meant to put \"PRIMARY KEY\" after \"INTEGER\"?"]); failingTest("Testing for missing parenthesis", "CREATE TABLE books (id INTEGER PRIMARY KEY, name TEXT, rating INTEGER; ", - ["Are you missing a parenthesis?"]); + ["There's a syntax error near \"INTEGER\". Are you missing a parenthesis?"]); failingTest("Testing for extra comma at end", "CREATE TABLE books(id INTEGER PRIMARY KEY, name TEXT, );", - ["Do you have an extra comma?"]); + ["There's a syntax error near \")\". Do you have an extra comma?"]); failingTest("Testing for bad CREATE syntax", "CREATE TABLE books(id INTEGER PRIMARY KEY name TEXT);", - ["There's a syntax error near "]); + ["There's a syntax error near \"name\"."]); failingTest("Testing for CREATE missing TABLE", "CREATE books(id INTEGER PRIMARY KEY name TEXT);", - ["You may be missing what to create. For example, CREATE TABLE..."]); + ["There's a syntax error near \"books\". You may be missing what to create. For example, \"CREATE TABLE...\""]); failingTest("Testing for bad table name", "CREATE TABLE favorite books (name TEXT);" + "SELECT name WHERE name = 3;", - ["You can't have a space in your table name."]); - + ["There's a syntax error near \"books\". You can't have a space in your table name."]); + failingTest("Testing for missing table name", "CREATE TABLE (name TEXT);", - ["Are you missing the table name?"]); + ["There's a syntax error near \"(\". Are you missing the table name?"]); failingTest("Testing for extra commas in column types", "CREATE TABLE books (id INTEGER PRIMARY KEY,name,TEXT,rating INTEGER);", - ["Do you have an extra comma between the name and type?"]); + ["Please use one of the valid column types when creating a table: \"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\". Do you have an extra comma between the name and type?"]); failingTest("Testing for extra commas in column types #2", "CREATE TABLE booklist (id, INTEGER PRIMARY KEY, name TEXT, rating INTEGER);", - ["Do you have an extra comma between the name and type?"]); + ["Please use one of the valid column types when creating a table: \"TEXT\", \"NUMERIC\", \"INTEGER\", \"REAL\", \"NONE\". Do you have an extra comma between the name and type?"]); failingTest("Testing for space in column name", "CREATE TABLE books (id INTEGER PRIMARY KEY, title TEXT, rating out of ten INTEGER);", ["You can't have a space in your column name."]); - + failingTest("Testing for missing quotes #1", "INSERT INTO FavBooks VALUES (1, Beautiful Creatures, 10);", - ["Are you missing quotes around text values?"]); + ["There's a syntax error near \"Creatures\". Are you missing quotes around text values?"]); failingTest("Testing for missing quotes #2", "insert into Books VALUES(1,\"Harry Potter\",5 star); ", - ["Are you missing quotes around text values?"]); + ["There's a syntax error near \"star\". Are you missing quotes around text values?"]); failingTest("Testing for missing semi-colon #1", "CREATE TABLE books (name TEXT)" + - "INSERT INTO books VALUES (1, 'book a', 100)" + - "INSERT INTO books VALUES (2, 'book b', 110)" + + "INSERT INTO books VALUES (1, 'book a', 100)" + + "INSERT INTO books VALUES (2, 'book b', 110)" + "INSERT INTO books VALUES (3, 'book c', 1)", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"INSERT\". Do you have a semi-colon after each statement?"]); failingTest("Testing for missing semi-colon #2", "CREATE TABLE books (id INTEGER PRIMARY KEY, bookname TEXT, ratingoutof5 INTEGER);\n" + - "INSERT INTO books VALUES (1, 'Harry Potter', 5)\n" + - "INSERT INTO books VALUES (2, 'Percy Jackson and the Olympians', 5)\n" + + "INSERT INTO books VALUES (1, 'Harry Potter', 5)\n" + + "INSERT INTO books VALUES (2, 'Percy Jackson and the Olympians', 5)\n" + "INSERT INTO books VALUES (3, 'The Hunger Games', 5)\n", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"INSERT\". Do you have a semi-colon after each statement?"]); failingTest("Testing for missing semi-colon #3", - "CREATE TABLE books (id INTEGER PRIMARY KEY, name TEXT, rating INTEGER)" + + "CREATE TABLE books (id INTEGER PRIMARY KEY, name TEXT, rating INTEGER)" + "INSERT INTO books VALUES (1 , 'Rumo', 5);", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"INSERT\". Do you have a semi-colon after each statement?"]); failingTest("Testing for missing semi-colon #4", - "CREATE TABLE books (id INTEGER PRIMARY KEY, name TEXT, rating INTEGER)\n" + + "CREATE TABLE books (id INTEGER PRIMARY KEY, name TEXT, rating INTEGER)\n" + " INSERT INTO books VALUES (1 , 'Rumo', 5);", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"INSERT\". Do you have a semi-colon after each statement?"]); failingTest("Testing for missing semi-colon #5", - "SELECT * FROM movies\n" + + "SELECT * FROM movies\n" + " SELECT * FROM movies WHERE release_year > 1999;", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"SELECT\". Do you have a semi-colon after each statement?"]); failingTest("Testing for missing semi-colon #6", "CREATE TABLE customers (id INTEGER\n" + "PRIMARY KEY, name TEXT, rating INTEGER)\n" + "INSERT INTO customers VALUES (73, \"Brian\", 33);", - ["Do you have a semi-colon after each statement?"]); + ["There's a syntax error near \"INSERT\". Do you have a semi-colon after each statement?"]); /* TODO(pamela): Add tests to make sure the following *don't* generate the message - SELECT SUM(minutes INTEGER) FROM todo_list; + SELECT SUM(minutes INTEGER) FROM todo_list; SELECT minutes SUM(quantity) FROM todo_list ORDER BY minutes; SELECT minutes SUM(item) FROM todo_list GROUP BY minutes; SELECT minutes SUM(quantity), FROM todo_list; @@ -240,17 +253,17 @@ describe("Linting", function() { failingTest("Testing for extra comma", "CREATE TABLE books (id INTEGER, name TEXT, rating INTEGER);" + "INSERT, INTO books VALUES (1, \"gone with the wind\", 1)", - ["There shouldn't be a comma after INSERT."]); + ["There's a syntax error near \",\". There shouldn't be a comma after \"INSERT\"."]); failingTest("Testing for UNIQUE constraint", - "CREATE TABLE customers (id INTEGER PRIMARY KEY);\n" + + "CREATE TABLE customers (id INTEGER PRIMARY KEY);\n" + "INSERT INTO customers VALUES (1);" + "INSERT INTO customers VALUES (1);", - ["Are you specifying a different value for each row?"]); + ["\"UNIQUE\" constraint failed on column \"customers.id\". Are you specifying a different value for each row?"]); failingTest("Testing for duplicate column name", "CREATE TABLE customers (id INTEGER PRIMARY KEY, id TEXT);", - ["You have multiple columns named `id` - column names must be unique."]); + ["You have multiple columns named \"id\" - column names must be unique."]); }); diff --git a/tmpl/mediapicker-modal.handlebars b/tmpl/mediapicker-modal.handlebars index 8cc9e6e63..3d7ab5610 100644 --- a/tmpl/mediapicker-modal.handlebars +++ b/tmpl/mediapicker-modal.handlebars @@ -57,7 +57,7 @@ \ No newline at end of file diff --git a/tmpl/mediapicker-preview.handlebars b/tmpl/mediapicker-preview.handlebars index 566e98e2d..173b65ede 100644 --- a/tmpl/mediapicker-preview.handlebars +++ b/tmpl/mediapicker-preview.handlebars @@ -1,6 +1,6 @@
- + {{#if isAudio}}
@@ -9,12 +9,12 @@
-
+
{{/if}} + {{pickMsg}} +
\ No newline at end of file diff --git a/tmpl/sql-results.handlebars b/tmpl/sql-results.handlebars index 9b92f3cd9..13f80c306 100644 --- a/tmpl/sql-results.handlebars +++ b/tmpl/sql-results.handlebars @@ -86,7 +86,7 @@ table.sql-schema-table .row-count {
{{#if tables}} -

Database Schema

+

{{databaseMsg}}

{{/if}} {{#each tables}} @@ -108,7 +108,7 @@ table.sql-schema-table .row-count { {{/each}} {{#if results }} -

Results

+

{{resultsMsg}}

{{/if}} {{#each results}}
diff --git a/tmpl/tipbar.handlebars b/tmpl/tipbar.handlebars index 8035d952e..1e2197355 100644 --- a/tmpl/tipbar.handlebars +++ b/tmpl/tipbar.handlebars @@ -8,11 +8,11 @@
{{showMeMsg}}
- -