diff --git a/main/webapp/modules/core/MOD-INF/controller.js b/main/webapp/modules/core/MOD-INF/controller.js index 40a898bb3..ad68f1c23 100644 --- a/main/webapp/modules/core/MOD-INF/controller.js +++ b/main/webapp/modules/core/MOD-INF/controller.js @@ -345,16 +345,22 @@ function init() { "externals/jquery.cookie.js", "externals/jquery-ui/jquery-ui-1.10.3.custom.js", "externals/date.js", + + "externals/CLDRPluralRuleParser.js", "externals/jquery.i18n.js", + "externals/jquery.i18n.messagestore.js", + "externals/jquery.i18n.fallbacks.js", + "externals/jquery.i18n.parser.js", + "externals/jquery.i18n.emitter.js", + "externals/jquery.i18n.language.js", + "externals/languages/fi.js", + "externals/languages/ru.js", + "externals/tablesorter/jquery.tablesorter.min.js", "externals/moment-with-locales.min.js", "externals/select2/select2.min.js", "externals/jquery.lavalamp.min.js", - "externals/jquery.i18n.messagestore.js", - "externals/jquery.i18n.emitter.js", - "externals/jquery.i18n.parser.js", - "externals/jquery.i18n.emitter.js", - "externals/jquery.i18n.language.js", + "scripts/util/misc.js", "scripts/util/url.js", @@ -550,11 +556,17 @@ function init() { "externals/jquery-ui/jquery-ui-1.10.3.custom.js", "externals/imgareaselect/jquery.imgareaselect.js", "externals/date.js", + + "externals/CLDRPluralRuleParser.js", "externals/jquery.i18n.js", "externals/jquery.i18n.messagestore.js", + "externals/jquery.i18n.fallbacks.js", "externals/jquery.i18n.parser.js", "externals/jquery.i18n.emitter.js", "externals/jquery.i18n.language.js", + "externals/languages/fi.js", + "externals/languages/ru.js", + "externals/underscore-min.js", "scripts/preferences.js", ] diff --git a/main/webapp/modules/core/externals/CLDRPluralRuleParser.js b/main/webapp/modules/core/externals/CLDRPluralRuleParser.js new file mode 100644 index 000000000..f76459e57 --- /dev/null +++ b/main/webapp/modules/core/externals/CLDRPluralRuleParser.js @@ -0,0 +1,607 @@ +/** + * cldrpluralparser.js + * A parser engine for CLDR plural rules. + * + * Copyright 2012-2014 Santhosh Thottingal and other contributors + * Released under the MIT license + * http://opensource.org/licenses/MIT + * + * @source https://github.com/santhoshtr/CLDRPluralRuleParser + * @author Santhosh Thottingal + * @author Timo Tijhof + * @author Amir Aharoni + */ + +/** + * Evaluates a plural rule in CLDR syntax for a number + * @param {string} rule + * @param {integer} number + * @return {boolean} true if evaluation passed, false if evaluation failed. + */ + +// UMD returnExports https://github.com/umdjs/umd/blob/master/returnExports.js +(function(root, factory) { + if (typeof define === 'function' && define.amd) { + // AMD. Register as an anonymous module. + define(factory); + } else if (typeof exports === 'object') { + // Node. Does not work with strict CommonJS, but + // only CommonJS-like environments that support module.exports, + // like Node. + module.exports = factory(); + } else { + // Browser globals (root is window) + root.pluralRuleParser = factory(); + } +}(this, function() { + +function pluralRuleParser(rule, number) { + 'use strict'; + + /* + Syntax: see http://unicode.org/reports/tr35/#Language_Plural_Rules + ----------------------------------------------------------------- + condition = and_condition ('or' and_condition)* + ('@integer' samples)? + ('@decimal' samples)? + and_condition = relation ('and' relation)* + relation = is_relation | in_relation | within_relation + is_relation = expr 'is' ('not')? value + in_relation = expr (('not')? 'in' | '=' | '!=') range_list + within_relation = expr ('not')? 'within' range_list + expr = operand (('mod' | '%') value)? + operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w' + range_list = (range | value) (',' range_list)* + value = digit+ + digit = 0|1|2|3|4|5|6|7|8|9 + range = value'..'value + samples = sampleRange (',' sampleRange)* (',' ('…'|'...'))? + sampleRange = decimalValue '~' decimalValue + decimalValue = value ('.' value)? + */ + + // We don't evaluate the samples section of the rule. Ignore it. + rule = rule.split('@')[0].replace(/^\s*/, '').replace(/\s*$/, ''); + + if (!rule.length) { + // Empty rule or 'other' rule. + return true; + } + + // Indicates the current position in the rule as we parse through it. + // Shared among all parsing functions below. + var pos = 0, + operand, + expression, + relation, + result, + whitespace = makeRegexParser(/^\s+/), + value = makeRegexParser(/^\d+/), + _n_ = makeStringParser('n'), + _i_ = makeStringParser('i'), + _f_ = makeStringParser('f'), + _t_ = makeStringParser('t'), + _v_ = makeStringParser('v'), + _w_ = makeStringParser('w'), + _is_ = makeStringParser('is'), + _isnot_ = makeStringParser('is not'), + _isnot_sign_ = makeStringParser('!='), + _equal_ = makeStringParser('='), + _mod_ = makeStringParser('mod'), + _percent_ = makeStringParser('%'), + _not_ = makeStringParser('not'), + _in_ = makeStringParser('in'), + _within_ = makeStringParser('within'), + _range_ = makeStringParser('..'), + _comma_ = makeStringParser(','), + _or_ = makeStringParser('or'), + _and_ = makeStringParser('and'); + + function debug() { + // console.log.apply(console, arguments); + } + + debug('pluralRuleParser', rule, number); + + // Try parsers until one works, if none work return null + function choice(parserSyntax) { + return function() { + var i, result; + + for (i = 0; i < parserSyntax.length; i++) { + result = parserSyntax[i](); + + if (result !== null) { + return result; + } + } + + return null; + }; + } + + // Try several parserSyntax-es in a row. + // All must succeed; otherwise, return null. + // This is the only eager one. + function sequence(parserSyntax) { + var i, parserRes, + originalPos = pos, + result = []; + + for (i = 0; i < parserSyntax.length; i++) { + parserRes = parserSyntax[i](); + + if (parserRes === null) { + pos = originalPos; + + return null; + } + + result.push(parserRes); + } + + return result; + } + + // Run the same parser over and over until it fails. + // Must succeed a minimum of n times; otherwise, return null. + function nOrMore(n, p) { + return function() { + var originalPos = pos, + result = [], + parsed = p(); + + while (parsed !== null) { + result.push(parsed); + parsed = p(); + } + + if (result.length < n) { + pos = originalPos; + + return null; + } + + return result; + }; + } + + // Helpers - just make parserSyntax out of simpler JS builtin types + function makeStringParser(s) { + var len = s.length; + + return function() { + var result = null; + + if (rule.substr(pos, len) === s) { + result = s; + pos += len; + } + + return result; + }; + } + + function makeRegexParser(regex) { + return function() { + var matches = rule.substr(pos).match(regex); + + if (matches === null) { + return null; + } + + pos += matches[0].length; + + return matches[0]; + }; + } + + /** + * Integer digits of n. + */ + function i() { + var result = _i_(); + + if (result === null) { + debug(' -- failed i', parseInt(number, 10)); + + return result; + } + + result = parseInt(number, 10); + debug(' -- passed i ', result); + + return result; + } + + /** + * Absolute value of the source number (integer and decimals). + */ + function n() { + var result = _n_(); + + if (result === null) { + debug(' -- failed n ', number); + + return result; + } + + result = parseFloat(number, 10); + debug(' -- passed n ', result); + + return result; + } + + /** + * Visible fractional digits in n, with trailing zeros. + */ + function f() { + var result = _f_(); + + if (result === null) { + debug(' -- failed f ', number); + + return result; + } + + result = (number + '.').split('.')[1] || 0; + debug(' -- passed f ', result); + + return result; + } + + /** + * Visible fractional digits in n, without trailing zeros. + */ + function t() { + var result = _t_(); + + if (result === null) { + debug(' -- failed t ', number); + + return result; + } + + result = (number + '.').split('.')[1].replace(/0$/, '') || 0; + debug(' -- passed t ', result); + + return result; + } + + /** + * Number of visible fraction digits in n, with trailing zeros. + */ + function v() { + var result = _v_(); + + if (result === null) { + debug(' -- failed v ', number); + + return result; + } + + result = (number + '.').split('.')[1].length || 0; + debug(' -- passed v ', result); + + return result; + } + + /** + * Number of visible fraction digits in n, without trailing zeros. + */ + function w() { + var result = _w_(); + + if (result === null) { + debug(' -- failed w ', number); + + return result; + } + + result = (number + '.').split('.')[1].replace(/0$/, '').length || 0; + debug(' -- passed w ', result); + + return result; + } + + // operand = 'n' | 'i' | 'f' | 't' | 'v' | 'w' + operand = choice([n, i, f, t, v, w]); + + // expr = operand (('mod' | '%') value)? + expression = choice([mod, operand]); + + function mod() { + var result = sequence( + [operand, whitespace, choice([_mod_, _percent_]), whitespace, value] + ); + + if (result === null) { + debug(' -- failed mod'); + + return null; + } + + debug(' -- passed ' + parseInt(result[0], 10) + ' ' + result[2] + ' ' + parseInt(result[4], 10)); + + return parseFloat(result[0]) % parseInt(result[4], 10); + } + + function not() { + var result = sequence([whitespace, _not_]); + + if (result === null) { + debug(' -- failed not'); + + return null; + } + + return result[1]; + } + + // is_relation = expr 'is' ('not')? value + function is() { + var result = sequence([expression, whitespace, choice([_is_]), whitespace, value]); + + if (result !== null) { + debug(' -- passed is : ' + result[0] + ' == ' + parseInt(result[4], 10)); + + return result[0] === parseInt(result[4], 10); + } + + debug(' -- failed is'); + + return null; + } + + // is_relation = expr 'is' ('not')? value + function isnot() { + var result = sequence( + [expression, whitespace, choice([_isnot_, _isnot_sign_]), whitespace, value] + ); + + if (result !== null) { + debug(' -- passed isnot: ' + result[0] + ' != ' + parseInt(result[4], 10)); + + return result[0] !== parseInt(result[4], 10); + } + + debug(' -- failed isnot'); + + return null; + } + + function not_in() { + var i, range_list, + result = sequence([expression, whitespace, _isnot_sign_, whitespace, rangeList]); + + if (result !== null) { + debug(' -- passed not_in: ' + result[0] + ' != ' + result[4]); + range_list = result[4]; + + for (i = 0; i < range_list.length; i++) { + if (parseInt(range_list[i], 10) === parseInt(result[0], 10)) { + return false; + } + } + + return true; + } + + debug(' -- failed not_in'); + + return null; + } + + // range_list = (range | value) (',' range_list)* + function rangeList() { + var result = sequence([choice([range, value]), nOrMore(0, rangeTail)]), + resultList = []; + + if (result !== null) { + resultList = resultList.concat(result[0]); + + if (result[1][0]) { + resultList = resultList.concat(result[1][0]); + } + + return resultList; + } + + debug(' -- failed rangeList'); + + return null; + } + + function rangeTail() { + // ',' range_list + var result = sequence([_comma_, rangeList]); + + if (result !== null) { + return result[1]; + } + + debug(' -- failed rangeTail'); + + return null; + } + + // range = value'..'value + function range() { + var i, array, left, right, + result = sequence([value, _range_, value]); + + if (result !== null) { + debug(' -- passed range'); + + array = []; + left = parseInt(result[0], 10); + right = parseInt(result[2], 10); + + for (i = left; i <= right; i++) { + array.push(i); + } + + return array; + } + + debug(' -- failed range'); + + return null; + } + + function _in() { + var result, range_list, i; + + // in_relation = expr ('not')? 'in' range_list + result = sequence( + [expression, nOrMore(0, not), whitespace, choice([_in_, _equal_]), whitespace, rangeList] + ); + + if (result !== null) { + debug(' -- passed _in:' + result); + + range_list = result[5]; + + for (i = 0; i < range_list.length; i++) { + if (parseInt(range_list[i], 10) === parseFloat(result[0])) { + return (result[1][0] !== 'not'); + } + } + + return (result[1][0] === 'not'); + } + + debug(' -- failed _in '); + + return null; + } + + /** + * The difference between "in" and "within" is that + * "in" only includes integers in the specified range, + * while "within" includes all values. + */ + function within() { + var range_list, result; + + // within_relation = expr ('not')? 'within' range_list + result = sequence( + [expression, nOrMore(0, not), whitespace, _within_, whitespace, rangeList] + ); + + if (result !== null) { + debug(' -- passed within'); + + range_list = result[5]; + + if ((result[0] >= parseInt(range_list[0], 10)) && + (result[0] < parseInt(range_list[range_list.length - 1], 10))) { + + return (result[1][0] !== 'not'); + } + + return (result[1][0] === 'not'); + } + + debug(' -- failed within '); + + return null; + } + + // relation = is_relation | in_relation | within_relation + relation = choice([is, not_in, isnot, _in, within]); + + // and_condition = relation ('and' relation)* + function and() { + var i, + result = sequence([relation, nOrMore(0, andTail)]); + + if (result) { + if (!result[0]) { + return false; + } + + for (i = 0; i < result[1].length; i++) { + if (!result[1][i]) { + return false; + } + } + + return true; + } + + debug(' -- failed and'); + + return null; + } + + // ('and' relation)* + function andTail() { + var result = sequence([whitespace, _and_, whitespace, relation]); + + if (result !== null) { + debug(' -- passed andTail' + result); + + return result[3]; + } + + debug(' -- failed andTail'); + + return null; + + } + // ('or' and_condition)* + function orTail() { + var result = sequence([whitespace, _or_, whitespace, and]); + + if (result !== null) { + debug(' -- passed orTail: ' + result[3]); + + return result[3]; + } + + debug(' -- failed orTail'); + + return null; + } + + // condition = and_condition ('or' and_condition)* + function condition() { + var i, + result = sequence([and, nOrMore(0, orTail)]); + + if (result) { + for (i = 0; i < result[1].length; i++) { + if (result[1][i]) { + return true; + } + } + + return result[0]; + } + + return false; + } + + result = condition(); + + /** + * For success, the pos must have gotten to the end of the rule + * and returned a non-null. + * n.b. This is part of language infrastructure, + * so we do not throw an internationalizable message. + */ + if (result === null) { + throw new Error('Parse error at position ' + pos.toString() + ' for rule: ' + rule); + } + + if (pos !== rule.length) { + debug('Warning: Rule not parsed completely. Parser stopped at ' + rule.substr(0, pos) + ' for rule: ' + rule); + } + + return result; +} + +return pluralRuleParser; + +})); diff --git a/main/webapp/modules/core/externals/jquery.i18n.emitter.bidi.js b/main/webapp/modules/core/externals/jquery.i18n.emitter.bidi.js new file mode 100644 index 000000000..531f13656 --- /dev/null +++ b/main/webapp/modules/core/externals/jquery.i18n.emitter.bidi.js @@ -0,0 +1,95 @@ +/*! + * BIDI embedding support for jQuery.i18n + * + * Copyright (C) 2015, David Chan + * + * This code is dual licensed GPLv2 or later and MIT. You don't have to do + * anything special to choose one license or the other and you don't have to + * notify anyone which license you are using. You are free to use this code + * in commercial projects as long as the copyright header is left intact. + * See files GPL-LICENSE and MIT-LICENSE for details. + * + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ + +( function ( $ ) { + 'use strict'; + var strongDirRegExp; + + /** + * Matches the first strong directionality codepoint: + * - in group 1 if it is LTR + * - in group 2 if it is RTL + * Does not match if there is no strong directionality codepoint. + * + * Generated by UnicodeJS (see tools/strongDir) from the UCD; see + * https://phabricator.wikimedia.org/diffusion/GUJS/ . + */ + // eslint-disable-next-line no-misleading-character-class + strongDirRegExp = new RegExp( + '(?:' + + '(' + + '[\u0041-\u005a\u0061-\u007a\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02b8\u02bb-\u02c1\u02d0\u02d1\u02e0-\u02e4\u02ee\u0370-\u0373\u0376\u0377\u037a-\u037d\u037f\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0482\u048a-\u052f\u0531-\u0556\u0559-\u055f\u0561-\u0587\u0589\u0903-\u0939\u093b\u093d-\u0940\u0949-\u094c\u094e-\u0950\u0958-\u0961\u0964-\u0980\u0982\u0983\u0985-\u098c\u098f\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd-\u09c0\u09c7\u09c8\u09cb\u09cc\u09ce\u09d7\u09dc\u09dd\u09df-\u09e1\u09e6-\u09f1\u09f4-\u09fa\u0a03\u0a05-\u0a0a\u0a0f\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32\u0a33\u0a35\u0a36\u0a38\u0a39\u0a3e-\u0a40\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a83\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2\u0ab3\u0ab5-\u0ab9\u0abd-\u0ac0\u0ac9\u0acb\u0acc\u0ad0\u0ae0\u0ae1\u0ae6-\u0af0\u0af9\u0b02\u0b03\u0b05-\u0b0c\u0b0f\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32\u0b33\u0b35-\u0b39\u0b3d\u0b3e\u0b40\u0b47\u0b48\u0b4b\u0b4c\u0b57\u0b5c\u0b5d\u0b5f-\u0b61\u0b66-\u0b77\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99\u0b9a\u0b9c\u0b9e\u0b9f\u0ba3\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bbe\u0bbf\u0bc1\u0bc2\u0bc6-\u0bc8\u0bca-\u0bcc\u0bd0\u0bd7\u0be6-\u0bf2\u0c01-\u0c03\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c39\u0c3d\u0c41-\u0c44\u0c58-\u0c5a\u0c60\u0c61\u0c66-\u0c6f\u0c7f\u0c82\u0c83\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd-\u0cc4\u0cc6-\u0cc8\u0cca\u0ccb\u0cd5\u0cd6\u0cde\u0ce0\u0ce1\u0ce6-\u0cef\u0cf1\u0cf2\u0d02\u0d03\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d3a\u0d3d-\u0d40\u0d46-\u0d48\u0d4a-\u0d4c\u0d4e\u0d57\u0d5f-\u0d61\u0d66-\u0d75\u0d79-\u0d7f\u0d82\u0d83\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0dcf-\u0dd1\u0dd8-\u0ddf\u0de6-\u0def\u0df2-\u0df4\u0e01-\u0e30\u0e32\u0e33\u0e40-\u0e46\u0e4f-\u0e5b\u0e81\u0e82\u0e84\u0e87\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa\u0eab\u0ead-\u0eb0\u0eb2\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edf\u0f00-\u0f17\u0f1a-\u0f34\u0f36\u0f38\u0f3e-\u0f47\u0f49-\u0f6c\u0f7f\u0f85\u0f88-\u0f8c\u0fbe-\u0fc5\u0fc7-\u0fcc\u0fce-\u0fda\u1000-\u102c\u1031\u1038\u103b\u103c\u103f-\u1057\u105a-\u105d\u1061-\u1070\u1075-\u1081\u1083\u1084\u1087-\u108c\u108e-\u109c\u109e-\u10c5\u10c7\u10cd\u10d0-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1360-\u137c\u1380-\u138f\u13a0-\u13f5\u13f8-\u13fd\u1401-\u167f\u1681-\u169a\u16a0-\u16f8\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1735\u1736\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17b6\u17be-\u17c5\u17c7\u17c8\u17d4-\u17da\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u18b0-\u18f5\u1900-\u191e\u1923-\u1926\u1929-\u192b\u1930\u1931\u1933-\u1938\u1946-\u196d\u1970-\u1974\u1980-\u19ab\u19b0-\u19c9\u19d0-\u19da\u1a00-\u1a16\u1a19\u1a1a\u1a1e-\u1a55\u1a57\u1a61\u1a63\u1a64\u1a6d-\u1a72\u1a80-\u1a89\u1a90-\u1a99\u1aa0-\u1aad\u1b04-\u1b33\u1b35\u1b3b\u1b3d-\u1b41\u1b43-\u1b4b\u1b50-\u1b6a\u1b74-\u1b7c\u1b82-\u1ba1\u1ba6\u1ba7\u1baa\u1bae-\u1be5\u1be7\u1bea-\u1bec\u1bee\u1bf2\u1bf3\u1bfc-\u1c2b\u1c34\u1c35\u1c3b-\u1c49\u1c4d-\u1c7f\u1cc0-\u1cc7\u1cd3\u1ce1\u1ce9-\u1cec\u1cee-\u1cf3\u1cf5\u1cf6\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u200e\u2071\u207f\u2090-\u209c\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u214f\u2160-\u2188\u2336-\u237a\u2395\u249c-\u24e9\u26ac\u2800-\u28ff\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2ce4\u2ceb-\u2cee\u2cf2\u2cf3\u2d00-\u2d25\u2d27\u2d2d\u2d30-\u2d67\u2d6f\u2d70\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u3005-\u3007\u3021-\u3029\u302e\u302f\u3031-\u3035\u3038-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u3190-\u31ba\u31f0-\u321c\u3220-\u324f\u3260-\u327b\u327f-\u32b0\u32c0-\u32cb\u32d0-\u32fe\u3300-\u3376\u337b-\u33dd\u33e0-\u33fe\u3400-\u4db5\u4e00-\u9fd5\ua000-\ua48c\ua4d0-\ua60c\ua610-\ua62b\ua640-\ua66e\ua680-\ua69d\ua6a0-\ua6ef\ua6f2-\ua6f7\ua722-\ua787\ua789-\ua7ad\ua7b0-\ua7b7\ua7f7-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua824\ua827\ua830-\ua837\ua840-\ua873\ua880-\ua8c3\ua8ce-\ua8d9\ua8f2-\ua8fd\ua900-\ua925\ua92e-\ua946\ua952\ua953\ua95f-\ua97c\ua983-\ua9b2\ua9b4\ua9b5\ua9ba\ua9bb\ua9bd-\ua9cd\ua9cf-\ua9d9\ua9de-\ua9e4\ua9e6-\ua9fe\uaa00-\uaa28\uaa2f\uaa30\uaa33\uaa34\uaa40-\uaa42\uaa44-\uaa4b\uaa4d\uaa50-\uaa59\uaa5c-\uaa7b\uaa7d-\uaaaf\uaab1\uaab5\uaab6\uaab9-\uaabd\uaac0\uaac2\uaadb-\uaaeb\uaaee-\uaaf5\uab01-\uab06\uab09-\uab0e\uab11-\uab16\uab20-\uab26\uab28-\uab2e\uab30-\uab65\uab70-\uabe4\uabe6\uabe7\uabe9-\uabec\uabf0-\uabf9\uac00-\ud7a3\ud7b0-\ud7c6\ud7cb-\ud7fb\ue000-\ufa6d\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\uff21-\uff3a\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]|\ud800[\udc00-\udc0b]|\ud800[\udc0d-\udc26]|\ud800[\udc28-\udc3a]|\ud800\udc3c|\ud800\udc3d|\ud800[\udc3f-\udc4d]|\ud800[\udc50-\udc5d]|\ud800[\udc80-\udcfa]|\ud800\udd00|\ud800\udd02|\ud800[\udd07-\udd33]|\ud800[\udd37-\udd3f]|\ud800[\uddd0-\uddfc]|\ud800[\ude80-\ude9c]|\ud800[\udea0-\uded0]|\ud800[\udf00-\udf23]|\ud800[\udf30-\udf4a]|\ud800[\udf50-\udf75]|\ud800[\udf80-\udf9d]|\ud800[\udf9f-\udfc3]|\ud800[\udfc8-\udfd5]|\ud801[\udc00-\udc9d]|\ud801[\udca0-\udca9]|\ud801[\udd00-\udd27]|\ud801[\udd30-\udd63]|\ud801\udd6f|\ud801[\ude00-\udf36]|\ud801[\udf40-\udf55]|\ud801[\udf60-\udf67]|\ud804\udc00|\ud804[\udc02-\udc37]|\ud804[\udc47-\udc4d]|\ud804[\udc66-\udc6f]|\ud804[\udc82-\udcb2]|\ud804\udcb7|\ud804\udcb8|\ud804[\udcbb-\udcc1]|\ud804[\udcd0-\udce8]|\ud804[\udcf0-\udcf9]|\ud804[\udd03-\udd26]|\ud804\udd2c|\ud804[\udd36-\udd43]|\ud804[\udd50-\udd72]|\ud804[\udd74-\udd76]|\ud804[\udd82-\uddb5]|\ud804[\uddbf-\uddc9]|\ud804\uddcd|\ud804[\uddd0-\udddf]|\ud804[\udde1-\uddf4]|\ud804[\ude00-\ude11]|\ud804[\ude13-\ude2e]|\ud804\ude32|\ud804\ude33|\ud804\ude35|\ud804[\ude38-\ude3d]|\ud804[\ude80-\ude86]|\ud804\ude88|\ud804[\ude8a-\ude8d]|\ud804[\ude8f-\ude9d]|\ud804[\ude9f-\udea9]|\ud804[\udeb0-\udede]|\ud804[\udee0-\udee2]|\ud804[\udef0-\udef9]|\ud804\udf02|\ud804\udf03|\ud804[\udf05-\udf0c]|\ud804\udf0f|\ud804\udf10|\ud804[\udf13-\udf28]|\ud804[\udf2a-\udf30]|\ud804\udf32|\ud804\udf33|\ud804[\udf35-\udf39]|\ud804[\udf3d-\udf3f]|\ud804[\udf41-\udf44]|\ud804\udf47|\ud804\udf48|\ud804[\udf4b-\udf4d]|\ud804\udf50|\ud804\udf57|\ud804[\udf5d-\udf63]|\ud805[\udc80-\udcb2]|\ud805\udcb9|\ud805[\udcbb-\udcbe]|\ud805\udcc1|\ud805[\udcc4-\udcc7]|\ud805[\udcd0-\udcd9]|\ud805[\udd80-\uddb1]|\ud805[\uddb8-\uddbb]|\ud805\uddbe|\ud805[\uddc1-\udddb]|\ud805[\ude00-\ude32]|\ud805\ude3b|\ud805\ude3c|\ud805\ude3e|\ud805[\ude41-\ude44]|\ud805[\ude50-\ude59]|\ud805[\ude80-\udeaa]|\ud805\udeac|\ud805\udeae|\ud805\udeaf|\ud805\udeb6|\ud805[\udec0-\udec9]|\ud805[\udf00-\udf19]|\ud805\udf20|\ud805\udf21|\ud805\udf26|\ud805[\udf30-\udf3f]|\ud806[\udca0-\udcf2]|\ud806\udcff|\ud806[\udec0-\udef8]|\ud808[\udc00-\udf99]|\ud809[\udc00-\udc6e]|\ud809[\udc70-\udc74]|\ud809[\udc80-\udd43]|\ud80c[\udc00-\udfff]|\ud80d[\udc00-\udc2e]|\ud811[\udc00-\ude46]|\ud81a[\udc00-\ude38]|\ud81a[\ude40-\ude5e]|\ud81a[\ude60-\ude69]|\ud81a\ude6e|\ud81a\ude6f|\ud81a[\uded0-\udeed]|\ud81a\udef5|\ud81a[\udf00-\udf2f]|\ud81a[\udf37-\udf45]|\ud81a[\udf50-\udf59]|\ud81a[\udf5b-\udf61]|\ud81a[\udf63-\udf77]|\ud81a[\udf7d-\udf8f]|\ud81b[\udf00-\udf44]|\ud81b[\udf50-\udf7e]|\ud81b[\udf93-\udf9f]|\ud82c\udc00|\ud82c\udc01|\ud82f[\udc00-\udc6a]|\ud82f[\udc70-\udc7c]|\ud82f[\udc80-\udc88]|\ud82f[\udc90-\udc99]|\ud82f\udc9c|\ud82f\udc9f|\ud834[\udc00-\udcf5]|\ud834[\udd00-\udd26]|\ud834[\udd29-\udd66]|\ud834[\udd6a-\udd72]|\ud834\udd83|\ud834\udd84|\ud834[\udd8c-\udda9]|\ud834[\uddae-\udde8]|\ud834[\udf60-\udf71]|\ud835[\udc00-\udc54]|\ud835[\udc56-\udc9c]|\ud835\udc9e|\ud835\udc9f|\ud835\udca2|\ud835\udca5|\ud835\udca6|\ud835[\udca9-\udcac]|\ud835[\udcae-\udcb9]|\ud835\udcbb|\ud835[\udcbd-\udcc3]|\ud835[\udcc5-\udd05]|\ud835[\udd07-\udd0a]|\ud835[\udd0d-\udd14]|\ud835[\udd16-\udd1c]|\ud835[\udd1e-\udd39]|\ud835[\udd3b-\udd3e]|\ud835[\udd40-\udd44]|\ud835\udd46|\ud835[\udd4a-\udd50]|\ud835[\udd52-\udea5]|\ud835[\udea8-\udeda]|\ud835[\udedc-\udf14]|\ud835[\udf16-\udf4e]|\ud835[\udf50-\udf88]|\ud835[\udf8a-\udfc2]|\ud835[\udfc4-\udfcb]|\ud836[\udc00-\uddff]|\ud836[\ude37-\ude3a]|\ud836[\ude6d-\ude74]|\ud836[\ude76-\ude83]|\ud836[\ude85-\ude8b]|\ud83c[\udd10-\udd2e]|\ud83c[\udd30-\udd69]|\ud83c[\udd70-\udd9a]|\ud83c[\udde6-\ude02]|\ud83c[\ude10-\ude3a]|\ud83c[\ude40-\ude48]|\ud83c\ude50|\ud83c\ude51|[\ud840-\ud868][\udc00-\udfff]|\ud869[\udc00-\uded6]|\ud869[\udf00-\udfff]|[\ud86a-\ud86c][\udc00-\udfff]|\ud86d[\udc00-\udf34]|\ud86d[\udf40-\udfff]|\ud86e[\udc00-\udc1d]|\ud86e[\udc20-\udfff]|[\ud86f-\ud872][\udc00-\udfff]|\ud873[\udc00-\udea1]|\ud87e[\udc00-\ude1d]|[\udb80-\udbbe][\udc00-\udfff]|\udbbf[\udc00-\udffd]|[\udbc0-\udbfe][\udc00-\udfff]|\udbff[\udc00-\udffd]' + + ')|(' + + '[\u0590\u05be\u05c0\u05c3\u05c6\u05c8-\u05ff\u07c0-\u07ea\u07f4\u07f5\u07fa-\u0815\u081a\u0824\u0828\u082e-\u0858\u085c-\u089f\u200f\ufb1d\ufb1f-\ufb28\ufb2a-\ufb4f\u0608\u060b\u060d\u061b-\u064a\u066d-\u066f\u0671-\u06d5\u06e5\u06e6\u06ee\u06ef\u06fa-\u0710\u0712-\u072f\u074b-\u07a5\u07b1-\u07bf\u08a0-\u08e2\ufb50-\ufd3d\ufd40-\ufdcf\ufdf0-\ufdfc\ufdfe\ufdff\ufe70-\ufefe]|\ud802[\udc00-\udd1e]|\ud802[\udd20-\ude00]|\ud802\ude04|\ud802[\ude07-\ude0b]|\ud802[\ude10-\ude37]|\ud802[\ude3b-\ude3e]|\ud802[\ude40-\udee4]|\ud802[\udee7-\udf38]|\ud802[\udf40-\udfff]|\ud803[\udc00-\ude5f]|\ud803[\ude7f-\udfff]|\ud83a[\udc00-\udccf]|\ud83a[\udcd7-\udfff]|\ud83b[\udc00-\uddff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\udf00-\udfff]|\ud83b[\ude00-\udeef]|\ud83b[\udef2-\udeff]' + + ')' + + ')' + ); + + /** + * Gets directionality of the first strongly directional codepoint + * + * This is the rule the BIDI algorithm uses to determine the directionality of + * paragraphs ( http://unicode.org/reports/tr9/#The_Paragraph_Level ) and + * FSI isolates ( http://unicode.org/reports/tr9/#Explicit_Directional_Isolates ). + * + * TODO: Does not handle BIDI control characters inside the text. + * TODO: Does not handle unallocated characters. + * + * @param {string} text The text from which to extract initial directionality. + * @return {string} Directionality (either 'ltr' or 'rtl') + */ + function strongDirFromContent( text ) { + var m = text.match( strongDirRegExp ); + if ( !m ) { + return null; + } + if ( m[ 2 ] === undefined ) { + return 'ltr'; + } + return 'rtl'; + } + + $.extend( $.i18n.parser.emitter, { + /** + * Wraps argument with unicode control characters for directionality safety + * + * This solves the problem where directionality-neutral characters at the edge of + * the argument string get interpreted with the wrong directionality from the + * enclosing context, giving renderings that look corrupted like "(Ben_(WMF". + * + * The wrapping is LRE...PDF or RLE...PDF, depending on the detected + * directionality of the argument string, using the BIDI algorithm's own "First + * strong directional codepoint" rule. Essentially, this works round the fact that + * there is no embedding equivalent of U+2068 FSI (isolation with heuristic + * direction inference). The latter is cleaner but still not widely supported. + * + * @param {string[]} nodes The text nodes from which to take the first item. + * @return {string} Wrapped String of content as needed. + */ + bidi: function ( nodes ) { + var dir = strongDirFromContent( nodes[ 0 ] ); + if ( dir === 'ltr' ) { + // Wrap in LEFT-TO-RIGHT EMBEDDING ... POP DIRECTIONAL FORMATTING + return '\u202A' + nodes[ 0 ] + '\u202C'; + } + if ( dir === 'rtl' ) { + // Wrap in RIGHT-TO-LEFT EMBEDDING ... POP DIRECTIONAL FORMATTING + return '\u202B' + nodes[ 0 ] + '\u202C'; + } + // No strong directionality: do not wrap + return nodes[ 0 ]; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/jquery.i18n.emitter.js b/main/webapp/modules/core/externals/jquery.i18n.emitter.js index ef96d4969..330e50c5e 100644 --- a/main/webapp/modules/core/externals/jquery.i18n.emitter.js +++ b/main/webapp/modules/core/externals/jquery.i18n.emitter.js @@ -1 +1,168 @@ -!function(e){"use strict";var n=function(){this.language=e.i18n.languages[String.locale]||e.i18n.languages.default};n.prototype={constructor:n,emit:function(n,r){var t,a,u,i=this;switch(typeof n){case"string":case"number":t=n;break;case"object":if(a=e.map(n.slice(1),function(e){return i.emit(e,r)}),u=n[0].toLowerCase(),"function"!=typeof i[u])throw new Error('unknown operation "'+u+'"');t=i[u](a,r);break;case"undefined":t="";break;default:throw new Error("unexpected type in AST: "+typeof n)}return t},concat:function(n){var r="";return e.each(n,function(e,n){r+=n}),r},replace:function(e,n){var r=parseInt(e[0],10);return r + * parameter 98 -> not found -> return "$99" ) TODO throw error if + * nodes.length > 1 ? + * + * @param {Array} nodes One element, integer, n >= 0 + * @param {Array} replacements for $1, $2, ... $n + * @return {string} replacement + */ + replace: function ( nodes, replacements ) { + var index = parseInt( nodes[ 0 ], 10 ); + + if ( index < replacements.length ) { + // replacement is not a string, don't touch! + return replacements[ index ]; + } else { + // index not found, fallback to displaying variable + return '$' + ( index + 1 ); + } + }, + + /** + * Transform parsed structure into pluralization n.b. The first node may + * be a non-integer (for instance, a string representing an Arabic + * number). So convert it back with the current language's + * convertNumber. + * + * @param {Array} nodes List [ {String|Number}, {String}, {String} ... ] + * @return {string} selected pluralized form according to current + * language. + */ + plural: function ( nodes ) { + var count = parseFloat( this.language.convertNumber( nodes[ 0 ], 10 ) ), + forms = nodes.slice( 1 ); + + return forms.length ? this.language.convertPlural( count, forms ) : ''; + }, + + /** + * Transform parsed structure into gender Usage + * {{gender:gender|masculine|feminine|neutral}}. + * + * @param {Array} nodes List [ {String}, {String}, {String} , {String} ] + * @return {string} selected gender form according to current language + */ + gender: function ( nodes ) { + var gender = nodes[ 0 ], + forms = nodes.slice( 1 ); + + return this.language.gender( gender, forms ); + }, + + /** + * Transform parsed structure into grammar conversion. Invoked by + * putting {{grammar:form|word}} in a message + * + * @param {Array} nodes List [{Grammar case eg: genitive}, {String word}] + * @return {string} selected grammatical form according to current + * language. + */ + grammar: function ( nodes ) { + var form = nodes[ 0 ], + word = nodes[ 1 ]; + + return word && form && this.language.convertGrammar( word, form ); + } + }; + + $.extend( $.i18n.parser.emitter, new MessageParserEmitter() ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/jquery.i18n.fallbacks.js b/main/webapp/modules/core/externals/jquery.i18n.fallbacks.js new file mode 100644 index 000000000..74b16f22d --- /dev/null +++ b/main/webapp/modules/core/externals/jquery.i18n.fallbacks.js @@ -0,0 +1,186 @@ +/*! + * jQuery Internationalization library + * + * Copyright (C) 2012 Santhosh Thottingal + * + * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do anything special to + * choose one license or the other and you don't have to notify anyone which license you are using. + * You are free to use UniversalLanguageSelector in commercial projects as long as the copyright + * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. + * + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ +( function ( $ ) { + 'use strict'; + + $.i18n = $.i18n || {}; + $.extend( $.i18n.fallbacks, { + ab: [ 'ru' ], + ace: [ 'id' ], + aln: [ 'sq' ], + // Not so standard - als is supposed to be Tosk Albanian, + // but in Wikipedia it's used for a Germanic language. + als: [ 'gsw', 'de' ], + an: [ 'es' ], + anp: [ 'hi' ], + arn: [ 'es' ], + arz: [ 'ar' ], + av: [ 'ru' ], + ay: [ 'es' ], + ba: [ 'ru' ], + bar: [ 'de' ], + 'bat-smg': [ 'sgs', 'lt' ], + bcc: [ 'fa' ], + 'be-x-old': [ 'be-tarask' ], + bh: [ 'bho' ], + bjn: [ 'id' ], + bm: [ 'fr' ], + bpy: [ 'bn' ], + bqi: [ 'fa' ], + bug: [ 'id' ], + 'cbk-zam': [ 'es' ], + ce: [ 'ru' ], + crh: [ 'crh-latn' ], + 'crh-cyrl': [ 'ru' ], + csb: [ 'pl' ], + cv: [ 'ru' ], + 'de-at': [ 'de' ], + 'de-ch': [ 'de' ], + 'de-formal': [ 'de' ], + dsb: [ 'de' ], + dtp: [ 'ms' ], + egl: [ 'it' ], + eml: [ 'it' ], + ff: [ 'fr' ], + fit: [ 'fi' ], + 'fiu-vro': [ 'vro', 'et' ], + frc: [ 'fr' ], + frp: [ 'fr' ], + frr: [ 'de' ], + fur: [ 'it' ], + gag: [ 'tr' ], + gan: [ 'gan-hant', 'zh-hant', 'zh-hans' ], + 'gan-hans': [ 'zh-hans' ], + 'gan-hant': [ 'zh-hant', 'zh-hans' ], + gl: [ 'pt' ], + glk: [ 'fa' ], + gn: [ 'es' ], + gsw: [ 'de' ], + hif: [ 'hif-latn' ], + hsb: [ 'de' ], + ht: [ 'fr' ], + ii: [ 'zh-cn', 'zh-hans' ], + inh: [ 'ru' ], + iu: [ 'ike-cans' ], + jut: [ 'da' ], + jv: [ 'id' ], + kaa: [ 'kk-latn', 'kk-cyrl' ], + kbd: [ 'kbd-cyrl' ], + khw: [ 'ur' ], + kiu: [ 'tr' ], + kk: [ 'kk-cyrl' ], + 'kk-arab': [ 'kk-cyrl' ], + 'kk-latn': [ 'kk-cyrl' ], + 'kk-cn': [ 'kk-arab', 'kk-cyrl' ], + 'kk-kz': [ 'kk-cyrl' ], + 'kk-tr': [ 'kk-latn', 'kk-cyrl' ], + kl: [ 'da' ], + 'ko-kp': [ 'ko' ], + koi: [ 'ru' ], + krc: [ 'ru' ], + ks: [ 'ks-arab' ], + ksh: [ 'de' ], + ku: [ 'ku-latn' ], + 'ku-arab': [ 'ckb' ], + kv: [ 'ru' ], + lad: [ 'es' ], + lb: [ 'de' ], + lbe: [ 'ru' ], + lez: [ 'ru' ], + li: [ 'nl' ], + lij: [ 'it' ], + liv: [ 'et' ], + lmo: [ 'it' ], + ln: [ 'fr' ], + ltg: [ 'lv' ], + lzz: [ 'tr' ], + mai: [ 'hi' ], + 'map-bms': [ 'jv', 'id' ], + mg: [ 'fr' ], + mhr: [ 'ru' ], + min: [ 'id' ], + mo: [ 'ro' ], + mrj: [ 'ru' ], + mwl: [ 'pt' ], + myv: [ 'ru' ], + mzn: [ 'fa' ], + nah: [ 'es' ], + nap: [ 'it' ], + nds: [ 'de' ], + 'nds-nl': [ 'nl' ], + 'nl-informal': [ 'nl' ], + no: [ 'nb' ], + os: [ 'ru' ], + pcd: [ 'fr' ], + pdc: [ 'de' ], + pdt: [ 'de' ], + pfl: [ 'de' ], + pms: [ 'it' ], + pt: [ 'pt-br' ], + 'pt-br': [ 'pt' ], + qu: [ 'es' ], + qug: [ 'qu', 'es' ], + rgn: [ 'it' ], + rmy: [ 'ro' ], + 'roa-rup': [ 'rup' ], + rue: [ 'uk', 'ru' ], + ruq: [ 'ruq-latn', 'ro' ], + 'ruq-cyrl': [ 'mk' ], + 'ruq-latn': [ 'ro' ], + sa: [ 'hi' ], + sah: [ 'ru' ], + scn: [ 'it' ], + sg: [ 'fr' ], + sgs: [ 'lt' ], + sli: [ 'de' ], + sr: [ 'sr-ec' ], + srn: [ 'nl' ], + stq: [ 'de' ], + su: [ 'id' ], + szl: [ 'pl' ], + tcy: [ 'kn' ], + tg: [ 'tg-cyrl' ], + tt: [ 'tt-cyrl', 'ru' ], + 'tt-cyrl': [ 'ru' ], + ty: [ 'fr' ], + udm: [ 'ru' ], + ug: [ 'ug-arab' ], + uk: [ 'ru' ], + vec: [ 'it' ], + vep: [ 'et' ], + vls: [ 'nl' ], + vmf: [ 'de' ], + vot: [ 'fi' ], + vro: [ 'et' ], + wa: [ 'fr' ], + wo: [ 'fr' ], + wuu: [ 'zh-hans' ], + xal: [ 'ru' ], + xmf: [ 'ka' ], + yi: [ 'he' ], + za: [ 'zh-hans' ], + zea: [ 'nl' ], + zh: [ 'zh-hans' ], + 'zh-classical': [ 'lzh' ], + 'zh-cn': [ 'zh-hans' ], + 'zh-hant': [ 'zh-hans' ], + 'zh-hk': [ 'zh-hant', 'zh-hans' ], + 'zh-min-nan': [ 'nan' ], + 'zh-mo': [ 'zh-hk', 'zh-hant', 'zh-hans' ], + 'zh-my': [ 'zh-sg', 'zh-hans' ], + 'zh-sg': [ 'zh-hans' ], + 'zh-tw': [ 'zh-hant', 'zh-hans' ], + 'zh-yue': [ 'yue' ] + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/jquery.i18n.js b/main/webapp/modules/core/externals/jquery.i18n.js index b6b5fda38..d6c0bae61 100644 --- a/main/webapp/modules/core/externals/jquery.i18n.js +++ b/main/webapp/modules/core/externals/jquery.i18n.js @@ -1,4 +1,290 @@ -/* jQuery Internationalization library 1.0.4 +/*! + * jQuery Internationalization library + * * Copyright (C) 2012 Santhosh Thottingal - * Dual licensed GPLv2 or later and MIT. */ -!function(e){"use strict";var n,a,o=Array.prototype.slice;(a=function(n){this.options=e.extend({},a.defaults,n),this.parser=this.options.parser,this.locale=this.options.locale,this.messageStore=this.options.messageStore,this.languages={},this.init()}).prototype={init:function(){var n=this;String.locale=n.locale,String.prototype.toLocaleString=function(){var a,o,t,i,l,r,s;for(t=this.valueOf(),i=n.locale,l=0;i;){o=(a=i.split("-")).length;do{if(r=a.slice(0,o).join("-"),s=n.messageStore.get(r,t))return s;o--}while(o);if("en"===i)break;i=e.i18n.fallbacks[n.locale]&&e.i18n.fallbacks[n.locale][l]||n.options.fallbackLocale,e.i18n.log("Trying fallback locale for "+n.locale+": "+i),l++}return""}},destroy:function(){e.removeData(document,"i18n")},load:function(n,a){var o,t,i,l={};if(n||a||(n="i18n/"+e.i18n().locale+".json",a=e.i18n().locale),"string"==typeof n&&"json"!==n.split(".").pop()){l[a]=n+"/"+a+".json",o=(e.i18n.fallbacks[a]||[]).concat(this.options.fallbackLocale);for(t in o)l[i=o[t]]=n+"/"+i+".json";return this.load(l)}return this.messageStore.load(n,a)},parse:function(n,a){var o=n.toLocaleString();return this.parser.language=e.i18n.languages[e.i18n().locale]||e.i18n.languages.default,""===o&&(o=n),this.parser.parse(o,a)}},e.i18n=function(n,t){var i,l=e.data(document,"i18n"),r="object"==typeof n&&n;return r&&r.locale&&l&&l.locale!==r.locale&&(String.locale=l.locale=r.locale),l||(l=new a(r),e.data(document,"i18n",l)),"string"==typeof n?(i=void 0!==t?o.call(arguments,1):[],l.parse(n,i)):l},e.fn.i18n=function(){var n=e.data(document,"i18n");return n||(n=new a,e.data(document,"i18n",n)),String.locale=n.locale,this.each(function(){var a=e(this),o=a.data("i18n");o?a.text(n.parse(o)):a.find("[data-i18n]").i18n()})},String.locale=String.locale||e("html").attr("lang"),String.locale||(void 0!==typeof window.navigator?(n=window.navigator,String.locale=n.language||n.userLanguage||""):String.locale=""),e.i18n.languages={},e.i18n.messageStore=e.i18n.messageStore||{},e.i18n.parser={parse:function(e,n){return e.replace(/\$(\d+)/g,function(e,a){var o=parseInt(a,10)-1;return void 0!==n[o]?n[o]:"$"+a})},emitter:{}},e.i18n.fallbacks={},e.i18n.debug=!1,e.i18n.log=function(){window.console&&e.i18n.debug&&window.console.log.apply(window.console,arguments)},a.defaults={locale:String.locale,fallbackLocale:"en",parser:e.i18n.parser,messageStore:e.i18n.messageStore},e.i18n.constructor=a}(jQuery); + * + * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do + * anything special to choose one license or the other and you don't have to + * notify anyone which license you are using. You are free to use + * UniversalLanguageSelector in commercial projects as long as the copyright + * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. + * + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ + +( function ( $ ) { + 'use strict'; + + var I18N, + slice = Array.prototype.slice; + /** + * @constructor + * @param {Object} options + */ + I18N = function ( options ) { + // Load defaults + this.options = $.extend( {}, I18N.defaults, options ); + + this.parser = this.options.parser; + this.locale = this.options.locale; + this.messageStore = this.options.messageStore; + this.languages = {}; + }; + + I18N.prototype = { + /** + * Localize a given messageKey to a locale. + * @param {string} messageKey + * @return {string} Localized message + */ + localize: function ( messageKey ) { + var localeParts, localePartIndex, locale, fallbackIndex, + tryingLocale, message; + + locale = this.locale; + fallbackIndex = 0; + + while ( locale ) { + // Iterate through locales starting at most-specific until + // localization is found. As in fi-Latn-FI, fi-Latn and fi. + localeParts = locale.split( '-' ); + localePartIndex = localeParts.length; + + do { + tryingLocale = localeParts.slice( 0, localePartIndex ).join( '-' ); + message = this.messageStore.get( tryingLocale, messageKey ); + + if ( message ) { + return message; + } + + localePartIndex--; + } while ( localePartIndex ); + + if ( locale === this.options.fallbackLocale ) { + break; + } + + locale = ( $.i18n.fallbacks[ this.locale ] && + $.i18n.fallbacks[ this.locale ][ fallbackIndex ] ) || + this.options.fallbackLocale; + $.i18n.log( 'Trying fallback locale for ' + this.locale + ': ' + locale + ' (' + messageKey + ')' ); + + fallbackIndex++; + } + + // key not found + return ''; + }, + + /* + * Destroy the i18n instance. + */ + destroy: function () { + $.removeData( document, 'i18n' ); + }, + + /** + * General message loading API This can take a URL string for + * the json formatted messages. Example: + * load('path/to/all_localizations.json'); + * + * To load a localization file for a locale: + * + * load('path/to/de-messages.json', 'de' ); + * + * + * To load a localization file from a directory: + * + * load('path/to/i18n/directory', 'de' ); + * + * The above method has the advantage of fallback resolution. + * ie, it will automatically load the fallback locales for de. + * For most usecases, this is the recommended method. + * It is optional to have trailing slash at end. + * + * A data object containing message key- message translation mappings + * can also be passed. Example: + * + * load( { 'hello' : 'Hello' }, optionalLocale ); + * + * + * A source map containing key-value pair of languagename and locations + * can also be passed. Example: + * + * load( { + * bn: 'i18n/bn.json', + * he: 'i18n/he.json', + * en: 'i18n/en.json' + * } ) + * + * + * If the data argument is null/undefined/false, + * all cached messages for the i18n instance will get reset. + * + * @param {string|Object} source + * @param {string} locale Language tag + * @return {jQuery.Promise} + */ + load: function ( source, locale ) { + var fallbackLocales, locIndex, fallbackLocale, sourceMap = {}; + if ( !source && !locale ) { + source = 'i18n/' + $.i18n().locale + '.json'; + locale = $.i18n().locale; + } + if ( typeof source === 'string' && + // source extension should be json, but can have query params after that. + source.split( '?' )[ 0 ].split( '.' ).pop() !== 'json' + ) { + // Load specified locale then check for fallbacks when directory is + // specified in load() + sourceMap[ locale ] = source + '/' + locale + '.json'; + fallbackLocales = ( $.i18n.fallbacks[ locale ] || [] ) + .concat( this.options.fallbackLocale ); + for ( locIndex = 0; locIndex < fallbackLocales.length; locIndex++ ) { + fallbackLocale = fallbackLocales[ locIndex ]; + sourceMap[ fallbackLocale ] = source + '/' + fallbackLocale + '.json'; + } + return this.load( sourceMap ); + } else { + return this.messageStore.load( source, locale ); + } + + }, + + /** + * Does parameter and magic word substitution. + * + * @param {string} key Message key + * @param {Array} parameters Message parameters + * @return {string} + */ + parse: function ( key, parameters ) { + var message = this.localize( key ); + // FIXME: This changes the state of the I18N object, + // should probably not change the 'this.parser' but just + // pass it to the parser. + this.parser.language = $.i18n.languages[ $.i18n().locale ] || $.i18n.languages[ 'default' ]; + if ( message === '' ) { + message = key; + } + return this.parser.parse( message, parameters ); + } + }; + + /** + * Process a message from the $.I18N instance + * for the current document, stored in jQuery.data(document). + * + * @param {string} key Key of the message. + * @param {string} param1 [param...] Variadic list of parameters for {key}. + * @return {string|$.I18N} Parsed message, or if no key was given + * the instance of $.I18N is returned. + */ + $.i18n = function ( key, param1 ) { + var parameters, + i18n = $.data( document, 'i18n' ), + options = typeof key === 'object' && key; + + // If the locale option for this call is different then the setup so far, + // update it automatically. This doesn't just change the context for this + // call but for all future call as well. + // If there is no i18n setup yet, don't do this. It will be taken care of + // by the `new I18N` construction below. + // NOTE: It should only change language for this one call. + // Then cache instances of I18N somewhere. + if ( options && options.locale && i18n && i18n.locale !== options.locale ) { + i18n.locale = options.locale; + } + + if ( !i18n ) { + i18n = new I18N( options ); + $.data( document, 'i18n', i18n ); + } + + if ( typeof key === 'string' ) { + if ( param1 !== undefined ) { + parameters = slice.call( arguments, 1 ); + } else { + parameters = []; + } + + return i18n.parse( key, parameters ); + } else { + // FIXME: remove this feature/bug. + return i18n; + } + }; + + $.fn.i18n = function () { + var i18n = $.data( document, 'i18n' ); + + if ( !i18n ) { + i18n = new I18N(); + $.data( document, 'i18n', i18n ); + } + + return this.each( function () { + var $this = $( this ), + messageKey = $this.data( 'i18n' ), + lBracket, rBracket, type, key; + + if ( messageKey ) { + lBracket = messageKey.indexOf( '[' ); + rBracket = messageKey.indexOf( ']' ); + if ( lBracket !== -1 && rBracket !== -1 && lBracket < rBracket ) { + type = messageKey.slice( lBracket + 1, rBracket ); + key = messageKey.slice( rBracket + 1 ); + if ( type === 'html' ) { + $this.html( i18n.parse( key ) ); + } else { + $this.attr( type, i18n.parse( key ) ); + } + } else { + $this.text( i18n.parse( messageKey ) ); + } + } else { + $this.find( '[data-i18n]' ).i18n(); + } + } ); + }; + + function getDefaultLocale() { + var locale = $( 'html' ).attr( 'lang' ); + if ( !locale ) { + locale = navigator.language || navigator.userLanguage || ''; + } + return locale; + } + + $.i18n.languages = {}; + $.i18n.messageStore = $.i18n.messageStore || {}; + $.i18n.parser = { + // The default parser only handles variable substitution + parse: function ( message, parameters ) { + return message.replace( /\$(\d+)/g, function ( str, match ) { + var index = parseInt( match, 10 ) - 1; + return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match; + } ); + }, + emitter: {} + }; + $.i18n.fallbacks = {}; + $.i18n.debug = false; + $.i18n.log = function ( /* arguments */ ) { + if ( window.console && $.i18n.debug ) { + window.console.log.apply( window.console, arguments ); + } + }; + /* Static members */ + I18N.defaults = { + locale: getDefaultLocale(), + fallbackLocale: 'en', + parser: $.i18n.parser, + messageStore: $.i18n.messageStore + }; + + // Expose constructor + $.i18n.constructor = I18N; +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/jquery.i18n.language.js b/main/webapp/modules/core/externals/jquery.i18n.language.js index eb4282a32..f905aa8f0 100644 --- a/main/webapp/modules/core/externals/jquery.i18n.language.js +++ b/main/webapp/modules/core/externals/jquery.i18n.language.js @@ -1,4 +1,499 @@ -/* jQuery Internationalization library 1.0.4 - * Copyright (C) 2012 Santhosh Thottingal - * Dual licensed GPLv2 or later and MIT. */ -!function(n){"use strict";var o={pluralRules:{ak:{one:"n = 0..1"},am:{one:"i = 0 or n = 1"},ar:{zero:"n = 0",one:"n = 1",two:"n = 2",few:"n % 100 = 3..10",many:"n % 100 = 11..99"},be:{one:"n % 10 = 1 and n % 100 != 11",few:"n % 10 = 2..4 and n % 100 != 12..14",many:"n % 10 = 0 or n % 10 = 5..9 or n % 100 = 11..14"},bh:{one:"n = 0..1"},bn:{one:"i = 0 or n = 1"},br:{one:"n % 10 = 1 and n % 100 != 11,71,91",two:"n % 10 = 2 and n % 100 != 12,72,92",few:"n % 10 = 3..4,9 and n % 100 != 10..19,70..79,90..99",many:"n != 0 and n % 1000000 = 0"},bs:{one:"v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"},cs:{one:"i = 1 and v = 0",few:"i = 2..4 and v = 0",many:"v != 0"},cy:{zero:"n = 0",one:"n = 1",two:"n = 2",few:"n = 3",many:"n = 6"},da:{one:"n = 1 or t != 0 and i = 0,1"},fa:{one:"i = 0 or n = 1"},ff:{one:"i = 0,1"},fil:{one:"i = 0..1 and v = 0"},fr:{one:"i = 0,1"},ga:{one:"n = 1",two:"n = 2",few:"n = 3..6",many:"n = 7..10"},gd:{one:"n = 1,11",two:"n = 2,12",few:"n = 3..10,13..19"},gu:{one:"i = 0 or n = 1"},guw:{one:"n = 0..1"},gv:{one:"n % 10 = 1",two:"n % 10 = 2",few:"n % 100 = 0,20,40,60"},he:{one:"i = 1 and v = 0",two:"i = 2 and v = 0",many:"v = 0 and n != 0..10 and n % 10 = 0"},hi:{one:"i = 0 or n = 1"},hr:{one:"v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"},hy:{one:"i = 0,1"},is:{one:"t = 0 and i % 10 = 1 and i % 100 != 11 or t != 0"},iu:{one:"n = 1",two:"n = 2"},iw:{one:"i = 1 and v = 0",two:"i = 2 and v = 0",many:"v = 0 and n != 0..10 and n % 10 = 0"},kab:{one:"i = 0,1"},kn:{one:"i = 0 or n = 1"},kw:{one:"n = 1",two:"n = 2"},lag:{zero:"n = 0",one:"i = 0,1 and n != 0"},ln:{one:"n = 0..1"},lt:{one:"n % 10 = 1 and n % 100 != 11..19",few:"n % 10 = 2..9 and n % 100 != 11..19",many:"f != 0"},lv:{zero:"n % 10 = 0 or n % 100 = 11..19 or v = 2 and f % 100 = 11..19",one:"n % 10 = 1 and n % 100 != 11 or v = 2 and f % 10 = 1 and f % 100 != 11 or v != 2 and f % 10 = 1"},mg:{one:"n = 0..1"},mk:{one:"v = 0 and i % 10 = 1 or f % 10 = 1"},mo:{one:"i = 1 and v = 0",few:"v != 0 or n = 0 or n != 1 and n % 100 = 1..19"},mr:{one:"i = 0 or n = 1"},mt:{one:"n = 1",few:"n = 0 or n % 100 = 2..10",many:"n % 100 = 11..19"},naq:{one:"n = 1",two:"n = 2"},nso:{one:"n = 0..1"},pa:{one:"n = 0..1"},pl:{one:"i = 1 and v = 0",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14",many:"v = 0 and i != 1 and i % 10 = 0..1 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 12..14"},pt:{one:"i = 1 and v = 0 or i = 0 and t = 1"},pt_PT:{one:"n = 1 and v = 0"},ro:{one:"i = 1 and v = 0",few:"v != 0 or n = 0 or n != 1 and n % 100 = 1..19"},ru:{one:"v = 0 and i % 10 = 1 and i % 100 != 11",many:"v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14"},se:{one:"n = 1",two:"n = 2"},sh:{one:"v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"},shi:{one:"i = 0 or n = 1",few:"n = 2..10"},si:{one:"n = 0,1 or i = 0 and f = 1"},sk:{one:"i = 1 and v = 0",few:"i = 2..4 and v = 0",many:"v != 0"},sl:{one:"v = 0 and i % 100 = 1",two:"v = 0 and i % 100 = 2",few:"v = 0 and i % 100 = 3..4 or v != 0"},sma:{one:"n = 1",two:"n = 2"},smi:{one:"n = 1",two:"n = 2"},smj:{one:"n = 1",two:"n = 2"},smn:{one:"n = 1",two:"n = 2"},sms:{one:"n = 1",two:"n = 2"},sr:{one:"v = 0 and i % 10 = 1 and i % 100 != 11 or f % 10 = 1 and f % 100 != 11",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14 or f % 10 = 2..4 and f % 100 != 12..14"},ti:{one:"n = 0..1"},tl:{one:"i = 0..1 and v = 0"},tzm:{one:"n = 0..1 or n = 11..99"},uk:{one:"v = 0 and i % 10 = 1 and i % 100 != 11",few:"v = 0 and i % 10 = 2..4 and i % 100 != 12..14",many:"v = 0 and i % 10 = 0 or v = 0 and i % 10 = 5..9 or v = 0 and i % 100 = 11..14"},wa:{one:"n = 0..1"},zu:{one:"i = 0 or n = 1"}},convertPlural:function(o,e){var a,r,i,d,t=new RegExp("\\d+=","i");if(!e||0===e.length)return"";for(i=0;iload('path/to/all_localizations.json'); + * + * This can also load a localization file for a locale + * load( 'path/to/de-messages.json', 'de' ); + * + * A data object containing message key- message translation mappings + * can also be passed Eg: + * + * load( { 'hello' : 'Hello' }, optionalLocale ); + * If the data argument is + * null/undefined/false, + * all cached messages for the i18n instance will get reset. + * + * @param {string|Object} source + * @param {string} locale Language tag + * @return {jQuery.Promise} + */ + load: function ( source, locale ) { + var key = null, + deferreds = [], + messageStore = this; + + if ( typeof source === 'string' ) { + // This is a URL to the messages file. + $.i18n.log( 'Loading messages from: ' + source ); + return jsonMessageLoader( source ) + .then( function ( localization ) { + return messageStore.load( localization, locale ); + } ); + } + + if ( locale ) { + // source is an key-value pair of messages for given locale + messageStore.set( locale, source ); + + return $.Deferred().resolve(); + } else { + // source is a key-value pair of locales and their source + for ( key in source ) { + if ( Object.prototype.hasOwnProperty.call( source, key ) ) { + locale = key; + // No {locale} given, assume data is a group of languages, + // call this function again for each language. + deferreds.push( messageStore.load( source[ key ], locale ) ); + } + } + return $.when.apply( $, deferreds ); + } + + }, + + /** + * Set messages to the given locale. + * If locale exists, add messages to the locale. + * + * @param {string} locale + * @param {Object} messages + */ + set: function ( locale, messages ) { + if ( !this.messages[ locale ] ) { + this.messages[ locale ] = messages; + } else { + this.messages[ locale ] = $.extend( this.messages[ locale ], messages ); + } + }, + + /** + * + * @param {string} locale + * @param {string} messageKey + * @return {boolean} + */ + get: function ( locale, messageKey ) { + return this.messages[ locale ] && this.messages[ locale ][ messageKey ]; + } + }; + + $.extend( $.i18n.messageStore, new MessageStore() ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/jquery.i18n.parser.js b/main/webapp/modules/core/externals/jquery.i18n.parser.js index 8c1c023fb..8c47e55f4 100644 --- a/main/webapp/modules/core/externals/jquery.i18n.parser.js +++ b/main/webapp/modules/core/externals/jquery.i18n.parser.js @@ -1,4 +1,310 @@ -/* jQuery Internationalization library 1.0.4 - * Copyright (C) 2012 Santhosh Thottingal - * Dual licensed GPLv2 or later and MIT. */ -!function(n){"use strict";var r=function(r){this.options=n.extend({},n.i18n.parser.defaults,r),this.language=n.i18n.languages[String.locale]||n.i18n.languages.default,this.emitter=n.i18n.parser.emitter};r.prototype={constructor:r,simpleParse:function(n,r){return n.replace(/\$(\d+)/g,function(n,t){var u=parseInt(t,10)-1;return void 0!==r[u]?r[u]:"$"+t})},parse:function(r,t){return r.indexOf("{{")<0?this.simpleParse(r,t):(this.emitter.language=n.i18n.languages[n.i18n().locale]||n.i18n.languages.default,this.emitter.emit(this.ast(r),t))},ast:function(n){function r(n){return function(){var r,t;for(r=0;r1?["CONCAT"].concat(n):n[0]}function c(){var n=t([w,v,a]);return null===n?null:[n[0],n[2]]}function f(){var n=t([w,v,b]);return null===n?null:[n[0],n[2]]}function s(){var n=t([E,j,O]);return null===n?null:n[1]}var g,v,h,p,d,m,$,x,C,A,P,j,w,E,O,S,b,y,F=0;if(g=l("|"),v=l(":"),h=l("\\"),p=e(/^./),d=l("$"),m=e(/^\d+/),$=e(/^[^{}\[\]$\\]/),x=e(/^[^{}\[\]$\\|]/),C=e(/^[^{}\[\]$\s]/),r([i,C]),A=r([i,x]),P=r([i,$]),w=function(n,r){return function(){var t=n();return null===t?null:r(t)}}(e(/^[ !"$&'()*,.\/0-9;=?@A-Z\^_`a-z~\x80-\xFF+\-]+/),function(n){return n.toString()}),j=r([function(){var n=t([r([c,f]),u(0,o)]);return null===n?null:n[0].concat(n[1])},function(){var n=t([w,u(0,o)]);return null===n?null:[n[0]].concat(n[1])}]),E=l("{{"),O=l("}}"),S=r([s,a,function(){var n=u(1,P)();return null===n?null:n.join("")}]),b=r([s,a,function(){var n=u(1,A)();return null===n?null:n.join("")}]),null===(y=function(){var n=u(0,S)();return null===n?null:["CONCAT"].concat(n)}())||F!==n.length)throw new Error("Parse error at position "+F.toString()+" in input: "+n);return y}},n.extend(n.i18n.parser,new r)}(jQuery); +/*! + * jQuery Internationalization library + * + * Copyright (C) 2011-2013 Santhosh Thottingal, Neil Kandalgaonkar + * + * jquery.i18n is dual licensed GPLv2 or later and MIT. You don't have to do + * anything special to choose one license or the other and you don't have to + * notify anyone which license you are using. You are free to use + * UniversalLanguageSelector in commercial projects as long as the copyright + * header is left intact. See files GPL-LICENSE and MIT-LICENSE for details. + * + * @licence GNU General Public Licence 2.0 or later + * @licence MIT License + */ + +( function ( $ ) { + 'use strict'; + + var MessageParser = function ( options ) { + this.options = $.extend( {}, $.i18n.parser.defaults, options ); + this.language = $.i18n.languages[ String.locale ] || $.i18n.languages[ 'default' ]; + this.emitter = $.i18n.parser.emitter; + }; + + MessageParser.prototype = { + + constructor: MessageParser, + + simpleParse: function ( message, parameters ) { + return message.replace( /\$(\d+)/g, function ( str, match ) { + var index = parseInt( match, 10 ) - 1; + + return parameters[ index ] !== undefined ? parameters[ index ] : '$' + match; + } ); + }, + + parse: function ( message, replacements ) { + if ( message.indexOf( '{{' ) < 0 ) { + return this.simpleParse( message, replacements ); + } + + this.emitter.language = $.i18n.languages[ $.i18n().locale ] || + $.i18n.languages[ 'default' ]; + + return this.emitter.emit( this.ast( message ), replacements ); + }, + + ast: function ( message ) { + var pipe, colon, backslash, anyCharacter, dollar, digits, regularLiteral, + regularLiteralWithoutBar, regularLiteralWithoutSpace, escapedOrLiteralWithoutBar, + escapedOrRegularLiteral, templateContents, templateName, openTemplate, + closeTemplate, expression, paramExpression, result, + pos = 0; + + // Try parsers until one works, if none work return null + function choice( parserSyntax ) { + return function () { + var i, result; + + for ( i = 0; i < parserSyntax.length; i++ ) { + result = parserSyntax[ i ](); + + if ( result !== null ) { + return result; + } + } + + return null; + }; + } + + // Try several parserSyntax-es in a row. + // All must succeed; otherwise, return null. + // This is the only eager one. + function sequence( parserSyntax ) { + var i, res, + originalPos = pos, + result = []; + + for ( i = 0; i < parserSyntax.length; i++ ) { + res = parserSyntax[ i ](); + + if ( res === null ) { + pos = originalPos; + + return null; + } + + result.push( res ); + } + + return result; + } + + // Run the same parser over and over until it fails. + // Must succeed a minimum of n times; otherwise, return null. + function nOrMore( n, p ) { + return function () { + var originalPos = pos, + result = [], + parsed = p(); + + while ( parsed !== null ) { + result.push( parsed ); + parsed = p(); + } + + if ( result.length < n ) { + pos = originalPos; + + return null; + } + + return result; + }; + } + + // Helpers -- just make parserSyntax out of simpler JS builtin types + + function makeStringParser( s ) { + var len = s.length; + + return function () { + var result = null; + + if ( message.slice( pos, pos + len ) === s ) { + result = s; + pos += len; + } + + return result; + }; + } + + function makeRegexParser( regex ) { + return function () { + var matches = message.slice( pos ).match( regex ); + + if ( matches === null ) { + return null; + } + + pos += matches[ 0 ].length; + + return matches[ 0 ]; + }; + } + + pipe = makeStringParser( '|' ); + colon = makeStringParser( ':' ); + backslash = makeStringParser( '\\' ); + anyCharacter = makeRegexParser( /^./ ); + dollar = makeStringParser( '$' ); + digits = makeRegexParser( /^\d+/ ); + regularLiteral = makeRegexParser( /^[^{}[\]$\\]/ ); + regularLiteralWithoutBar = makeRegexParser( /^[^{}[\]$\\|]/ ); + regularLiteralWithoutSpace = makeRegexParser( /^[^{}[\]$\s]/ ); + + // There is a general pattern: + // parse a thing; + // if it worked, apply transform, + // otherwise return null. + // But using this as a combinator seems to cause problems + // when combined with nOrMore(). + // May be some scoping issue. + function transform( p, fn ) { + return function () { + var result = p(); + + return result === null ? null : fn( result ); + }; + } + + // Used to define "literals" within template parameters. The pipe + // character is the parameter delimeter, so by default + // it is not a literal in the parameter + function literalWithoutBar() { + var result = nOrMore( 1, escapedOrLiteralWithoutBar )(); + + return result === null ? null : result.join( '' ); + } + + function literal() { + var result = nOrMore( 1, escapedOrRegularLiteral )(); + + return result === null ? null : result.join( '' ); + } + + function escapedLiteral() { + var result = sequence( [ backslash, anyCharacter ] ); + + return result === null ? null : result[ 1 ]; + } + + choice( [ escapedLiteral, regularLiteralWithoutSpace ] ); + escapedOrLiteralWithoutBar = choice( [ escapedLiteral, regularLiteralWithoutBar ] ); + escapedOrRegularLiteral = choice( [ escapedLiteral, regularLiteral ] ); + + function replacement() { + var result = sequence( [ dollar, digits ] ); + + if ( result === null ) { + return null; + } + + return [ 'REPLACE', parseInt( result[ 1 ], 10 ) - 1 ]; + } + + templateName = transform( + // see $wgLegalTitleChars + // not allowing : due to the need to catch "PLURAL:$1" + makeRegexParser( /^[ !"$&'()*,./0-9;=?@A-Z^_`a-z~\x80-\xFF+-]+/ ), + + function ( result ) { + return result.toString(); + } + ); + + function templateParam() { + var expr, + result = sequence( [ pipe, nOrMore( 0, paramExpression ) ] ); + + if ( result === null ) { + return null; + } + + expr = result[ 1 ]; + + // use a "CONCAT" operator if there are multiple nodes, + // otherwise return the first node, raw. + return expr.length > 1 ? [ 'CONCAT' ].concat( expr ) : expr[ 0 ]; + } + + function templateWithReplacement() { + var result = sequence( [ templateName, colon, replacement ] ); + + return result === null ? null : [ result[ 0 ], result[ 2 ] ]; + } + + function templateWithOutReplacement() { + var result = sequence( [ templateName, colon, paramExpression ] ); + + return result === null ? null : [ result[ 0 ], result[ 2 ] ]; + } + + templateContents = choice( [ + function () { + var res = sequence( [ + // templates can have placeholders for dynamic + // replacement eg: {{PLURAL:$1|one car|$1 cars}} + // or no placeholders eg: + // {{GRAMMAR:genitive|{{SITENAME}}} + choice( [ templateWithReplacement, templateWithOutReplacement ] ), + nOrMore( 0, templateParam ) + ] ); + + return res === null ? null : res[ 0 ].concat( res[ 1 ] ); + }, + function () { + var res = sequence( [ templateName, nOrMore( 0, templateParam ) ] ); + + if ( res === null ) { + return null; + } + + return [ res[ 0 ] ].concat( res[ 1 ] ); + } + ] ); + + openTemplate = makeStringParser( '{{' ); + closeTemplate = makeStringParser( '}}' ); + + function template() { + var result = sequence( [ openTemplate, templateContents, closeTemplate ] ); + + return result === null ? null : result[ 1 ]; + } + + expression = choice( [ template, replacement, literal ] ); + paramExpression = choice( [ template, replacement, literalWithoutBar ] ); + + function start() { + var result = nOrMore( 0, expression )(); + + if ( result === null ) { + return null; + } + + return [ 'CONCAT' ].concat( result ); + } + + result = start(); + + /* + * For success, the pos must have gotten to the end of the input + * and returned a non-null. + * n.b. This is part of language infrastructure, so we do not throw an + * internationalizable message. + */ + if ( result === null || pos !== message.length ) { + throw new Error( 'Parse error at position ' + pos.toString() + ' in input: ' + message ); + } + + return result; + } + + }; + + $.extend( $.i18n.parser, new MessageParser() ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/bs.js b/main/webapp/modules/core/externals/languages/bs.js new file mode 100644 index 000000000..41862ace8 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/bs.js @@ -0,0 +1,22 @@ +/** + * Bosnian (bosanski) language functions + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.bs = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'instrumental': // instrumental + word = 's ' + word; + break; + case 'lokativ': // locative + word = 'o ' + word; + break; + } + + return word; + } + } ); + +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/dsb.js b/main/webapp/modules/core/externals/languages/dsb.js new file mode 100644 index 000000000..a1d33ac2e --- /dev/null +++ b/main/webapp/modules/core/externals/languages/dsb.js @@ -0,0 +1,22 @@ +/** + * Lower Sorbian (Dolnoserbski) language functions + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.dsb = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'instrumental': // instrumental + word = 'z ' + word; + break; + case 'lokatiw': // lokatiw + word = 'wo ' + word; + break; + } + + return word; + } + } ); + +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/fi.js b/main/webapp/modules/core/externals/languages/fi.js new file mode 100644 index 000000000..345260160 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/fi.js @@ -0,0 +1,49 @@ +/** + * Finnish (Suomi) language functions + * + * @author Santhosh Thottingal + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.fi = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + // vowel harmony flag + var aou = word.match( /[aou][^äöy]*$/i ), + origWord = word; + if ( word.match( /wiki$/i ) ) { + aou = false; + } + + // append i after final consonant + if ( word.match( /[bcdfghjklmnpqrstvwxz]$/i ) ) { + word += 'i'; + } + + switch ( form ) { + case 'genitive': + word += 'n'; + break; + case 'elative': + word += ( aou ? 'sta' : 'stä' ); + break; + case 'partitive': + word += ( aou ? 'a' : 'ä' ); + break; + case 'illative': + // Double the last letter and add 'n' + word += word.slice( -1 ) + 'n'; + break; + case 'inessive': + word += ( aou ? 'ssa' : 'ssä' ); + break; + default: + word = origWord; + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/ga.js b/main/webapp/modules/core/externals/languages/ga.js new file mode 100644 index 000000000..843673a1e --- /dev/null +++ b/main/webapp/modules/core/externals/languages/ga.js @@ -0,0 +1,38 @@ +/** + * Irish (Gaeilge) language functions + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.ga = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + if ( form === 'ainmlae' ) { + switch ( word ) { + case 'an Domhnach': + word = 'Dé Domhnaigh'; + break; + case 'an Luan': + word = 'Dé Luain'; + break; + case 'an Mháirt': + word = 'Dé Mháirt'; + break; + case 'an Chéadaoin': + word = 'Dé Chéadaoin'; + break; + case 'an Déardaoin': + word = 'Déardaoin'; + break; + case 'an Aoine': + word = 'Dé hAoine'; + break; + case 'an Satharn': + word = 'Dé Sathairn'; + break; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/he.js b/main/webapp/modules/core/externals/languages/he.js new file mode 100644 index 000000000..b59342912 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/he.js @@ -0,0 +1,31 @@ +/** + * Hebrew (עברית) language functions + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.he = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'prefixed': + case 'תחילית': // the same word in Hebrew + // Duplicate prefixed "Waw", but only if it's not already double + if ( word.slice( 0, 1 ) === 'ו' && word.slice( 0, 2 ) !== 'וו' ) { + word = 'ו' + word; + } + + // Remove the "He" if prefixed + if ( word.slice( 0, 1 ) === 'ה' ) { + word = word.slice( 1 ); + } + + // Add a hyphen (maqaf) before numbers and non-Hebrew letters + if ( word.slice( 0, 1 ) < 'א' || word.slice( 0, 1 ) > 'ת' ) { + word = '־' + word; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/hsb.js b/main/webapp/modules/core/externals/languages/hsb.js new file mode 100644 index 000000000..2beb8dee0 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/hsb.js @@ -0,0 +1,21 @@ +/** + * Upper Sorbian (Hornjoserbsce) language functions + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.hsb = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'instrumental': // instrumental + word = 'z ' + word; + break; + case 'lokatiw': // lokatiw + word = 'wo ' + word; + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/hu.js b/main/webapp/modules/core/externals/languages/hu.js new file mode 100644 index 000000000..ec52f4c40 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/hu.js @@ -0,0 +1,26 @@ +/** + * Hungarian language functions + * + * @author Santhosh Thottingal + */ +( function ( $ ) { + 'use strict'; + + $.i18n.languages.hu = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'rol': + word += 'ról'; + break; + case 'ba': + word += 'ba'; + break; + case 'k': + word += 'k'; + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/hy.js b/main/webapp/modules/core/externals/languages/hy.js new file mode 100644 index 000000000..5ecf7ba56 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/hy.js @@ -0,0 +1,25 @@ +/** + * Armenian (Հայերեն) language functions + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.hy = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + if ( form === 'genitive' ) { // սեռական հոլով + if ( word.slice( -1 ) === 'ա' ) { + word = word.slice( 0, -1 ) + 'այի'; + } else if ( word.slice( -1 ) === 'ո' ) { + word = word.slice( 0, -1 ) + 'ոյի'; + } else if ( word.slice( -4 ) === 'գիրք' ) { + word = word.slice( 0, -4 ) + 'գրքի'; + } else { + word = word + 'ի'; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/la.js b/main/webapp/modules/core/externals/languages/la.js new file mode 100644 index 000000000..1a6c92bd9 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/la.js @@ -0,0 +1,54 @@ +/** + * Latin (lingua Latina) language functions + * + * @author Santhosh Thottingal + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.la = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'genitive': + // only a few declensions, and even for those mostly the singular only + word = word.replace( /u[ms]$/i, 'i' ); // 2nd declension singular + word = word.replace( /ommunia$/i, 'ommunium' ); // 3rd declension neuter plural (partly) + word = word.replace( /a$/i, 'ae' ); // 1st declension singular + word = word.replace( /libri$/i, 'librorum' ); // 2nd declension plural (partly) + word = word.replace( /nuntii$/i, 'nuntiorum' ); // 2nd declension plural (partly) + word = word.replace( /tio$/i, 'tionis' ); // 3rd declension singular (partly) + word = word.replace( /ns$/i, 'ntis' ); + word = word.replace( /as$/i, 'atis' ); + word = word.replace( /es$/i, 'ei' ); // 5th declension singular + break; + case 'accusative': + // only a few declensions, and even for those mostly the singular only + word = word.replace( /u[ms]$/i, 'um' ); // 2nd declension singular + word = word.replace( /ommunia$/i, 'am' ); // 3rd declension neuter plural (partly) + word = word.replace( /a$/i, 'ommunia' ); // 1st declension singular + word = word.replace( /libri$/i, 'libros' ); // 2nd declension plural (partly) + word = word.replace( /nuntii$/i, 'nuntios' );// 2nd declension plural (partly) + word = word.replace( /tio$/i, 'tionem' ); // 3rd declension singular (partly) + word = word.replace( /ns$/i, 'ntem' ); + word = word.replace( /as$/i, 'atem' ); + word = word.replace( /es$/i, 'em' ); // 5th declension singular + break; + case 'ablative': + // only a few declensions, and even for those mostly the singular only + word = word.replace( /u[ms]$/i, 'o' ); // 2nd declension singular + word = word.replace( /ommunia$/i, 'ommunibus' ); // 3rd declension neuter plural (partly) + word = word.replace( /a$/i, 'a' ); // 1st declension singular + word = word.replace( /libri$/i, 'libris' ); // 2nd declension plural (partly) + word = word.replace( /nuntii$/i, 'nuntiis' ); // 2nd declension plural (partly) + word = word.replace( /tio$/i, 'tione' ); // 3rd declension singular (partly) + word = word.replace( /ns$/i, 'nte' ); + word = word.replace( /as$/i, 'ate' ); + word = word.replace( /es$/i, 'e' ); // 5th declension singular + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/ml.js b/main/webapp/modules/core/externals/languages/ml.js new file mode 100644 index 000000000..d9c82cfb6 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/ml.js @@ -0,0 +1,98 @@ +/** + * Malayalam language functions + * + * @author Santhosh Thottingal + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.ml = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + form = form.toLowerCase(); + switch ( form ) { + case 'ഉദ്ദേശിക': + case 'dative': + if ( word.slice( -1 ) === 'ു' || + word.slice( -1 ) === 'ൂ' || + word.slice( -1 ) === 'ൗ' || + word.slice( -1 ) === 'ൌ' + ) { + word += 'വിന്'; + } else if ( word.slice( -1 ) === 'ം' ) { + word = word.slice( 0, -1 ) + 'ത്തിന്'; + } else if ( word.slice( -1 ) === 'ൻ' ) { + // Atomic chillu n. അവൻ -> അവന് + word = word.slice( 0, -1 ) + 'ന്'; + } else if ( word.slice( -3 ) === 'ന്\u200d' ) { + // chillu n. അവൻ -> അവന് + word = word.slice( 0, -1 ); + } else if ( word.slice( -1 ) === 'ൾ' || word.slice( -3 ) === 'ള്\u200d' ) { + word += 'ക്ക്'; + } else if ( word.slice( -1 ) === 'ർ' || word.slice( -3 ) === 'ര്\u200d' ) { + word += 'ക്ക്'; + } else if ( word.slice( -1 ) === 'ൽ' ) { + // Atomic chillu ൽ , ഫയൽ -> ഫയലിന് + word = word.slice( 0, -1 ) + 'ലിന്'; + } else if ( word.slice( -3 ) === 'ല്\u200d' ) { + // chillu ല്\u200d , ഫയല്\u200d -> ഫയലിന് + word = word.slice( 0, -2 ) + 'ിന്'; + } else if ( word.slice( -2 ) === 'ു്' ) { + word = word.slice( 0, -2 ) + 'ിന്'; + } else if ( word.slice( -1 ) === '്' ) { + word = word.slice( 0, -1 ) + 'ിന്'; + } else { + // കാവ്യ -> കാവ്യയ്ക്ക്, ഹരി -> ഹരിയ്ക്ക്, മല -> മലയ്ക്ക് + word += 'യ്ക്ക്'; + } + + break; + case 'സംബന്ധിക': + case 'genitive': + if ( word.slice( -1 ) === 'ം' ) { + word = word.slice( 0, -1 ) + 'ത്തിന്റെ'; + } else if ( word.slice( -2 ) === 'ു്' ) { + word = word.slice( 0, -2 ) + 'ിന്റെ'; + } else if ( word.slice( -1 ) === '്' ) { + word = word.slice( 0, -1 ) + 'ിന്റെ'; + } else if ( word.slice( -1 ) === 'ു' || + word.slice( -1 ) === 'ൂ' || + word.slice( -1 ) === 'ൗ' || + word.slice( -1 ) === 'ൌ' + ) { + word += 'വിന്റെ'; + } else if ( word.slice( -1 ) === 'ൻ' ) { + // Atomic chillu n. അവൻ -> അവന്റെ + word = word.slice( 0, -1 ) + 'ന്റെ'; + } else if ( word.slice( -3 ) === 'ന്\u200d' ) { + // chillu n. അവൻ -> അവന്റെ + word = word.slice( 0, -1 ) + 'റെ'; + } else if ( word.slice( -3 ) === 'ള്\u200d' ) { + // chillu n. അവൾ -> അവളുടെ + word = word.slice( 0, -2 ) + 'ുടെ'; + } else if ( word.slice( -1 ) === 'ൾ' ) { + // Atomic chillu n. അവള്\u200d -> അവളുടെ + word = word.slice( 0, -1 ) + 'ളുടെ'; + } else if ( word.slice( -1 ) === 'ൽ' ) { + // Atomic l. മുയല്\u200d -> മുയലിന്റെ + word = word.slice( 0, -1 ) + 'ലിന്റെ'; + } else if ( word.slice( -3 ) === 'ല്\u200d' ) { + // chillu l. മുയല്\u200d -> അവളുടെ + word = word.slice( 0, -2 ) + 'ിന്റെ'; + } else if ( word.slice( -3 ) === 'ര്\u200d' ) { + // chillu r. അവര്\u200d -> അവരുടെ + word = word.slice( 0, -2 ) + 'ുടെ'; + } else if ( word.slice( -1 ) === 'ർ' ) { + // Atomic chillu r. അവർ -> അവരുടെ + word = word.slice( 0, -1 ) + 'രുടെ'; + } else { + word += 'യുടെ'; + } + + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/os.js b/main/webapp/modules/core/externals/languages/os.js new file mode 100644 index 000000000..e788db942 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/os.js @@ -0,0 +1,75 @@ +/** + * Ossetian (Ирон) language functions + * + * @author Santhosh Thottingal + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.os = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + var endAllative, jot, hyphen, ending; + + // Ending for allative case + endAllative = 'мæ'; + // Variable for 'j' beetwen vowels + jot = ''; + // Variable for "-" for not Ossetic words + hyphen = ''; + // Variable for ending + ending = ''; + + if ( word.match( /тæ$/i ) ) { + // Checking if the $word is in plural form + word = word.slice( 0, -1 ); + endAllative = 'æм'; + } else if ( word.match( /[аæеёиоыэюя]$/i ) ) { + // Works if word is in singular form. + // Checking if word ends on one of the vowels: е, ё, и, о, ы, э, ю, + // я. + jot = 'й'; + } else if ( word.match( /у$/i ) ) { + // Checking if word ends on 'у'. 'У' can be either consonant 'W' or + // vowel 'U' in cyrillic Ossetic. + // Examples: {{grammar:genitive|аунеу}} = аунеуы, + // {{grammar:genitive|лæппу}} = лæппуйы. + if ( !word.slice( -2, -1 ).match( /[аæеёиоыэюя]$/i ) ) { + jot = 'й'; + } + } else if ( !word.match( /[бвгджзйклмнопрстфхцчшщьъ]$/i ) ) { + hyphen = '-'; + } + + switch ( form ) { + case 'genitive': + ending = hyphen + jot + 'ы'; + break; + case 'dative': + ending = hyphen + jot + 'æн'; + break; + case 'allative': + ending = hyphen + endAllative; + break; + case 'ablative': + if ( jot === 'й' ) { + ending = hyphen + jot + 'æ'; + } else { + ending = hyphen + jot + 'æй'; + } + break; + case 'superessive': + ending = hyphen + jot + 'ыл'; + break; + case 'equative': + ending = hyphen + jot + 'ау'; + break; + case 'comitative': + ending = hyphen + 'имæ'; + break; + } + + return word + ending; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/ru.js b/main/webapp/modules/core/externals/languages/ru.js new file mode 100644 index 000000000..60fefffaf --- /dev/null +++ b/main/webapp/modules/core/externals/languages/ru.js @@ -0,0 +1,29 @@ +/** + * Russian (Русский) language functions + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.ru = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + if ( form === 'genitive' ) { // родительный падеж + if ( word.slice( -1 ) === 'ь' ) { + word = word.slice( 0, -1 ) + 'я'; + } else if ( word.slice( -2 ) === 'ия' ) { + word = word.slice( 0, -2 ) + 'ии'; + } else if ( word.slice( -2 ) === 'ка' ) { + word = word.slice( 0, -2 ) + 'ки'; + } else if ( word.slice( -2 ) === 'ти' ) { + word = word.slice( 0, -2 ) + 'тей'; + } else if ( word.slice( -2 ) === 'ды' ) { + word = word.slice( 0, -2 ) + 'дов'; + } else if ( word.slice( -3 ) === 'ник' ) { + word = word.slice( 0, -3 ) + 'ника'; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/sl.js b/main/webapp/modules/core/externals/languages/sl.js new file mode 100644 index 000000000..0e14ed5f9 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/sl.js @@ -0,0 +1,26 @@ +/** + * Slovenian (Slovenščina) language functions + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.sl = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + // locative + case 'mestnik': + word = 'o ' + word; + + break; + // instrumental + case 'orodnik': + word = 'z ' + word; + + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/main/webapp/modules/core/externals/languages/uk.js b/main/webapp/modules/core/externals/languages/uk.js new file mode 100644 index 000000000..f5292cf84 --- /dev/null +++ b/main/webapp/modules/core/externals/languages/uk.js @@ -0,0 +1,39 @@ +/** + * Ukrainian (Українська) language functions + */ + +( function ( $ ) { + 'use strict'; + + $.i18n.languages.uk = $.extend( {}, $.i18n.languages[ 'default' ], { + convertGrammar: function ( word, form ) { + switch ( form ) { + case 'genitive': // родовий відмінок + if ( word.slice( -1 ) === 'ь' ) { + word = word.slice( 0, -1 ) + 'я'; + } else if ( word.slice( -2 ) === 'ія' ) { + word = word.slice( 0, -2 ) + 'ії'; + } else if ( word.slice( -2 ) === 'ка' ) { + word = word.slice( 0, -2 ) + 'ки'; + } else if ( word.slice( -2 ) === 'ти' ) { + word = word.slice( 0, -2 ) + 'тей'; + } else if ( word.slice( -2 ) === 'ды' ) { + word = word.slice( 0, -2 ) + 'дов'; + } else if ( word.slice( -3 ) === 'ник' ) { + word = word.slice( 0, -3 ) + 'ника'; + } + + break; + case 'accusative': // знахідний відмінок + if ( word.slice( -2 ) === 'ія' ) { + word = word.slice( 0, -2 ) + 'ію'; + } + + break; + } + + return word; + } + } ); + +}( jQuery ) ); diff --git a/main/webapp/modules/core/langs/translation-en.json b/main/webapp/modules/core/langs/translation-en.json index 999ee6029..fc35fb1f4 100644 --- a/main/webapp/modules/core/langs/translation-en.json +++ b/main/webapp/modules/core/langs/translation-en.json @@ -71,6 +71,9 @@ "core-index-import/several-file": "There are several files available. Please select the ones to import.", "core-index-import/sel-by-extension": "Select by Extension", "core-index-import/sel-by-regex": "Select by Regex on File Names", + "core-index-import/match-count" : "$1 {{plural:$1|match|matches}}", + "core-index-import/file-count" : "$1 {{plural:$1|file|files}}", + "core-index-import/files-selected" : "$1 of $2 {{plural:$2|file|files}} selected", "core-index-import/parsing-options": "Configure Parsing Options", "core-index-import/project-name": "Project name", "core-index-import/project-tags": "Tags", @@ -170,9 +173,8 @@ "core-dialogs/rows-in-cluster": "# Rows in Cluster", "core-dialogs/choice-avg-length": "Average Length of Choices", "core-dialogs/choice-var-length": "Length Variance of Choices", - "core-dialogs/found": "found", - "core-dialogs/filtered-from": "filtered from ", - "core-dialogs/from-total": " total", + "core-dialogs/clusters-found": "$1 {{plural:$1|cluster|clusters}} found", + "core-dialogs/clusters-filtered": "$1 {{plural:$1|cluster|clusters}} filtered from $2 total", "core-dialogs/cluster-descr": "This feature helps you find groups of different cell values that might be alternative representations of the same thing. For example, the two strings \"New York\" and \"new york\" are very likely to refer to the same concept and just have capitalization differences, and \"Gödel\" and \"Godel\" probably refer to the same person.", "core-dialogs/find-more": "Find out more…", "core-dialogs/method": "Method ", @@ -298,7 +300,8 @@ "core-facets/current-exp": "Current Expression", "core-facets/facet-choices": "Facet Choices as Tab Separated Values", "core-facets/loading": "Loading…", - "core-facets/too-many-choices": "choices total, too many to display", + "core-facets/choice-count": "$1 {{plural:$1|choice|choices}}", + "core-facets/too-many-choices": "$1 choices total, too many to display", "core-facets/set-choice-count": "Set choice count limit", "core-facets/edit": "edit", "core-facets/facet-by-count": "Facet by choice counts", diff --git a/main/webapp/modules/core/langs/translation-fr.json b/main/webapp/modules/core/langs/translation-fr.json index 61d042a23..aa51e34f5 100644 --- a/main/webapp/modules/core/langs/translation-fr.json +++ b/main/webapp/modules/core/langs/translation-fr.json @@ -79,6 +79,9 @@ "core-index-import/enter-url": "Indiquer une ou plusieurs adresses web (URLs) pointant vers les données à télécharger :", "core-index-import/pick-nodes": "Choisir les nœuds de l’entrée", "core-index-import/sel-by-regex": "Filtrer par une expression rationnelle (regex) sur les noms de fichier", + "core-index-import/match-count" : "$1 {{plural:$1|match|matchs}}", + "core-index-import/file-count" : "$1 {{plural:$1|fichier|fichiers}}", + "core-index-import/files-selected" : "$1 {{plural:$1|fichier|fichiers}} sur $2 sélectionnés", "core-index-import/name": "Nom", "core-index-import/parsing-options": "Configurer les options pour l’analyse syntaxique", "core-index-import/data-package": "Data Package (URL JSON)", @@ -153,7 +156,7 @@ "core-dialogs/html-table": "Table HTML", "core-dialogs/internal-err": "Erreur interne", "core-dialogs/error-apply-code": "Erreur lors de l’application du code d’option", - "core-dialogs/from-total": " total", + "core-dialogs/clusters-filtered": "$1 {{plural:$1|grappe|grappes}} filtrées sur $2 au total", "core-dialogs/out-col-header": "Écrire les entêtes de colonnes", "core-dialogs/tsv": "Valeurs séparées par des tabulations (TSV)", "core-dialogs/template-rowt": "Modèle de la ligne", @@ -174,7 +177,7 @@ "core-dialogs/merge": "Fusionner ?", "core-dialogs/cluster-descr": "Cet outil vous aide à identifier des groupes de cellules ayant des valeurs différentes mais qui peuvent correspondre à des représentations alternatives de la même valeur. Par exemple, les deux chaînes \"New York\" et \"new york\" n’ont qu’une différence de casse et font très certainement référence à la même ville. \"Gödel\" et \"Godel\" se réfèrent probablement à la même personne.", "clustering-keyers/cologne-phonetic": "phonétique de Cologne", - "core-dialogs/found": "trouvé", + "core-dialogs/clusters-found": "$1 {{plural:$1|grappe|grappes}} trouvée", "core-dialogs/starred": "Étoilée", "core-dialogs/find-more": "En savoir plus…", "core-dialogs/ngram-size": "Taille des N-grammes", @@ -312,7 +315,8 @@ "core-facets/linear-plot": "Tracé linéaire", "core-facets/rotated-clock": "Tourné de 45°", "core-facets/edit-facet-exp": "Éditer l’expression de la facette", - "core-facets/too-many-choices": "choix au total, trop nombreux à afficher", + "core-facets/choice-count": "$1 choix", + "core-facets/too-many-choices": "$1 choix au total, trop nombreux à afficher", "core-facets/error": "Erreur", "core-facets/export-plot": "exporter le tracé", "core-facets/big-dot": "Point de grande taille", diff --git a/main/webapp/modules/core/scripts/dialogs/clustering-dialog.js b/main/webapp/modules/core/scripts/dialogs/clustering-dialog.js index e390a6c82..c8e05d640 100644 --- a/main/webapp/modules/core/scripts/dialogs/clustering-dialog.js +++ b/main/webapp/modules/core/scripts/dialogs/clustering-dialog.js @@ -271,8 +271,8 @@ ClusteringDialog.prototype._renderTable = function(clusters) { this._elmts.resultSummary.html( (clusters.length === this._clusters.length) ? - ("" + this._clusters.length + " cluster" + ((this._clusters.length != 1) ? "s" : "") + " "+$.i18n('core-dialogs/found')) : - ("" + clusters.length + " cluster" + ((clusters.length != 1) ? "s" : "") + " "+$.i18n('core-dialogs/filtered-from')+ this._clusters.length +$.i18n('core-dialogs/from-total') ) + $.i18n('core-dialogs/clusters-found', this._clusters.length) : + $.i18n('core-dialogs/clusters-filtered', clusters.length, this._clusters.length) ); } else { diff --git a/main/webapp/modules/core/scripts/facets/list-facet.js b/main/webapp/modules/core/scripts/facets/list-facet.js index 78e6580c5..febaf7930 100644 --- a/main/webapp/modules/core/scripts/facets/list-facet.js +++ b/main/webapp/modules/core/scripts/facets/list-facet.js @@ -293,7 +293,7 @@ ListFacet.prototype._update = function(resetScroll) { this._elmts.bodyInnerDiv.empty(); var messageDiv = $('
') - .text(this._data.choiceCount + " "+$.i18n('core-facets/too-many-choices')) + .text($.i18n('core-facets/too-many-choices', this._data.choiceCount)) .addClass("facet-body-message") .appendTo(this._elmts.bodyInnerDiv); $('
').appendTo(messageDiv); @@ -348,7 +348,7 @@ ListFacet.prototype._update = function(resetScroll) { (this._blankChoice !== null && this._blankChoice.s ? 1 : 0) + (this._errorChoice !== null && this._errorChoice.s ? 1 : 0); - this._elmts.choiceCountContainer.text(choices.length + " choices"); + this._elmts.choiceCountContainer.text($.i18n("core-facets/choice-count", choices.length)); if (selectionCount > 0) { this._elmts.resetButton.show(); this._elmts.invertButton.show(); diff --git a/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js b/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js index d7bc53f05..c5c69f709 100644 --- a/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js +++ b/main/webapp/modules/core/scripts/index/default-importing-controller/file-selection-panel.js @@ -200,7 +200,7 @@ Refine.DefaultImportingController.prototype._renderFileSelectionPanelControlPane var renderExtension = function(extension) { var tr = table.insertRow(table.rows.length); $('').text(extension.extension).appendTo(tr); - $('').text(extension.count + (extension.count > 1 ? " files" : " file")).appendTo(tr); + $('').text($.i18n('core-index-import/file-count'), extension.count).appendTo(tr); $('