Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Move translations to frontend, translate home view and ¾ of test view

  • Loading branch information...
commit 0a786f81d37eb0c879b4f967ee776d3cb62a44ee 1 parent 0246576
@alexanderteinum alexanderteinum authored
View
33 backend/main.coffee
@@ -34,16 +34,39 @@ transformResult = (checkerResult) ->
transformVerifyResult = (checkerResult, result) ->
switch checkerResult.testResultId
when 'SC2.4.2-1-11'
- _.extend(result, { question: 'Does the page title “' + checkerResult.details.pageTitle + '” describe the content of this web page?', answers: ['Yes', 'No'] })
+ _.extend result, {
+ questionValues: [checkerResult.details.pageTitle]
+ answers: ['yes', 'no']
+ }
when 'SC2.4.4-2-11'
- _.extend(result, { question: 'Does the link text ' + checkerResult.details.codeExtract + ' describe the link purpose?', answers: ['Yes', 'No', 'Unsure'] })
+ _.extend result, {
+ questionValues: [checkerResult.details.codeExtract]
+ answers: ['yes', 'no', 'unsure']
+ }
when 'SC2.4.4-2-12'
- _.extend(result, { question: 'Does the link title “' + checkerResult.details.title + '” for the link ' + checkerResult.details.codeExtract + ' describe the link purpose?', answers: ['Yes', 'No', 'Unsure'] })
+ _.extend result, {
+ questionValues: [
+ checkerResult.details.title,
+ checkerResult.details.codeExtract
+ ]
+ answers: ['yes', 'no', 'unsure']
+ }
when 'SC2.4.6-1-11'
- _.extend(result, { question: 'Does the heading “' + checkerResult.details.heading + '” describe the section it belongs to?', answers: ['Yes', 'No', 'Unsure'] })
+ _.extend result, {
+ questionValues: [checkerResult.details.heading]
+ answers: ['yes', 'no', 'unsure']
+ }
when 'SC3.1.2-2-11'
languageCode = checkerResult.details.languageDefinition.languageCode
- _.extend(result, { question: 'Does the text “' + checkerResult.details.checkedText + '” correlate with the specified language ' + getLanguage(languageCode) + ' (' + languageCode + ')?', answers: ['Yes', 'No', 'Unsure', 'I don’t speak ' + getLanguage(languageCode)] })
+
+ _.extend result, {
+ questionValues: [
+ checkerResult.details.checkedText,
+ getLanguage(languageCode),
+ languageCode
+ ]
+ answers: ['yes', 'no', 'unsure', 'i_dont_speak_this_language']
+ }
else
console.log 'Not supported: ' + checkerResult.testResultId
{}
View
3  backend/package.json
@@ -6,6 +6,7 @@
"languages": "0.1.x",
"request": "2.12.x",
"socket.io": "0.9.x",
- "underscore": "1.4.x"
+ "underscore": "1.4.x",
+ "jed": "0.5.x"
}
}
View
1,008 frontend/deps/jed.js
@@ -0,0 +1,1008 @@
+/*
+jed.js
+v0.5.0beta
+
+https://github.com/SlexAxton/Jed
+-----------
+A gettext compatible i18n library for modern JavaScript Applications
+
+by Alex Sexton - AlexSexton [at] gmail - @SlexAxton
+WTFPL license for use
+Dojo CLA for contributions
+
+Jed offers the entire applicable GNU gettext spec'd set of
+functions, but also offers some nicer wrappers around them.
+The api for gettext was written for a language with no function
+overloading, so Jed allows a little more of that.
+
+Many thanks to Joshua I. Miller - unrtst@cpan.org - who wrote
+gettext.js back in 2008. I was able to vet a lot of my ideas
+against his. I also made sure Jed passed against his tests
+in order to offer easy upgrades -- jsgettext.berlios.de
+*/
+(function (root, undef) {
+
+ // Set up some underscore-style functions, if you already have
+ // underscore, feel free to delete this section, and use it
+ // directly, however, the amount of functions used doesn't
+ // warrant having underscore as a full dependency.
+ // Underscore 1.3.0 was used to port and is licensed
+ // under the MIT License by Jeremy Ashkenas.
+ var ArrayProto = Array.prototype,
+ ObjProto = Object.prototype,
+ slice = ArrayProto.slice,
+ hasOwnProp = ObjProto.hasOwnProperty,
+ nativeForEach = ArrayProto.forEach,
+ breaker = {};
+
+ // We're not using the OOP style _ so we don't need the
+ // extra level of indirection. This still means that you
+ // sub out for real `_` though.
+ var _ = {
+ forEach : function( obj, iterator, context ) {
+ var i, l, key;
+ if ( obj === null ) {
+ return;
+ }
+
+ if ( nativeForEach && obj.forEach === nativeForEach ) {
+ obj.forEach( iterator, context );
+ }
+ else if ( obj.length === +obj.length ) {
+ for ( i = 0, l = obj.length; i < l; i++ ) {
+ if ( i in obj && iterator.call( context, obj[i], i, obj ) === breaker ) {
+ return;
+ }
+ }
+ }
+ else {
+ for ( key in obj) {
+ if ( hasOwnProp.call( obj, key ) ) {
+ if ( iterator.call (context, obj[key], key, obj ) === breaker ) {
+ return;
+ }
+ }
+ }
+ }
+ },
+ extend : function( obj ) {
+ this.forEach( slice.call( arguments, 1 ), function ( source ) {
+ for ( var prop in source ) {
+ obj[prop] = source[prop];
+ }
+ });
+ return obj;
+ }
+ };
+ // END Miniature underscore impl
+
+ // Jed is a constructor function
+ var Jed = function ( options ) {
+ // Some minimal defaults
+ this.defaults = {
+ "locale_data" : {
+ "messages" : {
+ "" : {
+ "domain" : "messages",
+ "lang" : "en",
+ "plural_forms" : "nplurals=2; plural=(n != 1);"
+ }
+ // There are no default keys, though
+ }
+ },
+ // The default domain if one is missing
+ "domain" : "messages"
+ };
+
+ // Mix in the sent options with the default options
+ this.options = _.extend( {}, this.defaults, options );
+ this.textdomain( this.options.domain );
+
+ if ( options.domain && ! this.options.locale_data[ this.options.domain ] ) {
+ throw new Error('Text domain set to non-existent domain: `' + options.domain + '`');
+ }
+ };
+
+ // The gettext spec sets this character as the default
+ // delimiter for context lookups.
+ // e.g.: context\u0004key
+ // If your translation company uses something different,
+ // just change this at any time and it will use that instead.
+ Jed.context_delimiter = String.fromCharCode( 4 );
+
+ function getPluralFormFunc ( plural_form_string ) {
+ return Jed.PF.compile( plural_form_string || "nplurals=2; plural=(n != 1);");
+ }
+
+ function Chain( key, i18n ){
+ this._key = key;
+ this._i18n = i18n;
+ }
+
+ // Create a chainable api for adding args prettily
+ _.extend( Chain.prototype, {
+ onDomain : function ( domain ) {
+ this._domain = domain;
+ return this;
+ },
+ withContext : function ( context ) {
+ this._context = context;
+ return this;
+ },
+ ifPlural : function ( num, pkey ) {
+ this._val = num;
+ this._pkey = pkey;
+ return this;
+ },
+ fetch : function ( sArr ) {
+ if ( {}.toString.call( sArr ) != '[object Array]' ) {
+ sArr = [].slice.call(arguments);
+ }
+ return ( sArr && sArr.length ? Jed.sprintf : function(x){ return x; } )(
+ this._i18n.dcnpgettext(this._domain, this._context, this._key, this._pkey, this._val),
+ sArr
+ );
+ }
+ });
+
+ // Add functions to the Jed prototype.
+ // These will be the functions on the object that's returned
+ // from creating a `new Jed()`
+ // These seem redundant, but they gzip pretty well.
+ _.extend( Jed.prototype, {
+ // The sexier api start point
+ translate : function ( key ) {
+ return new Chain( key, this );
+ },
+
+ textdomain : function ( domain ) {
+ if ( ! domain ) {
+ return this._textdomain;
+ }
+ this._textdomain = domain;
+ },
+
+ gettext : function ( key ) {
+ return this.dcnpgettext.call( this, undef, undef, key );
+ },
+
+ dgettext : function ( domain, key ) {
+ return this.dcnpgettext.call( this, domain, undef, key );
+ },
+
+ dcgettext : function ( domain , key /*, category */ ) {
+ // Ignores the category anyways
+ return this.dcnpgettext.call( this, domain, undef, key );
+ },
+
+ ngettext : function ( skey, pkey, val ) {
+ return this.dcnpgettext.call( this, undef, undef, skey, pkey, val );
+ },
+
+ dngettext : function ( domain, skey, pkey, val ) {
+ return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
+ },
+
+ dcngettext : function ( domain, skey, pkey, val/*, category */) {
+ return this.dcnpgettext.call( this, domain, undef, skey, pkey, val );
+ },
+
+ pgettext : function ( context, key ) {
+ return this.dcnpgettext.call( this, undef, context, key );
+ },
+
+ dpgettext : function ( domain, context, key ) {
+ return this.dcnpgettext.call( this, domain, context, key );
+ },
+
+ dcpgettext : function ( domain, context, key/*, category */) {
+ return this.dcnpgettext.call( this, domain, context, key );
+ },
+
+ npgettext : function ( context, skey, pkey, val ) {
+ return this.dcnpgettext.call( this, undef, context, skey, pkey, val );
+ },
+
+ dnpgettext : function ( domain, context, skey, pkey, val ) {
+ return this.dcnpgettext.call( this, domain, context, skey, pkey, val );
+ },
+
+ // The most fully qualified gettext function. It has every option.
+ // Since it has every option, we can use it from every other method.
+ // This is the bread and butter.
+ // Technically there should be one more argument in this function for 'Category',
+ // but since we never use it, we might as well not waste the bytes to define it.
+ dcnpgettext : function ( domain, context, singular_key, plural_key, val ) {
+ // Set some defaults
+
+ plural_key = plural_key || singular_key;
+
+ // Use the global domain default if one
+ // isn't explicitly passed in
+ domain = domain || this._textdomain;
+
+ // Default the value to the singular case
+ val = typeof val == 'undefined' ? 1 : val;
+
+ var fallback;
+
+ // Handle special cases
+
+ // No options found
+ if ( ! this.options ) {
+ // There's likely something wrong, but we'll return the correct key for english
+ // We do this by instantiating a brand new Jed instance with the default set
+ // for everything that could be broken.
+ fallback = new Jed();
+ return fallback.dcnpgettext.call( fallback, undefined, undefined, singular_key, plural_key, val );
+ }
+
+ // No translation data provided
+ if ( ! this.options.locale_data ) {
+ throw new Error('No locale data provided.');
+ }
+
+ if ( ! this.options.locale_data[ domain ] ) {
+ throw new Error('Domain `' + domain + '` was not found.');
+ }
+
+ if ( ! this.options.locale_data[ domain ][ "" ] ) {
+ throw new Error('No locale meta information provided.');
+ }
+
+ // Make sure we have a truthy key. Otherwise we might start looking
+ // into the empty string key, which is the options for the locale
+ // data.
+ if ( ! singular_key ) {
+ throw new Error('No translation key found.');
+ }
+
+ // Handle invalid numbers, but try casting strings for good measure
+ if ( typeof val != 'number' ) {
+ val = parseInt( val, 10 );
+
+ if ( isNaN( val ) ) {
+ throw new Error('The number that was passed in is not a number.');
+ }
+ }
+
+ var key = context ? context + Jed.context_delimiter + singular_key : singular_key,
+ locale_data = this.options.locale_data,
+ dict = locale_data[ domain ],
+ pluralForms = dict[""].plural_forms || (locale_data.messages || this.defaults.locale_data.messages)[""].plural_forms,
+ val_idx = getPluralFormFunc(pluralForms)(val) + 1,
+ val_list,
+ res;
+
+ // Throw an error if a domain isn't found
+ if ( ! dict ) {
+ throw new Error('No domain named `' + domain + '` could be found.');
+ }
+
+ val_list = dict[ key ];
+
+ // If there is no match, then revert back to
+ // english style singular/plural with the keys passed in.
+ if ( ! val_list || val_idx >= val_list.length ) {
+ if (this.options.missing_key_callback) {
+ this.options.missing_key_callback(key);
+ }
+ res = [ null, singular_key, plural_key ];
+ return res[ getPluralFormFunc(pluralForms)( val ) + 1 ];
+ }
+
+ res = val_list[ val_idx ];
+
+ // This includes empty strings on purpose
+ if ( ! res ) {
+ res = [ null, singular_key, plural_key ];
+ return res[ getPluralFormFunc(pluralForms)( val ) + 1 ];
+ }
+ return res;
+ }
+ });
+
+
+ // We add in sprintf capabilities for post translation value interolation
+ // This is not internally used, so you can remove it if you have this
+ // available somewhere else, or want to use a different system.
+
+ // We _slightly_ modify the normal sprintf behavior to more gracefully handle
+ // undefined values.
+
+ /**
+ sprintf() for JavaScript 0.7-beta1
+ http://www.diveintojavascript.com/projects/javascript-sprintf
+
+ Copyright (c) Alexandru Marasteanu <alexaholic [at) gmail (dot] com>
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ * Neither the name of sprintf() for JavaScript nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ DISCLAIMED. IN NO EVENT SHALL Alexandru Marasteanu BE LIABLE FOR ANY
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+ var sprintf = (function() {
+ function get_type(variable) {
+ return Object.prototype.toString.call(variable).slice(8, -1).toLowerCase();
+ }
+ function str_repeat(input, multiplier) {
+ for (var output = []; multiplier > 0; output[--multiplier] = input) {/* do nothing */}
+ return output.join('');
+ }
+
+ var str_format = function() {
+ if (!str_format.cache.hasOwnProperty(arguments[0])) {
+ str_format.cache[arguments[0]] = str_format.parse(arguments[0]);
+ }
+ return str_format.format.call(null, str_format.cache[arguments[0]], arguments);
+ };
+
+ str_format.format = function(parse_tree, argv) {
+ var cursor = 1, tree_length = parse_tree.length, node_type = '', arg, output = [], i, k, match, pad, pad_character, pad_length;
+ for (i = 0; i < tree_length; i++) {
+ node_type = get_type(parse_tree[i]);
+ if (node_type === 'string') {
+ output.push(parse_tree[i]);
+ }
+ else if (node_type === 'array') {
+ match = parse_tree[i]; // convenience purposes only
+ if (match[2]) { // keyword argument
+ arg = argv[cursor];
+ for (k = 0; k < match[2].length; k++) {
+ if (!arg.hasOwnProperty(match[2][k])) {
+ throw(sprintf('[sprintf] property "%s" does not exist', match[2][k]));
+ }
+ arg = arg[match[2][k]];
+ }
+ }
+ else if (match[1]) { // positional argument (explicit)
+ arg = argv[match[1]];
+ }
+ else { // positional argument (implicit)
+ arg = argv[cursor++];
+ }
+
+ if (/[^s]/.test(match[8]) && (get_type(arg) != 'number')) {
+ throw(sprintf('[sprintf] expecting number but found %s', get_type(arg)));
+ }
+
+ // Jed EDIT
+ if ( typeof arg == 'undefined' || arg === null ) {
+ arg = '';
+ }
+ // Jed EDIT
+
+ switch (match[8]) {
+ case 'b': arg = arg.toString(2); break;
+ case 'c': arg = String.fromCharCode(arg); break;
+ case 'd': arg = parseInt(arg, 10); break;
+ case 'e': arg = match[7] ? arg.toExponential(match[7]) : arg.toExponential(); break;
+ case 'f': arg = match[7] ? parseFloat(arg).toFixed(match[7]) : parseFloat(arg); break;
+ case 'o': arg = arg.toString(8); break;
+ case 's': arg = ((arg = String(arg)) && match[7] ? arg.substring(0, match[7]) : arg); break;
+ case 'u': arg = Math.abs(arg); break;
+ case 'x': arg = arg.toString(16); break;
+ case 'X': arg = arg.toString(16).toUpperCase(); break;
+ }
+ arg = (/[def]/.test(match[8]) && match[3] && arg >= 0 ? '+'+ arg : arg);
+ pad_character = match[4] ? match[4] == '0' ? '0' : match[4].charAt(1) : ' ';
+ pad_length = match[6] - String(arg).length;
+ pad = match[6] ? str_repeat(pad_character, pad_length) : '';
+ output.push(match[5] ? arg + pad : pad + arg);
+ }
+ }
+ return output.join('');
+ };
+
+ str_format.cache = {};
+
+ str_format.parse = function(fmt) {
+ var _fmt = fmt, match = [], parse_tree = [], arg_names = 0;
+ while (_fmt) {
+ if ((match = /^[^\x25]+/.exec(_fmt)) !== null) {
+ parse_tree.push(match[0]);
+ }
+ else if ((match = /^\x25{2}/.exec(_fmt)) !== null) {
+ parse_tree.push('%');
+ }
+ else if ((match = /^\x25(?:([1-9]\d*)\$|\(([^\)]+)\))?(\+)?(0|'[^$])?(-)?(\d+)?(?:\.(\d+))?([b-fosuxX])/.exec(_fmt)) !== null) {
+ if (match[2]) {
+ arg_names |= 1;
+ var field_list = [], replacement_field = match[2], field_match = [];
+ if ((field_match = /^([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+ field_list.push(field_match[1]);
+ while ((replacement_field = replacement_field.substring(field_match[0].length)) !== '') {
+ if ((field_match = /^\.([a-z_][a-z_\d]*)/i.exec(replacement_field)) !== null) {
+ field_list.push(field_match[1]);
+ }
+ else if ((field_match = /^\[(\d+)\]/.exec(replacement_field)) !== null) {
+ field_list.push(field_match[1]);
+ }
+ else {
+ throw('[sprintf] huh?');
+ }
+ }
+ }
+ else {
+ throw('[sprintf] huh?');
+ }
+ match[2] = field_list;
+ }
+ else {
+ arg_names |= 2;
+ }
+ if (arg_names === 3) {
+ throw('[sprintf] mixing positional and named placeholders is not (yet) supported');
+ }
+ parse_tree.push(match);
+ }
+ else {
+ throw('[sprintf] huh?');
+ }
+ _fmt = _fmt.substring(match[0].length);
+ }
+ return parse_tree;
+ };
+
+ return str_format;
+ })();
+
+ var vsprintf = function(fmt, argv) {
+ argv.unshift(fmt);
+ return sprintf.apply(null, argv);
+ };
+
+ Jed.parse_plural = function ( plural_forms, n ) {
+ plural_forms = plural_forms.replace(/n/g, n);
+ return Jed.parse_expression(plural_forms);
+ };
+
+ Jed.sprintf = function ( fmt, args ) {
+ if ( {}.toString.call( args ) == '[object Array]' ) {
+ return vsprintf( fmt, [].slice.call(args) );
+ }
+ return sprintf.apply(this, [].slice.call(arguments) );
+ };
+
+ Jed.prototype.sprintf = function () {
+ return Jed.sprintf.apply(this, arguments);
+ };
+ // END sprintf Implementation
+
+ // Start the Plural forms section
+ // This is a full plural form expression parser. It is used to avoid
+ // running 'eval' or 'new Function' directly against the plural
+ // forms.
+ //
+ // This can be important if you get translations done through a 3rd
+ // party vendor. I encourage you to use this instead, however, I
+ // also will provide a 'precompiler' that you can use at build time
+ // to output valid/safe function representations of the plural form
+ // expressions. This means you can build this code out for the most
+ // part.
+ Jed.PF = {};
+
+ Jed.PF.parse = function ( p ) {
+ var plural_str = Jed.PF.extractPluralExpr( p );
+ return Jed.PF.parser.parse.call(Jed.PF.parser, plural_str);
+ };
+
+ Jed.PF.compile = function ( p ) {
+ // Handle trues and falses as 0 and 1
+ function imply( val ) {
+ return (val === true ? 1 : val ? val : 0);
+ }
+
+ var ast = Jed.PF.parse( p );
+ return function ( n ) {
+ return imply( Jed.PF.interpreter( ast )( n ) );
+ };
+ };
+
+ Jed.PF.interpreter = function ( ast ) {
+ return function ( n ) {
+ var res;
+ switch ( ast.type ) {
+ case 'GROUP':
+ return Jed.PF.interpreter( ast.expr )( n );
+ case 'TERNARY':
+ if ( Jed.PF.interpreter( ast.expr )( n ) ) {
+ return Jed.PF.interpreter( ast.truthy )( n );
+ }
+ return Jed.PF.interpreter( ast.falsey )( n );
+ case 'OR':
+ return Jed.PF.interpreter( ast.left )( n ) || Jed.PF.interpreter( ast.right )( n );
+ case 'AND':
+ return Jed.PF.interpreter( ast.left )( n ) && Jed.PF.interpreter( ast.right )( n );
+ case 'LT':
+ return Jed.PF.interpreter( ast.left )( n ) < Jed.PF.interpreter( ast.right )( n );
+ case 'GT':
+ return Jed.PF.interpreter( ast.left )( n ) > Jed.PF.interpreter( ast.right )( n );
+ case 'LTE':
+ return Jed.PF.interpreter( ast.left )( n ) <= Jed.PF.interpreter( ast.right )( n );
+ case 'GTE':
+ return Jed.PF.interpreter( ast.left )( n ) >= Jed.PF.interpreter( ast.right )( n );
+ case 'EQ':
+ return Jed.PF.interpreter( ast.left )( n ) == Jed.PF.interpreter( ast.right )( n );
+ case 'NEQ':
+ return Jed.PF.interpreter( ast.left )( n ) != Jed.PF.interpreter( ast.right )( n );
+ case 'MOD':
+ return Jed.PF.interpreter( ast.left )( n ) % Jed.PF.interpreter( ast.right )( n );
+ case 'VAR':
+ return n;
+ case 'NUM':
+ return ast.val;
+ default:
+ throw new Error("Invalid Token found.");
+ }
+ };
+ };
+
+ Jed.PF.extractPluralExpr = function ( p ) {
+ // trim first
+ p = p.replace(/^\s\s*/, '').replace(/\s\s*$/, '');
+
+ if (! /;\s*$/.test(p)) {
+ p = p.concat(';');
+ }
+
+ var nplurals_re = /nplurals\=(\d+);/,
+ plural_re = /plural\=(.*);/,
+ nplurals_matches = p.match( nplurals_re ),
+ res = {},
+ plural_matches;
+
+ // Find the nplurals number
+ if ( nplurals_matches.length > 1 ) {
+ res.nplurals = nplurals_matches[1];
+ }
+ else {
+ throw new Error('nplurals not found in plural_forms string: ' + p );
+ }
+
+ // remove that data to get to the formula
+ p = p.replace( nplurals_re, "" );
+ plural_matches = p.match( plural_re );
+
+ if (!( plural_matches && plural_matches.length > 1 ) ) {
+ throw new Error('`plural` expression not found: ' + p);
+ }
+ return plural_matches[ 1 ];
+ };
+
+ /* Jison generated parser */
+ Jed.PF.parser = (function(){
+
+var parser = {trace: function trace() { },
+yy: {},
+symbols_: {"error":2,"expressions":3,"e":4,"EOF":5,"?":6,":":7,"||":8,"&&":9,"<":10,"<=":11,">":12,">=":13,"!=":14,"==":15,"%":16,"(":17,")":18,"n":19,"NUMBER":20,"$accept":0,"$end":1},
+terminals_: {2:"error",5:"EOF",6:"?",7:":",8:"||",9:"&&",10:"<",11:"<=",12:">",13:">=",14:"!=",15:"==",16:"%",17:"(",18:")",19:"n",20:"NUMBER"},
+productions_: [0,[3,2],[4,5],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,3],[4,1],[4,1]],
+performAction: function anonymous(yytext,yyleng,yylineno,yy,yystate,$$,_$) {
+
+var $0 = $$.length - 1;
+switch (yystate) {
+case 1: return { type : 'GROUP', expr: $$[$0-1] };
+break;
+case 2:this.$ = { type: 'TERNARY', expr: $$[$0-4], truthy : $$[$0-2], falsey: $$[$0] };
+break;
+case 3:this.$ = { type: "OR", left: $$[$0-2], right: $$[$0] };
+break;
+case 4:this.$ = { type: "AND", left: $$[$0-2], right: $$[$0] };
+break;
+case 5:this.$ = { type: 'LT', left: $$[$0-2], right: $$[$0] };
+break;
+case 6:this.$ = { type: 'LTE', left: $$[$0-2], right: $$[$0] };
+break;
+case 7:this.$ = { type: 'GT', left: $$[$0-2], right: $$[$0] };
+break;
+case 8:this.$ = { type: 'GTE', left: $$[$0-2], right: $$[$0] };
+break;
+case 9:this.$ = { type: 'NEQ', left: $$[$0-2], right: $$[$0] };
+break;
+case 10:this.$ = { type: 'EQ', left: $$[$0-2], right: $$[$0] };
+break;
+case 11:this.$ = { type: 'MOD', left: $$[$0-2], right: $$[$0] };
+break;
+case 12:this.$ = { type: 'GROUP', expr: $$[$0-1] };
+break;
+case 13:this.$ = { type: 'VAR' };
+break;
+case 14:this.$ = { type: 'NUM', val: Number(yytext) };
+break;
+}
+},
+table: [{3:1,4:2,17:[1,3],19:[1,4],20:[1,5]},{1:[3]},{5:[1,6],6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{4:17,17:[1,3],19:[1,4],20:[1,5]},{5:[2,13],6:[2,13],7:[2,13],8:[2,13],9:[2,13],10:[2,13],11:[2,13],12:[2,13],13:[2,13],14:[2,13],15:[2,13],16:[2,13],18:[2,13]},{5:[2,14],6:[2,14],7:[2,14],8:[2,14],9:[2,14],10:[2,14],11:[2,14],12:[2,14],13:[2,14],14:[2,14],15:[2,14],16:[2,14],18:[2,14]},{1:[2,1]},{4:18,17:[1,3],19:[1,4],20:[1,5]},{4:19,17:[1,3],19:[1,4],20:[1,5]},{4:20,17:[1,3],19:[1,4],20:[1,5]},{4:21,17:[1,3],19:[1,4],20:[1,5]},{4:22,17:[1,3],19:[1,4],20:[1,5]},{4:23,17:[1,3],19:[1,4],20:[1,5]},{4:24,17:[1,3],19:[1,4],20:[1,5]},{4:25,17:[1,3],19:[1,4],20:[1,5]},{4:26,17:[1,3],19:[1,4],20:[1,5]},{4:27,17:[1,3],19:[1,4],20:[1,5]},{6:[1,7],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[1,28]},{6:[1,7],7:[1,29],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16]},{5:[2,3],6:[2,3],7:[2,3],8:[2,3],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,3]},{5:[2,4],6:[2,4],7:[2,4],8:[2,4],9:[2,4],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,4]},{5:[2,5],6:[2,5],7:[2,5],8:[2,5],9:[2,5],10:[2,5],11:[2,5],12:[2,5],13:[2,5],14:[2,5],15:[2,5],16:[1,16],18:[2,5]},{5:[2,6],6:[2,6],7:[2,6],8:[2,6],9:[2,6],10:[2,6],11:[2,6],12:[2,6],13:[2,6],14:[2,6],15:[2,6],16:[1,16],18:[2,6]},{5:[2,7],6:[2,7],7:[2,7],8:[2,7],9:[2,7],10:[2,7],11:[2,7],12:[2,7],13:[2,7],14:[2,7],15:[2,7],16:[1,16],18:[2,7]},{5:[2,8],6:[2,8],7:[2,8],8:[2,8],9:[2,8],10:[2,8],11:[2,8],12:[2,8],13:[2,8],14:[2,8],15:[2,8],16:[1,16],18:[2,8]},{5:[2,9],6:[2,9],7:[2,9],8:[2,9],9:[2,9],10:[2,9],11:[2,9],12:[2,9],13:[2,9],14:[2,9],15:[2,9],16:[1,16],18:[2,9]},{5:[2,10],6:[2,10],7:[2,10],8:[2,10],9:[2,10],10:[2,10],11:[2,10],12:[2,10],13:[2,10],14:[2,10],15:[2,10],16:[1,16],18:[2,10]},{5:[2,11],6:[2,11],7:[2,11],8:[2,11],9:[2,11],10:[2,11],11:[2,11],12:[2,11],13:[2,11],14:[2,11],15:[2,11],16:[2,11],18:[2,11]},{5:[2,12],6:[2,12],7:[2,12],8:[2,12],9:[2,12],10:[2,12],11:[2,12],12:[2,12],13:[2,12],14:[2,12],15:[2,12],16:[2,12],18:[2,12]},{4:30,17:[1,3],19:[1,4],20:[1,5]},{5:[2,2],6:[1,7],7:[2,2],8:[1,8],9:[1,9],10:[1,10],11:[1,11],12:[1,12],13:[1,13],14:[1,14],15:[1,15],16:[1,16],18:[2,2]}],
+defaultActions: {6:[2,1]},
+parseError: function parseError(str, hash) {
+ throw new Error(str);
+},
+parse: function parse(input) {
+ var self = this,
+ stack = [0],
+ vstack = [null], // semantic value stack
+ lstack = [], // location stack
+ table = this.table,
+ yytext = '',
+ yylineno = 0,
+ yyleng = 0,
+ recovering = 0,
+ TERROR = 2,
+ EOF = 1;
+
+ //this.reductionCount = this.shiftCount = 0;
+
+ this.lexer.setInput(input);
+ this.lexer.yy = this.yy;
+ this.yy.lexer = this.lexer;
+ if (typeof this.lexer.yylloc == 'undefined')
+ this.lexer.yylloc = {};
+ var yyloc = this.lexer.yylloc;
+ lstack.push(yyloc);
+
+ if (typeof this.yy.parseError === 'function')
+ this.parseError = this.yy.parseError;
+
+ function popStack (n) {
+ stack.length = stack.length - 2*n;
+ vstack.length = vstack.length - n;
+ lstack.length = lstack.length - n;
+ }
+
+ function lex() {
+ var token;
+ token = self.lexer.lex() || 1; // $end = 1
+ // if token isn't its numeric value, convert
+ if (typeof token !== 'number') {
+ token = self.symbols_[token] || token;
+ }
+ return token;
+ }
+
+ var symbol, preErrorSymbol, state, action, a, r, yyval={},p,len,newState, expected;
+ while (true) {
+ // retreive state number from top of stack
+ state = stack[stack.length-1];
+
+ // use default actions if available
+ if (this.defaultActions[state]) {
+ action = this.defaultActions[state];
+ } else {
+ if (symbol == null)
+ symbol = lex();
+ // read action for current state and first input
+ action = table[state] && table[state][symbol];
+ }
+
+ // handle parse error
+ _handle_error:
+ if (typeof action === 'undefined' || !action.length || !action[0]) {
+
+ if (!recovering) {
+ // Report error
+ expected = [];
+ for (p in table[state]) if (this.terminals_[p] && p > 2) {
+ expected.push("'"+this.terminals_[p]+"'");
+ }
+ var errStr = '';
+ if (this.lexer.showPosition) {
+ errStr = 'Parse error on line '+(yylineno+1)+":\n"+this.lexer.showPosition()+"\nExpecting "+expected.join(', ') + ", got '" + this.terminals_[symbol]+ "'";
+ } else {
+ errStr = 'Parse error on line '+(yylineno+1)+": Unexpected " +
+ (symbol == 1 /*EOF*/ ? "end of input" :
+ ("'"+(this.terminals_[symbol] || symbol)+"'"));
+ }
+ this.parseError(errStr,
+ {text: this.lexer.match, token: this.terminals_[symbol] || symbol, line: this.lexer.yylineno, loc: yyloc, expected: expected});
+ }
+
+ // just recovered from another error
+ if (recovering == 3) {
+ if (symbol == EOF) {
+ throw new Error(errStr || 'Parsing halted.');
+ }
+
+ // discard current lookahead and grab another
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ symbol = lex();
+ }
+
+ // try to recover from error
+ while (1) {
+ // check for error recovery rule in this state
+ if ((TERROR.toString()) in table[state]) {
+ break;
+ }
+ if (state == 0) {
+ throw new Error(errStr || 'Parsing halted.');
+ }
+ popStack(1);
+ state = stack[stack.length-1];
+ }
+
+ preErrorSymbol = symbol; // save the lookahead token
+ symbol = TERROR; // insert generic error symbol as new lookahead
+ state = stack[stack.length-1];
+ action = table[state] && table[state][TERROR];
+ recovering = 3; // allow 3 real symbols to be shifted before reporting a new error
+ }
+
+ // this shouldn't happen, unless resolve defaults are off
+ if (action[0] instanceof Array && action.length > 1) {
+ throw new Error('Parse Error: multiple actions possible at state: '+state+', token: '+symbol);
+ }
+
+ switch (action[0]) {
+
+ case 1: // shift
+ //this.shiftCount++;
+
+ stack.push(symbol);
+ vstack.push(this.lexer.yytext);
+ lstack.push(this.lexer.yylloc);
+ stack.push(action[1]); // push state
+ symbol = null;
+ if (!preErrorSymbol) { // normal execution/no error
+ yyleng = this.lexer.yyleng;
+ yytext = this.lexer.yytext;
+ yylineno = this.lexer.yylineno;
+ yyloc = this.lexer.yylloc;
+ if (recovering > 0)
+ recovering--;
+ } else { // error just occurred, resume old lookahead f/ before error
+ symbol = preErrorSymbol;
+ preErrorSymbol = null;
+ }
+ break;
+
+ case 2: // reduce
+ //this.reductionCount++;
+
+ len = this.productions_[action[1]][1];
+
+ // perform semantic action
+ yyval.$ = vstack[vstack.length-len]; // default to $$ = $1
+ // default location, uses first token for firsts, last for lasts
+ yyval._$ = {
+ first_line: lstack[lstack.length-(len||1)].first_line,
+ last_line: lstack[lstack.length-1].last_line,
+ first_column: lstack[lstack.length-(len||1)].first_column,
+ last_column: lstack[lstack.length-1].last_column
+ };
+ r = this.performAction.call(yyval, yytext, yyleng, yylineno, this.yy, action[1], vstack, lstack);
+
+ if (typeof r !== 'undefined') {
+ return r;
+ }
+
+ // pop off stack
+ if (len) {
+ stack = stack.slice(0,-1*len*2);
+ vstack = vstack.slice(0, -1*len);
+ lstack = lstack.slice(0, -1*len);
+ }
+
+ stack.push(this.productions_[action[1]][0]); // push nonterminal (reduce)
+ vstack.push(yyval.$);
+ lstack.push(yyval._$);
+ // goto new state = table[STATE][NONTERMINAL]
+ newState = table[stack[stack.length-2]][stack[stack.length-1]];
+ stack.push(newState);
+ break;
+
+ case 3: // accept
+ return true;
+ }
+
+ }
+
+ return true;
+}};/* Jison generated lexer */
+var lexer = (function(){
+
+var lexer = ({EOF:1,
+parseError:function parseError(str, hash) {
+ if (this.yy.parseError) {
+ this.yy.parseError(str, hash);
+ } else {
+ throw new Error(str);
+ }
+ },
+setInput:function (input) {
+ this._input = input;
+ this._more = this._less = this.done = false;
+ this.yylineno = this.yyleng = 0;
+ this.yytext = this.matched = this.match = '';
+ this.conditionStack = ['INITIAL'];
+ this.yylloc = {first_line:1,first_column:0,last_line:1,last_column:0};
+ return this;
+ },
+input:function () {
+ var ch = this._input[0];
+ this.yytext+=ch;
+ this.yyleng++;
+ this.match+=ch;
+ this.matched+=ch;
+ var lines = ch.match(/\n/);
+ if (lines) this.yylineno++;
+ this._input = this._input.slice(1);
+ return ch;
+ },
+unput:function (ch) {
+ this._input = ch + this._input;
+ return this;
+ },
+more:function () {
+ this._more = true;
+ return this;
+ },
+pastInput:function () {
+ var past = this.matched.substr(0, this.matched.length - this.match.length);
+ return (past.length > 20 ? '...':'') + past.substr(-20).replace(/\n/g, "");
+ },
+upcomingInput:function () {
+ var next = this.match;
+ if (next.length < 20) {
+ next += this._input.substr(0, 20-next.length);
+ }
+ return (next.substr(0,20)+(next.length > 20 ? '...':'')).replace(/\n/g, "");
+ },
+showPosition:function () {
+ var pre = this.pastInput();
+ var c = new Array(pre.length + 1).join("-");
+ return pre + this.upcomingInput() + "\n" + c+"^";
+ },
+next:function () {
+ if (this.done) {
+ return this.EOF;
+ }
+ if (!this._input) this.done = true;
+
+ var token,
+ match,
+ col,
+ lines;
+ if (!this._more) {
+ this.yytext = '';
+ this.match = '';
+ }
+ var rules = this._currentRules();
+ for (var i=0;i < rules.length; i++) {
+ match = this._input.match(this.rules[rules[i]]);
+ if (match) {
+ lines = match[0].match(/\n.*/g);
+ if (lines) this.yylineno += lines.length;
+ this.yylloc = {first_line: this.yylloc.last_line,
+ last_line: this.yylineno+1,
+ first_column: this.yylloc.last_column,
+ last_column: lines ? lines[lines.length-1].length-1 : this.yylloc.last_column + match[0].length}
+ this.yytext += match[0];
+ this.match += match[0];
+ this.matches = match;
+ this.yyleng = this.yytext.length;
+ this._more = false;
+ this._input = this._input.slice(match[0].length);
+ this.matched += match[0];
+ token = this.performAction.call(this, this.yy, this, rules[i],this.conditionStack[this.conditionStack.length-1]);
+ if (token) return token;
+ else return;
+ }
+ }
+ if (this._input === "") {
+ return this.EOF;
+ } else {
+ this.parseError('Lexical error on line '+(this.yylineno+1)+'. Unrecognized text.\n'+this.showPosition(),
+ {text: "", token: null, line: this.yylineno});
+ }
+ },
+lex:function lex() {
+ var r = this.next();
+ if (typeof r !== 'undefined') {
+ return r;
+ } else {
+ return this.lex();
+ }
+ },
+begin:function begin(condition) {
+ this.conditionStack.push(condition);
+ },
+popState:function popState() {
+ return this.conditionStack.pop();
+ },
+_currentRules:function _currentRules() {
+ return this.conditions[this.conditionStack[this.conditionStack.length-1]].rules;
+ },
+topState:function () {
+ return this.conditionStack[this.conditionStack.length-2];
+ },
+pushState:function begin(condition) {
+ this.begin(condition);
+ }});
+lexer.performAction = function anonymous(yy,yy_,$avoiding_name_collisions,YY_START) {
+
+var YYSTATE=YY_START;
+switch($avoiding_name_collisions) {
+case 0:/* skip whitespace */
+break;
+case 1:return 20
+break;
+case 2:return 19
+break;
+case 3:return 8
+break;
+case 4:return 9
+break;
+case 5:return 6
+break;
+case 6:return 7
+break;
+case 7:return 11
+break;
+case 8:return 13
+break;
+case 9:return 10
+break;
+case 10:return 12
+break;
+case 11:return 14
+break;
+case 12:return 15
+break;
+case 13:return 16
+break;
+case 14:return 17
+break;
+case 15:return 18
+break;
+case 16:return 5
+break;
+case 17:return 'INVALID'
+break;
+}
+};
+lexer.rules = [/^\s+/,/^[0-9]+(\.[0-9]+)?\b/,/^n\b/,/^\|\|/,/^&&/,/^\?/,/^:/,/^<=/,/^>=/,/^</,/^>/,/^!=/,/^==/,/^%/,/^\(/,/^\)/,/^$/,/^./];
+lexer.conditions = {"INITIAL":{"rules":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17],"inclusive":true}};return lexer;})()
+parser.lexer = lexer;
+return parser;
+})();
+// End parser
+
+ // Handle node, amd, and global systems
+ if (typeof exports !== 'undefined') {
+ if (typeof module !== 'undefined' && module.exports) {
+ exports = module.exports = Jed;
+ }
+ exports.Jed = Jed;
+ }
+ else {
+ if (typeof define === 'function' && define.amd) {
+ define('jed', function() {
+ return Jed;
+ });
+ }
+ // Leak a global regardless of module system
+ root['Jed'] = Jed;
+ }
+
+})(this);
View
43 frontend/index.html
@@ -23,18 +23,18 @@
<div class="navbar-inner">
<a class="brand" href="#">UTT</a>
<ul class="nav">
- <li <% if (route == 'test') { %>class="active"<% } %> id="test-nav-button"><a href="#/test">Test</a></li>
- <li <% if (route == 'result') { %>class="active"<% } %> id="result-nav-button"><a href="#/result">Results</a></li>
+ <li <% if (route == 'test') { %>class="active"<% } %> id="test-nav-button"><a href="#/test"><%= _test %></a></li>
+ <li <% if (route == 'result') { %>class="active"<% } %> id="result-nav-button"><a href="#/result"><%= _results %></a></li>
</ul>
<% if (route == 'home') { %>
<ul class="nav pull-right">
<li class="dropdown">
- <a class="dropdown-toggle" data-toggle="dropdown"><i class="icon-globe"></i> Language
+ <a class="dropdown-toggle" data-toggle="dropdown"><i class="icon-globe"></i> <%= _language %>
<b class="caret"></b>
</a>
<ul class="dropdown-menu">
- <li <% if (language == 'en') { %>class="disabled"<% } %>><a class="language" data-language="en">English</a>
- <li <% if (language == 'nb') { %>class="disabled"<% } %>><a class="language" data-language="nb">Norwegian Bokmål</a>
+ <li <% if (language == 'en') { %>class="disabled"<% } %>><a class="language" data-language="en"><%= _languageEnglish %></a>
+ <li <% if (language == 'no') { %>class="disabled"<% } %>><a class="language" data-language="no"><%= _languageNorwegian %></a>
</ul>
</li>
</ul>
@@ -43,7 +43,7 @@
<form class="navbar-form pull-right" onsubmit="return false;">
<div class="input-append">
<input <% if (state == 'loading') { %>disabled="true"<% } %>id="web-page" type="text" placeholder="Web page" value="<%= webPage %>" class="span4">
- <button <% if (state == 'loading') { %>class="btn disablled" disabled="true"<% } else { %>class="btn" <% } %> id="set-web-page" value="Set">Set</button>
+ <button <% if (state == 'loading') { %>class="btn disablled" disabled="true"<% } else { %>class="btn" <% } %> id="set-web-page" value="Set"><%= _set %></button>
</div>
</form>
<% } %>
@@ -54,21 +54,20 @@
<script type="text/template" id="home-template">
<div class="row">
<div class="span12">
- <h1>User Testing Tool</h1>
- <p class="lead">The User Testing Tool (UTT) is a research demonstrator to combine user testing with automated testing services to improve the accessibility and usability of web sites.</p>
- <p class="lead">
- To get started, enter a web page to test.
- </p>
+ <h1><%= userTestingTool %></h1>
+ <p class="lead"><%= description %></p>
+ <p class="lead"><%= enterWebPage %></p>
<form class="form-inline" onsubmit="return false;">
<fieldset>
<div class="input-append">
- <input id="web-page-2" type="text" placeholder="Web page" value="<%= webPage %>" class="span4 input-large"> <button id="set-web-page-2" class="btn btn-primary">Start testing</a>
+ <input id="web-page-2" type="text" placeholder="Web page" value="<%= webPage %>" class="span4 input-large">
+ <button id="set-web-page-2" class="btn btn-primary"><%= startTesting %></a>
</div>
<fieldset>
</form>
<hr>
<p>
- To report an issue, please send us an <a href="mailto:alexander.teinum@tingtun.no">email</a>, or create an issue on <a href="https://github.com/alexanderte/utt/issues">GitHub</a>.
+ <%= reportIssue %>
</p>
</div>
</div>
@@ -180,14 +179,16 @@
</script>
<script type="text/template" id="test-case-template">
- <p class="lead"><%= question %></p>
- <p>
- <a href="<%= previousUrl %>" class="btn <%= previousExtraClass %>" style="margin-right: 24px;">Previous</a>
- <% _.each(answers, function(answer) { %>
- <label class="radio inline"><input type="radio" id="answer" name="answer" value="<%= answer %>"><%= answer %></label>
- <% }); %>
- <a href="<%= nextUrl %>" class="btn btn-primary" style="margin-left: 24px;">Next</a>
- </p>
+ <p class="lead"><%= _question %></p>
+ <div style="margin-bottom: 8px;">
+ <a href="<%= previousUrl %>" class="btn btn-link" style="margin-right: 1em;"><i class="icon-angle-left"></i> <%= _previousTest %></a>
+ <div class="btn-group">
+ <% _.each(answers, function(answer) { %>
+ <button class="btn" value="<%= answer.value %>"><%= answer._value %></button>
+ <% }); %>
+ </div>
+ <a href="<%= nextUrl %>" class="btn btn-link" style="margin-left: 1em;"><%= _skipTest %> <i class="icon-angle-right"></i></a>
+ </div>
</script>
<script data-main="main" type="text/javascript" charset="utf-8" src="require.js"></script>
View
97 frontend/locale/en.json
@@ -0,0 +1,97 @@
+{
+ "en": {
+ "": {},
+ "test_answer_yes": [
+ null,
+ "Yes"
+ ],
+ "test_answer_no": [
+ null,
+ "No"
+ ],
+ "test_answer_unsure": [
+ null,
+ "Unsure"
+ ],
+ "test_answer_i_dont_speak_this_language": [
+ null,
+ "I don’t speak this language"
+ ],
+ "test_previous_test": [
+ null,
+ "Previous"
+ ],
+ "test_skip_test": [
+ null,
+ "Skip"
+ ],
+ "test_question_SC2.4.2-1-11": [
+ null,
+ "Does the page title “%s” describe the content of this web page?"
+ ],
+ "test_question_SC2.4.4-2-11": [
+ null,
+ "Does the link text “%s” describe the link purpose?"
+ ],
+ "test_question_SC2.4.4-2-12": [
+ null,
+ "Does the link title “%s” describe the link purpose?"
+ ],
+ "test_question_SC2.4.6-1-11": [
+ null,
+ "Does the heading “%s” describe the section it belongs to?"
+ ],
+ "test_question_SC3.1.2-2-11": [
+ null,
+ "Does the text “%s” correlate with the specified language %s (%s)?"
+ ],
+ "navbar_test": [
+ null,
+ "Test"
+ ],
+ "navbar_results": [
+ null,
+ "Results"
+ ],
+ "navbar_language": [
+ null,
+ "Language"
+ ],
+ "navbar_language_english": [
+ null,
+ "English"
+ ],
+ "navbar_language_norwegian": [
+ null,
+ "Norwegian"
+ ],
+ "navbar_set": [
+ null,
+ "Set"
+ ],
+ "home_user_testing_tool": [
+ null,
+ "User Testing Tool"
+ ],
+ "home_description": [
+ null,
+ "The User Testing Tool (UTT) is a research demonstrator to combine user testing with automated testing services to improve the accessibility and usability of web sites."
+ ],
+ "home_enter_web_page": [
+ null,
+ "To get started, enter a web page to test."
+ ],
+ "home_start_testing": [
+ null,
+ "Start testing"
+ ],
+ "home_report_issue": [
+ null,
+ "To report an issue, please send us an <a href=\"mailto:alexander.teinum@tingtun.no\">email</a>, or create an issue on <a href=\"https://github.com/alexanderte/utt/issues\">GitHub</a>."
+ ],
+ "question_SC4.1.2-2-11": [
+ null,
+ "Please check the name of the frame\";\"<p>Human input is necessary to verify, that the <samp>title</samp> of the <samp>frame</samp> or <samp>iframe</samp> describes its contents.</p>"
+ ]
+ }
+}
View
71 locale/en.po → frontend/locale/en.po
@@ -1,23 +1,74 @@
-answer_yes
+msgid ""
+msgstr ""
+
+msgid "test_answer_yes"
msgstr "Yes"
-answer_no
+
+msgid "test_answer_no"
msgstr "No"
-answer_unsure
+
+msgid "test_answer_unsure"
msgstr "Unsure"
-answer_i_dont_speak_language
-msgstr "I don’t speak %s"
-msgid "question_SC2.4.2-1-11"
+msgid "test_answer_i_dont_speak_this_language"
+msgstr "I don’t speak this language"
+
+msgid "test_previous_test"
+msgstr "Previous"
+
+msgid "test_skip_test"
+msgstr "Skip"
+
+msgid "test_question_SC2.4.2-1-11"
msgstr "Does the page title “%s” describe the content of this web page?"
-msgid "question_SC2.4.4-2-11"
+
+msgid "test_question_SC2.4.4-2-11"
msgstr "Does the link text “%s” describe the link purpose?"
-msgid "question_SC2.4.4-2-12"
+
+msgid "test_question_SC2.4.4-2-12"
msgstr "Does the link title “%s” describe the link purpose?"
-msgid "question_SC2.4.6-1-11"
+
+msgid "test_question_SC2.4.6-1-11"
msgstr "Does the heading “%s” describe the section it belongs to?"
-msgid "question_SC3.1.2-2-11"
+
+msgid "test_question_SC3.1.2-2-11"
msgstr "Does the text “%s” correlate with the specified language %s (%s)?"
+msgid "navbar_test"
+msgstr "Test"
+
+msgid "navbar_results"
+msgstr "Results"
+
+msgid "navbar_language"
+msgstr "Language"
+
+msgid "navbar_language_english"
+msgstr "English"
+
+msgid "navbar_language_norwegian"
+msgstr "Norwegian"
+
+msgid "navbar_set"
+msgstr "Set"
+
+msgid "home_user_testing_tool"
+msgstr "User Testing Tool"
+
+msgid "home_description"
+msgstr "The User Testing Tool (UTT) is a research demonstrator to combine user testing with automated testing services to improve the accessibility and usability of web sites."
+
+msgid "home_enter_web_page"
+msgstr "To get started, enter a web page to test."
+
+msgid "home_start_testing"
+msgstr "Start testing"
+
+msgid "home_report_issue"
+msgstr "To report an issue, please send us an <a href="mailto:alexander.teinum@tingtun.no">email</a>, or create an issue on <a href="https://github.com/alexanderte/utt/issues">GitHub</a>."
+
+msgstr "Please check the link text of the [el]area[/el]";"<p>Human input is necessary to verify, that the alternative text of the <samp>area</samp> serves the same purpose as the selectable area of the image.</p>"
+
# The following are not implemented
msgid "question_SC2.4.4-1-11"
msgstr "Please check the link text of the [el]area[/el]";"<p>Human input is necessary to verify, that the alternative text of the <samp>area</samp> serves the same purpose as the selectable area of the image.</p>"
View
105 frontend/locale/no.json
@@ -0,0 +1,105 @@
+{
+ "no": {
+ "": {},
+ "test_answer_yes": [
+ null,
+ "Ja"
+ ],
+ "test_answer_no": [
+ null,
+ "Nei"
+ ],
+ "test_answer_unsure": [
+ null,
+ "Usikker"
+ ],
+ "test_answer_i_dont_speak_this_language": [
+ null,
+ "Jeg snakker ikke dette språket"
+ ],
+ "test_previous_test": [
+ null,
+ "Forrige"
+ ],
+ "test_skip_test": [
+ null,
+ "Hopp over"
+ ],
+ "test_question_SC2.4.2-1-11": [
+ null,
+ "Beskriver sidetittelen «%s» innholdet på denne nettsiden?"
+ ],
+ "test_question_SC2.4.4-2-11": [
+ null,
+ "Beskriver lenketeksten «%s» hensikten til lenken?"
+ ],
+ "test_question_SC2.4.4-2-12": [
+ null,
+ "Beskriver lenketittelen «%s» hensikten med lenken?"
+ ],
+ "test_question_SC2.4.6-1-11": [
+ null,
+ "Beskriver overskiften «%s» seksjonen den tilhører?"
+ ],
+ "test_question_SC3.1.2-2-11": [
+ null,
+ "Korrelerer teksten «%s» med det spesifiserte språket %s (%s)?"
+ ],
+ "test_previous": [
+ null,
+ "Tilbake"
+ ],
+ "test_next": [
+ null,
+ "Frem"
+ ],
+ "navbar_test": [
+ null,
+ "Test"
+ ],
+ "navbar_results": [
+ null,
+ "Resultater"
+ ],
+ "navbar_language": [
+ null,
+ "Språk"
+ ],
+ "navbar_language_english": [
+ null,
+ "Engelsk"
+ ],
+ "navbar_language_norwegian": [
+ null,
+ "Norsk"
+ ],
+ "navbar_set": [
+ null,
+ "Endre"
+ ],
+ "home_user_testing_tool": [
+ null,
+ "User Testing Tool"
+ ],
+ "home_description": [
+ null,
+ "User Testing Tool (UTT) er en forskningsdemonstrator som kombinerer brukertesting med automatiserte testtjenester for å forbedre tilgjengeligheten og brukervennligheten til nettsteder."
+ ],
+ "home_enter_web_page": [
+ null,
+ "For å sette i gang, skriv inn en nettside som du vil teste."
+ ],
+ "home_start_testing": [
+ null,
+ "Begynn testingen"
+ ],
+ "home_report_issue": [
+ null,
+ "For å rapportere et problem, vennligst send oss en <a href=\"mailto:alexander.teinum@tingtun.no\">e-post</a> eller opprett en sak på <a href=\"https://github.com/alexanderte/utt/issues\">GitHub</a>."
+ ],
+ "question_SC4.1.2-2-11": [
+ null,
+ "Please check the name of the frame\";\"<p>Human input is necessary to verify, that the <samp>title</samp> of the <samp>frame</samp> or <samp>iframe</samp> describes its contents.</p>"
+ ]
+ }
+}
View
100 frontend/locale/no.po
@@ -0,0 +1,100 @@
+msgid ""
+msgstr ""
+
+msgid "test_answer_yes"
+msgstr "Ja"
+
+msgid "test_answer_no"
+msgstr "Nei"
+
+msgid "test_answer_unsure"
+msgstr "Usikker"
+
+msgid "test_answer_i_dont_speak_this_language"
+msgstr "Jeg snakker ikke dette språket"
+
+msgid "test_previous_test"
+msgstr "Forrige"
+
+msgid "test_skip_test"
+msgstr "Hopp over"
+
+msgid "test_question_SC2.4.2-1-11"
+msgstr "Beskriver sidetittelen «%s» innholdet på denne nettsiden?"
+
+msgid "test_question_SC2.4.4-2-11"
+msgstr "Beskriver lenketeksten «%s» hensikten til lenken?"
+
+msgid "test_question_SC2.4.4-2-12"
+msgstr "Beskriver lenketittelen «%s» hensikten med lenken?"
+
+msgid "test_question_SC2.4.6-1-11"
+msgstr "Beskriver overskiften «%s» seksjonen den tilhører?"
+
+msgid "test_question_SC3.1.2-2-11"
+msgstr "Korrelerer teksten «%s» med det spesifiserte språket %s (%s)?"
+
+msgid "test_previous"
+msgstr "Tilbake"
+
+msgid "test_next"
+msgstr "Frem"
+
+msgid "navbar_test"
+msgstr "Test"
+
+msgid "navbar_results"
+msgstr "Resultater"
+
+msgid "navbar_language"
+msgstr "Språk"
+
+msgid "navbar_language_english"
+msgstr "Engelsk"
+
+msgid "navbar_language_norwegian"
+msgstr "Norsk"
+
+msgid "navbar_set"
+msgstr "Endre"
+
+msgid "home_user_testing_tool"
+msgstr "User Testing Tool"
+
+msgid "home_description"
+msgstr "User Testing Tool (UTT) er en forskningsdemonstrator som kombinerer brukertesting med automatiserte testtjenester for å forbedre tilgjengeligheten og brukervennligheten til nettsteder."
+
+msgid "home_enter_web_page"
+msgstr "For å sette i gang, skriv inn en nettside som du vil teste."
+
+msgid "home_start_testing"
+msgstr "Begynn testingen"
+
+msgid "home_report_issue"
+msgstr "For å rapportere et problem, vennligst send oss en <a href="mailto:alexander.teinum@tingtun.no">e-post</a> eller opprett en sak på <a href="https://github.com/alexanderte/utt/issues">GitHub</a>."
+
+msgstr "Please check the link text of the [el]area[/el]";"<p>Human input is necessary to verify, that the alternative text of the <samp>area</samp> serves the same purpose as the selectable area of the image.</p>"
+
+# The following are not implemented
+msgid "question_SC2.4.4-1-11"
+msgstr "Please check the link text of the [el]area[/el]";"<p>Human input is necessary to verify, that the alternative text of the <samp>area</samp> serves the same purpose as the selectable area of the image.</p>"
+msgid "question_SC2.4.4-2-13"
+msgstr "Please check the link context";"<p>The link is a duplicate, and human input is necessary to verify, that the purpose of the link is identifiable by its context.</p>"
+msgid "question_SC2.4.6-2-11"
+msgstr "Please check the label";"<p>Human input is necessary to verify, that the <samp>label</samp> describes the purpose of the form control.</p>"
+msgid "question_SC3.1.1-1-11"
+msgstr "Please check the main language of the page";"<p>Human input is necessary to verify, that the main language specified for the page corresponds to the mainly used language.</p>"
+msgid "question_SC3.2.2-1-11"
+msgstr "Please check that implicit changes of context are described";"<p>Human input is necessary to verify, that the submit button is the only way to initiate a change of context with the form or that there is a description of the change before the initiating form control.</p>"
+msgid "question_SC3.2.2-3-11"
+msgstr "Please check that a change of context, initiated within or by the form, is described";"<p>Human input is necessary to verify, that the change of context is described prior to the form or initiating form control.</p>"
+msgid "question_SC3.2.2-3-12"
+msgstr "Please check that a change of context is described";"<p>Human input is necessary to verify, that a change of context is described prior to the initiating form control.</p>"
+msgid "question_SC3.3.2-2-11"
+msgstr "Please check the labelling title";"<p>Human input is necessary to verify, that the <samp>title</samp> describes the purpose of the form control.</p>"
+msgid "question_SC3.3.2-3-11"
+msgstr "Please check the labelling button";"<p>Human input is necessary to verify, that the text value of the button describes the purpose of the form control.</p>"
+msgid "question_SC3.3.2-6-11"
+msgstr "Please check the [el]legend[/el]";"<p>Human input is necessary to verify, that the <samp>legend</samp> describes the group formed by the <samp>fieldset</samp>.</p>"
+msgid "question_SC4.1.2-2-11"
+msgstr "Please check the name of the frame";"<p>Human input is necessary to verify, that the <samp>title</samp> of the <samp>frame</samp> or <samp>iframe</samp> describes its contents.</p>"
View
3  frontend/main.coffee
@@ -6,6 +6,7 @@ requirejs.config {
backbone: 'deps/backbone'
underscore: '//cdnjs.cloudflare.com/ajax/libs/underscore.js/1.4.4/underscore-min'
socketio: '//localhost:4563/socket.io/socket.io'
+ jed: 'deps/jed'
}
shim: {
'bootstrap': {
@@ -24,7 +25,7 @@ requirejs.config {
}
}
-require ['backbone', 'socketio', 'router', 'models/test-run', 'views/views', 'bootstrap'], (Backbone, io, Router, TestRun, Views) ->
+require ['backbone', 'socketio', 'router', 'models/test-run', 'views/views', 'bootstrap', 'jed'], (Backbone, io, Router, TestRun, Views) ->
router = new Router()
socket = io.connect 'http://localhost:4563'
testRun = new TestRun(socket)
View
50 frontend/models/test-run.coffee
@@ -1,4 +1,4 @@
-define ['backbone', 'socketio', 'collections/tests'], (Backbone, io, Tests) ->
+define ['backbone', 'socketio', 'collections/tests', 'jquery', 'jed'], (Backbone, io, Tests, $) ->
Backbone.Model.extend {
defaults: {
'webPage': 'http://www.tingtun.no/'
@@ -6,7 +6,10 @@ define ['backbone', 'socketio', 'collections/tests'], (Backbone, io, Tests) ->
'state': 'loading'
'language': 'en'
'route': 'home'
+ 'jed': undefined
}
+ foo: () ->
+ return 'bar'
verifyTests: () ->
this.get('tests').where({category: 'verify'})
nextTest: () ->
@@ -14,22 +17,35 @@ define ['backbone', 'socketio', 'collections/tests'], (Backbone, io, Tests) ->
getCurrentTest: () ->
this.verifyTests()[this.get('currentTest')]
+ updateLanguage: (thisArg, callback) ->
+ that = this
+ $.getJSON 'locale/' + that.get('language') + '.json', (data) ->
+ console.log 1
+ that.set 'jed', new Jed({ locale_data: { messages: data[that.get('language')] } })
+ console.log that.get('jed').translate('question_SC3.1.2-2-11').fetch(['foo', 'bar', 'baz'])
+ if callback
+ callback.apply(thisArg)
initialize: (socket) ->
- this.set 'socket', socket
- this.set 'tests', []
+ _.extend(this, Backbone.Events)
+ console.log 0
+ this.updateLanguage this, () ->
+ console.log 2
+ this.set 'socket', socket
+ this.set 'tests', []
- socket.emit('get tests', this.get('webPage'))
+ socket.emit('get tests', this.get('webPage'))
- that = this
- socket.on('tests', (data) ->
- if data == null
- that.set('state', 'error')
- else
- that.set 'tests', new Tests(data)
- that.set('state', 'loaded')
- )
+ that = this
+ socket.on('tests', (data) ->
+ if data == null
+ that.set('state', 'error')
+ else
+ that.set 'tests', new Tests(data)
+ that.set('state', 'loaded')
+ )
- this.bind('change:webPage', this.fetchTests, this)
+ this.bind('change:webPage', this.fetchTests, this)
+ this.trigger('appLoaded')
fetchTests: () ->
this.set('state', 'loading')
this.get('socket').emit('get tests', this.get('webPage'))
@@ -52,5 +68,13 @@ define ['backbone', 'socketio', 'collections/tests'], (Backbone, io, Tests) ->
this.set 'webPage', addProtocol(url)
setLanguage: (languageCode) ->
+ console.log(languageCode)
this.set 'language', languageCode
+ this.updateLanguage(this, () ->
+ this.trigger('languageUpdated')
+ )
+ translate: (str, args) ->
+ console.log('str')
+ console.log(str)
+ this.get('jed').translate(str).fetch(args)
}
View
25 frontend/views/home.coffee
@@ -1,4 +1,4 @@
-define ['backbone', 'underscore', 'jquery'], (Backbone, _, $) ->
+define ['backbone', 'underscore', 'jquery', 'jed'], (Backbone, _, $) ->
Backbone.View.extend {
el: '#home-view'
initialize: () ->
@@ -15,6 +15,16 @@ define ['backbone', 'underscore', 'jquery'], (Backbone, _, $) ->
$('#web-page-2').val(this.model.get('webPage'))
, this)
+ this.model.on('languageUpdated', () ->
+ console.log('languageUpdated')
+ this.render()
+ , this)
+
+ this.model.on('appLoaded', () ->
+ console.log(3)
+ this.render()
+ , this)
+
this.model.bind('change:state', () ->
if this.model.get('state') == 'loaded' or this.model.get('state') == 'error'
$('#web-page-2').removeClass 'disabled'
@@ -30,7 +40,18 @@ define ['backbone', 'underscore', 'jquery'], (Backbone, _, $) ->
, this)
render: () ->
- this.$el.html(_.template($('#home-template').html(), { webPage: this.model.get('webPage') }))
+ # TODO: Prevent render on route
+ if this.model.get('jed') == undefined
+ return
+ console.log 4
+ this.$el.html(_.template($('#home-template').html(), {
+ webPage: this.model.get('webPage')
+ userTestingTool: this.model.translate('home_user_testing_tool')
+ description: this.model.translate('home_description')
+ enterWebPage: this.model.translate('home_enter_web_page')
+ startTesting: this.model.translate('home_start_testing')
+ reportIssue: this.model.translate('home_report_issue')
+ }))
$('#web-page-2').focus()
events: { 'click button#set-web-page-2': 'setWebPage' }
setWebPage: () ->
View
13 frontend/views/navbar.coffee
@@ -2,7 +2,6 @@ define ['jquery', 'underscore', 'backbone'], ($, _, Backbone) ->
Backbone.View.extend {
el: '#navbar-view'
initialize: () ->
- do this.render
this.model.bind('change:route', () ->
do this.render
, this)
@@ -15,12 +14,24 @@ define ['jquery', 'underscore', 'backbone'], ($, _, Backbone) ->
this.model.bind('change:language', () ->
do this.render
, this)
+ this.model.on('languageUpdated', () ->
+ this.render()
+ , this)
+ this.model.on('appLoaded', () ->
+ this.render()
+ , this)
render: () ->
this.$el.html(_.template($('#navbar-template').html(), {
webPage: this.model.get('webPage'),
language: this.model.get('language')
state: this.model.get('state')
route: this.model.get('route')
+ _test: this.model.translate('navbar_test')
+ _results: this.model.translate('navbar_results')
+ _language: this.model.translate('navbar_language')
+ _languageEnglish: this.model.translate('navbar_language_english')
+ _languageNorwegian: this.model.translate('navbar_language_norwegian')
+ _set: this.model.translate('navbar_set')
}))
events: {
'click button#set-web-page': 'setWebPage'
View
10 frontend/views/test.coffee
@@ -30,12 +30,18 @@ define ['jquery', 'underscore', 'backbone'], ($, _, Backbone) ->
)
else
test = this.model.getCurrentTest()
+ console.log(test.get('answers'))
+
+ answers = _.map(test.get('answers'), (a) -> { value: a, _value: this.model.translate('test_answer_' + a) })
+ console.log(answers)
this.$el.html(
_.template($('#test-progress').html(), { 'progress': this.model.progress() }) +
_.template($(test.get('template')).html(), {
- 'question': test.get('question')
- 'answers': test.get('answers')
+ '_question': this.model.translate('test_question_' + test.get('testId') + '-' + test.get('testResultId'), test.get('questionValues'))
+ '_previousTest': this.model.translate('test_previous_test')
+ '_skipTest': this.model.translate('test_skip_test')
+ 'answers': answers
'nextUrl': this.nextUrl()
'previousUrl': this.previousUrl()
'previousExtraClass': this.previousExtraClass()
View
13 locale/en.json
@@ -1,13 +0,0 @@
-{
- "default": {
- "question_SC3.1.2-2-11": [
- null,
- "Does the text “%s” correlate with the specified language %s (%s)?"
- ],
- "question_SC4.1.2-2-11": [
- null,
- "Please check the name of the frame\";\"<p>Human input is necessary to verify, that the <samp>title</samp> of the <samp>frame</samp> or <samp>iframe</samp> describes its contents.</p>"
- ],
- "": {}
- }
-}
Please sign in to comment.
Something went wrong with that request. Please try again.