diff options
author | Pierre Schmitz <pierre@archlinux.de> | 2015-12-20 09:00:55 +0100 |
---|---|---|
committer | Pierre Schmitz <pierre@archlinux.de> | 2015-12-20 09:00:55 +0100 |
commit | a2190ac74dd4d7080b12bab90e552d7aa81209ef (patch) | |
tree | 8b31f38de9882d18df54cf8d9e0de74167a094eb /resources/lib/jquery.i18n/src | |
parent | 15e69f7b20b6596b9148030acce5b59993b95a45 (diff) | |
parent | 257401d8b2cf661adf36c84b0e3fd1cf85e33c22 (diff) |
Merge branch 'mw-1.26'
Diffstat (limited to 'resources/lib/jquery.i18n/src')
21 files changed, 2191 insertions, 0 deletions
diff --git a/resources/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js b/resources/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js new file mode 100644 index 00000000..3a5b6251 --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.emitter.bidi.js @@ -0,0 +1,88 @@ +/** + * 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://git.wikimedia.org/summary/unicodejs.git . + */ + 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. + */ + 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. + */ + 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/resources/lib/jquery.i18n/src/jquery.i18n.emitter.js b/resources/lib/jquery.i18n/src/jquery.i18n.emitter.js new file mode 100644 index 00000000..b26f147d --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.emitter.js @@ -0,0 +1,168 @@ +/** + * 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 MessageParserEmitter = function () { + this.language = $.i18n.languages[String.locale] || $.i18n.languages['default']; + }; + + MessageParserEmitter.prototype = { + constructor: MessageParserEmitter, + + /** + * (We put this method definition here, and not in prototype, to make + * sure it's not overwritten by any magic.) Walk entire node structure, + * applying replacements and template functions when appropriate + * + * @param {Mixed} node abstract syntax tree (top node or subnode) + * @param {Array} replacements for $1, $2, ... $n + * @return {Mixed} single-string node or array of nodes suitable for + * jQuery appending. + */ + emit: function ( node, replacements ) { + var ret, subnodes, operation, + messageParserEmitter = this; + + switch ( typeof node ) { + case 'string': + case 'number': + ret = node; + break; + case 'object': + // node is an array of nodes + subnodes = $.map( node.slice( 1 ), function ( n ) { + return messageParserEmitter.emit( n, replacements ); + } ); + + operation = node[0].toLowerCase(); + + if ( typeof messageParserEmitter[operation] === 'function' ) { + ret = messageParserEmitter[operation]( subnodes, replacements ); + } else { + throw new Error( 'unknown operation "' + operation + '"' ); + } + + break; + case 'undefined': + // Parsing the empty string (as an entire expression, or as a + // paramExpression in a template) results in undefined + // Perhaps a more clever parser can detect this, and return the + // empty string? Or is that useful information? + // The logical thing is probably to return the empty string here + // when we encounter undefined. + ret = ''; + break; + default: + throw new Error( 'unexpected type in AST: ' + typeof node ); + } + + return ret; + }, + + /** + * Parsing has been applied depth-first we can assume that all nodes + * here are single nodes Must return a single node to parents -- a + * jQuery with synthetic span However, unwrap any other synthetic spans + * in our children and pass them upwards + * + * @param {Array} nodes Mixed, some single nodes, some arrays of nodes. + * @return String + */ + concat: function ( nodes ) { + var result = ''; + + $.each( nodes, function ( i, node ) { + // strings, integers, anything else + result += node; + } ); + + return result; + }, + + /** + * Return escaped replacement of correct index, or string if + * unavailable. Note that we expect the parsed parameter to be + * zero-based. i.e. $1 should have become [ 0 ]. if the specified + * parameter is not found return the same string (e.g. "$99" -> + * 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/resources/lib/jquery.i18n/src/jquery.i18n.fallbacks.js b/resources/lib/jquery.i18n/src/jquery.i18n.fallbacks.js new file mode 100644 index 00000000..4584c5f2 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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 ( $, undefined ) { + '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/resources/lib/jquery.i18n/src/jquery.i18n.js b/resources/lib/jquery.i18n/src/jquery.i18n.js new file mode 100644 index 00000000..9236e4e2 --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.js @@ -0,0 +1,287 @@ +/** + * 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'; + + var nav, 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 = {}; + + this.init(); + }; + + I18N.prototype = { + /** + * Initialize by loading locales and setting up + * String.prototype.toLocaleString and String.locale. + */ + init: function () { + var i18n = this; + + // Set locale of String environment + String.locale = i18n.locale; + + // Override String.localeString method + String.prototype.toLocaleString = function () { + var localeParts, localePartIndex, value, locale, fallbackIndex, + tryingLocale, message; + + value = this.valueOf(); + locale = i18n.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 = i18n.messageStore.get( tryingLocale, value ); + + if ( message ) { + return message; + } + + localePartIndex--; + } while ( localePartIndex ); + + if ( locale === 'en' ) { + break; + } + + locale = ( $.i18n.fallbacks[i18n.locale] && $.i18n.fallbacks[i18n.locale][fallbackIndex] ) || + i18n.options.fallbackLocale; + $.i18n.log( 'Trying fallback locale for ' + i18n.locale + ': ' + locale ); + + 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: + * <code>load('path/to/all_localizations.json');</code> + * + * To load a localization file for a locale: + * <code> + * load('path/to/de-messages.json', 'de' ); + * </code> + * + * To load a localization file from a directory: + * <code> + * load('path/to/i18n/directory', 'de' ); + * </code> + * 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: + * <code> + * load( { 'hello' : 'Hello' }, optionalLocale ); + * </code> + * + * A source map containing key-value pair of languagename and locations + * can also be passed. Example: + * <code> + * load( { + * bn: 'i18n/bn.json', + * he: 'i18n/he.json', + * en: 'i18n/en.json' + * } ) + * </code> + * + * 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 + * @returns {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.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 in fallbackLocales ) { + 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 = key.toLocaleString(); + // 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 ) { + String.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 ); + } + String.locale = i18n.locale; + return this.each( function () { + var $this = $( this ), + messageKey = $this.data( 'i18n' ); + + if ( messageKey ) { + $this.text( i18n.parse( messageKey ) ); + } else { + $this.find( '[data-i18n]' ).i18n(); + } + } ); + }; + + String.locale = String.locale || $( 'html' ).attr( 'lang' ); + + if ( !String.locale ) { + if ( typeof window.navigator !== undefined ) { + nav = window.navigator; + String.locale = nav.language || nav.userLanguage || ''; + } else { + String.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: String.locale, + fallbackLocale: 'en', + parser: $.i18n.parser, + messageStore: $.i18n.messageStore + }; + + // Expose constructor + $.i18n.constructor = I18N; +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/jquery.i18n.language.js b/resources/lib/jquery.i18n/src/jquery.i18n.language.js new file mode 100644 index 00000000..e596fbc8 --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.language.js @@ -0,0 +1,472 @@ +/*global pluralRuleParser */ +( function ( $ ) { + 'use strict'; + + var language = { + // CLDR plural rules generated using + // libs/CLDRPluralRuleParser/tools/PluralXML2JSON.html + 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' + }, + // jscs:disable requireCamelCaseOrUpperCaseIdentifiers + pt_PT: { + one: 'n = 1 and v = 0' + }, + // jscs:enable requireCamelCaseOrUpperCaseIdentifiers + 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' + } + }, + + /** + * Plural form transformations, needed for some languages. + * + * @param count + * integer Non-localized quantifier + * @param forms + * array List of plural forms + * @return string Correct form for quantifier in this language + */ + convertPlural: function ( count, forms ) { + var pluralRules, + pluralFormIndex, + index, + explicitPluralPattern = new RegExp( '\\d+=', 'i' ), + formCount, + form; + + if ( !forms || forms.length === 0 ) { + return ''; + } + + // Handle for Explicit 0= & 1= values + for ( index = 0; index < forms.length; index++ ) { + form = forms[index]; + if ( explicitPluralPattern.test( form ) ) { + formCount = parseInt( form.substring( 0, form.indexOf( '=' ) ), 10 ); + if ( formCount === count ) { + return ( form.substr( form.indexOf( '=' ) + 1 ) ); + } + forms[index] = undefined; + } + } + + forms = $.map( forms, function ( form ) { + if ( form !== undefined ) { + return form; + } + } ); + + pluralRules = this.pluralRules[$.i18n().locale]; + + if ( !pluralRules ) { + // default fallback. + return ( count === 1 ) ? forms[0] : forms[1]; + } + + pluralFormIndex = this.getPluralForm( count, pluralRules ); + pluralFormIndex = Math.min( pluralFormIndex, forms.length - 1 ); + + return forms[pluralFormIndex]; + }, + + /** + * For the number, get the plural for index + * + * @param number + * @param pluralRules + * @return plural form index + */ + getPluralForm: function ( number, pluralRules ) { + var i, + pluralForms = [ 'zero', 'one', 'two', 'few', 'many', 'other' ], + pluralFormIndex = 0; + + for ( i = 0; i < pluralForms.length; i++ ) { + if ( pluralRules[pluralForms[i]] ) { + if ( pluralRuleParser( pluralRules[pluralForms[i]], number ) ) { + return pluralFormIndex; + } + + pluralFormIndex++; + } + } + + return pluralFormIndex; + }, + + /** + * Converts a number using digitTransformTable. + * + * @param {number} num Value to be converted + * @param {boolean} integer Convert the return value to an integer + */ + convertNumber: function ( num, integer ) { + var tmp, item, i, + transformTable, numberString, convertedNumber; + + // Set the target Transform table: + transformTable = this.digitTransformTable( $.i18n().locale ); + numberString = String( num ); + convertedNumber = ''; + + if ( !transformTable ) { + return num; + } + + // Check if the restore to Latin number flag is set: + if ( integer ) { + if ( parseFloat( num, 10 ) === num ) { + return num; + } + + tmp = []; + + for ( item in transformTable ) { + tmp[transformTable[item]] = item; + } + + transformTable = tmp; + } + + for ( i = 0; i < numberString.length; i++ ) { + if ( transformTable[numberString[i]] ) { + convertedNumber += transformTable[numberString[i]]; + } else { + convertedNumber += numberString[i]; + } + } + + return integer ? parseFloat( convertedNumber, 10 ) : convertedNumber; + }, + + /** + * Grammatical transformations, needed for inflected languages. + * Invoked by putting {{grammar:form|word}} in a message. + * Override this method for languages that need special grammar rules + * applied dynamically. + * + * @param word {String} + * @param form {String} + * @return {String} + */ + convertGrammar: function ( word, form ) { /*jshint unused: false */ + return word; + }, + + /** + * Provides an alternative text depending on specified gender. Usage + * {{gender:[gender|user object]|masculine|feminine|neutral}}. If second + * or third parameter are not specified, masculine is used. + * + * These details may be overriden per language. + * + * @param gender + * string male, female, or anything else for neutral. + * @param forms + * array List of gender forms + * + * @return string + */ + gender: function ( gender, forms ) { + if ( !forms || forms.length === 0 ) { + return ''; + } + + while ( forms.length < 2 ) { + forms.push( forms[forms.length - 1] ); + } + + if ( gender === 'male' ) { + return forms[0]; + } + + if ( gender === 'female' ) { + return forms[1]; + } + + return ( forms.length === 3 ) ? forms[2] : forms[0]; + }, + + /** + * Get the digit transform table for the given language + * See http://cldr.unicode.org/translation/numbering-systems + * @param language + * @returns {Array|boolean} List of digits in the passed language or false + * representation, or boolean false if there is no information. + */ + digitTransformTable: function ( language ) { + var tables = { + ar: '٠١٢٣٤٥٦٧٨٩', + fa: '۰۱۲۳۴۵۶۷۸۹', + ml: '൦൧൨൩൪൫൬൭൮൯', + kn: '೦೧೨೩೪೫೬೭೮೯', + lo: '໐໑໒໓໔໕໖໗໘໙', + or: '୦୧୨୩୪୫୬୭୮୯', + kh: '០១២៣៤៥៦៧៨៩', + pa: '੦੧੨੩੪੫੬੭੮੯', + gu: '૦૧૨૩૪૫૬૭૮૯', + hi: '०१२३४५६७८९', + my: '၀၁၂၃၄၅၆၇၈၉', + ta: '௦௧௨௩௪௫௬௭௮௯', + te: '౦౧౨౩౪౫౬౭౮౯', + th: '๐๑๒๓๔๕๖๗๘๙', // FIXME use iso 639 codes + bo: '༠༡༢༣༤༥༦༧༨༩' // FIXME use iso 639 codes + }; + + if ( !tables[language] ) { + return false; + } + + return tables[language].split( '' ); + } + }; + + $.extend( $.i18n.languages, { + 'default': language + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/jquery.i18n.messagestore.js b/resources/lib/jquery.i18n/src/jquery.i18n.messagestore.js new file mode 100644 index 00000000..759295ce --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.messagestore.js @@ -0,0 +1,125 @@ +/** + * jQuery Internationalization library - Message Store + * + * 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 ( $, window, undefined ) { + 'use strict'; + + var MessageStore = function () { + this.messages = {}; + this.sources = {}; + }; + + /** + * See https://github.com/wikimedia/jquery.i18n/wiki/Specification#wiki-Message_File_Loading + */ + MessageStore.prototype = { + + /** + * General message loading API This can take a URL string for + * the json formatted messages. + * <code>load('path/to/all_localizations.json');</code> + * + * This can also load a localization file for a locale <code> + * load( 'path/to/de-messages.json', 'de' ); + * </code> + * A data object containing message key- message translation mappings + * can also be passed Eg: + * <code> + * load( { 'hello' : 'Hello' }, optionalLocale ); + * </code> 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, + deferred = null, + deferreds = [], + messageStore = this; + + if ( typeof source === 'string' ) { + // This is a URL to the messages file. + $.i18n.log( 'Loading messages from: ' + source ); + deferred = jsonMessageLoader( source ) + .done( function ( localization ) { + messageStore.set( locale, localization ); + } ); + + return deferred.promise(); + } + + 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 locale + * @param messages + */ + set: function ( locale, messages ) { + if ( !this.messages[locale] ) { + this.messages[locale] = messages; + } else { + this.messages[locale] = $.extend( this.messages[locale], messages ); + } + }, + + /** + * + * @param locale + * @param messageKey + * @returns {Boolean} + */ + get: function ( locale, messageKey ) { + return this.messages[locale] && this.messages[locale][messageKey]; + } + }; + + function jsonMessageLoader( url ) { + var deferred = $.Deferred(); + + $.getJSON( url ) + .done( deferred.resolve ) + .fail( function ( jqxhr, settings, exception ) { + $.i18n.log( 'Error in loading messages from ' + url + ' Exception: ' + exception ); + // Ignore 404 exception, because we are handling fallabacks explicitly + deferred.resolve(); + } ); + + return deferred.promise(); + } + + $.extend( $.i18n.messageStore, new MessageStore() ); +}( jQuery, window ) ); diff --git a/resources/lib/jquery.i18n/src/jquery.i18n.parser.js b/resources/lib/jquery.i18n/src/jquery.i18n.parser.js new file mode 100644 index 00000000..3dea2842 --- /dev/null +++ b/resources/lib/jquery.i18n/src/jquery.i18n.parser.js @@ -0,0 +1,309 @@ +/** + * 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.substr( pos, len ) === s ) { + result = s; + pos += len; + } + + return result; + }; + } + + function makeRegexParser( regex ) { + return function () { + var matches = message.substr( 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/resources/lib/jquery.i18n/src/languages/bs.js b/resources/lib/jquery.i18n/src/languages/bs.js new file mode 100644 index 00000000..5370069e --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/dsb.js b/resources/lib/jquery.i18n/src/languages/dsb.js new file mode 100644 index 00000000..cc069ebf --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/fi.js b/resources/lib/jquery.i18n/src/languages/fi.js new file mode 100644 index 00000000..d8e9578a --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( word.length - 1 ) + 'n'; + break; + case 'inessive': + word += ( aou ? 'ssa' : 'ssä' ); + break; + default: + word = origWord; + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/languages/ga.js b/resources/lib/jquery.i18n/src/languages/ga.js new file mode 100644 index 00000000..1aceab75 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/he.js b/resources/lib/jquery.i18n/src/languages/he.js new file mode 100644 index 00000000..cbbe90b9 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( 0, 1 ) === 'ו' && word.substr( 0, 2 ) !== 'וו' ) { + word = 'ו' + word; + } + + // Remove the "He" if prefixed + if ( word.substr( 0, 1 ) === 'ה' ) { + word = word.substr( 1, word.length ); + } + + // Add a hyphen (maqaf) before numbers and non-Hebrew letters + if ( word.substr( 0, 1 ) < 'א' || word.substr( 0, 1 ) > 'ת' ) { + word = '־' + word; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/languages/hsb.js b/resources/lib/jquery.i18n/src/languages/hsb.js new file mode 100644 index 00000000..957616f6 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/hu.js b/resources/lib/jquery.i18n/src/languages/hu.js new file mode 100644 index 00000000..1177b850 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/hy.js b/resources/lib/jquery.i18n/src/languages/hy.js new file mode 100644 index 00000000..9c568992 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( -1 ) === 'ա' ) { + word = word.substr( 0, word.length - 1 ) + 'այի'; + } else if ( word.substr( -1 ) === 'ո' ) { + word = word.substr( 0, word.length - 1 ) + 'ոյի'; + } else if ( word.substr( -4 ) === 'գիրք' ) { + word = word.substr( 0, word.length - 4 ) + 'գրքի'; + } else { + word = word + 'ի'; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/languages/la.js b/resources/lib/jquery.i18n/src/languages/la.js new file mode 100644 index 00000000..11c1122d --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/ml.js b/resources/lib/jquery.i18n/src/languages/ml.js new file mode 100644 index 00000000..f724b7b2 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( -1 ) === 'ു' || + word.substr( -1 ) === 'ൂ' || + word.substr( -1 ) === 'ൗ' || + word.substr( -1 ) === 'ൌ' + ) { + word += 'വിന്'; + } else if ( word.substr( -1 ) === 'ം' ) { + word = word.substr( 0, word.length - 1 ) + 'ത്തിന്'; + } else if ( word.substr( -1 ) === 'ൻ' ) { + // Atomic chillu n. അവൻ -> അവന് + word = word.substr( 0, word.length - 1 ) + 'ന്'; + } else if ( word.substr( -3 ) === 'ന്\u200d' ) { + // chillu n. അവൻ -> അവന് + word = word.substr( 0, word.length - 1 ); + } else if ( word.substr( -1 ) === 'ൾ' || word.substr( -3 ) === 'ള്\u200d' ) { + word += 'ക്ക്'; + } else if ( word.substr( -1 ) === 'ർ' || word.substr( -3 ) === 'ര്\u200d' ) { + word += 'ക്ക്'; + } else if ( word.substr( -1 ) === 'ൽ' ) { + // Atomic chillu ൽ , ഫയൽ -> ഫയലിന് + word = word.substr( 0, word.length - 1 ) + 'ലിന്'; + } else if ( word.substr( -3 ) === 'ല്\u200d' ) { + // chillu ല്\u200d , ഫയല്\u200d -> ഫയലിന് + word = word.substr( 0, word.length - 2 ) + 'ിന്'; + } else if ( word.substr( -2 ) === 'ു്' ) { + word = word.substr( 0, word.length - 2 ) + 'ിന്'; + } else if ( word.substr( -1 ) === '്' ) { + word = word.substr( 0, word.length - 1 ) + 'ിന്'; + } else { + // കാവ്യ -> കാവ്യയ്ക്ക്, ഹരി -> ഹരിയ്ക്ക്, മല -> മലയ്ക്ക് + word += 'യ്ക്ക്'; + } + + break; + case 'സംബന്ധിക': + case 'genitive': + if ( word.substr( -1 ) === 'ം' ) { + word = word.substr( 0, word.length - 1 ) + 'ത്തിന്റെ'; + } else if ( word.substr( -2 ) === 'ു്' ) { + word = word.substr( 0, word.length - 2 ) + 'ിന്റെ'; + } else if ( word.substr( -1 ) === '്' ) { + word = word.substr( 0, word.length - 1 ) + 'ിന്റെ'; + } else if ( word.substr( -1 ) === 'ു' || + word.substr( -1 ) === 'ൂ' || + word.substr( -1 ) === 'ൗ' || + word.substr( -1 ) === 'ൌ' + ) { + word += 'വിന്റെ'; + } else if ( word.substr( -1 ) === 'ൻ' ) { + // Atomic chillu n. അവൻ -> അവന്റെ + word = word.substr( 0, word.length - 1 ) + 'ന്റെ'; + } else if ( word.substr( -3 ) === 'ന്\u200d' ) { + // chillu n. അവൻ -> അവന്റെ + word = word.substr( 0, word.length - 1 ) + 'റെ'; + } else if ( word.substr( -3 ) === 'ള്\u200d' ) { + // chillu n. അവൾ -> അവളുടെ + word = word.substr( 0, word.length - 2 ) + 'ുടെ'; + } else if ( word.substr( -1 ) === 'ൾ' ) { + // Atomic chillu n. അവള്\u200d -> അവളുടെ + word = word.substr( 0, word.length - 1 ) + 'ളുടെ'; + } else if ( word.substr( -1 ) === 'ൽ' ) { + // Atomic l. മുയല്\u200d -> മുയലിന്റെ + word = word.substr( 0, word.length - 1 ) + 'ലിന്റെ'; + } else if ( word.substr( -3 ) === 'ല്\u200d' ) { + // chillu l. മുയല്\u200d -> അവളുടെ + word = word.substr( 0, word.length - 2 ) + 'ിന്റെ'; + } else if ( word.substr( -3 ) === 'ര്\u200d' ) { + // chillu r. അവര്\u200d -> അവരുടെ + word = word.substr( 0, word.length - 2 ) + 'ുടെ'; + } else if ( word.substr( -1 ) === 'ർ' ) { + // Atomic chillu r. അവർ -> അവരുടെ + word = word.substr( 0, word.length - 1 ) + 'രുടെ'; + } else { + word += 'യുടെ'; + } + + break; + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/languages/os.js b/resources/lib/jquery.i18n/src/languages/os.js new file mode 100644 index 00000000..47443671 --- /dev/null +++ b/resources/lib/jquery.i18n/src/languages/os.js @@ -0,0 +1,76 @@ +/** + * 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.substring( 0, word.length - 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.substring( word.length - 2, word.length - 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/resources/lib/jquery.i18n/src/languages/ru.js b/resources/lib/jquery.i18n/src/languages/ru.js new file mode 100644 index 00000000..893b2386 --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( -1 ) === 'ь' ) { + word = word.substr( 0, word.length - 1 ) + 'я'; + } else if ( word.substr( -2 ) === 'ия' ) { + word = word.substr( 0, word.length - 2 ) + 'ии'; + } else if ( word.substr( -2 ) === 'ка' ) { + word = word.substr( 0, word.length - 2 ) + 'ки'; + } else if ( word.substr( -2 ) === 'ти' ) { + word = word.substr( 0, word.length - 2 ) + 'тей'; + } else if ( word.substr( -2 ) === 'ды' ) { + word = word.substr( 0, word.length - 2 ) + 'дов'; + } else if ( word.substr( -3 ) === 'ник' ) { + word = word.substr( 0, word.length - 3 ) + 'ника'; + } + } + + return word; + } + } ); +}( jQuery ) ); diff --git a/resources/lib/jquery.i18n/src/languages/sl.js b/resources/lib/jquery.i18n/src/languages/sl.js new file mode 100644 index 00000000..a3aafc3b --- /dev/null +++ b/resources/lib/jquery.i18n/src/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/resources/lib/jquery.i18n/src/languages/uk.js b/resources/lib/jquery.i18n/src/languages/uk.js new file mode 100644 index 00000000..8e69efcd --- /dev/null +++ b/resources/lib/jquery.i18n/src/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.substr( -1 ) === 'ь' ) { + word = word.substr( 0, word.length - 1 ) + 'я'; + } else if ( word.substr( -2 ) === 'ія' ) { + word = word.substr( 0, word.length - 2 ) + 'ії'; + } else if ( word.substr( -2 ) === 'ка' ) { + word = word.substr( 0, word.length - 2 ) + 'ки'; + } else if ( word.substr( -2 ) === 'ти' ) { + word = word.substr( 0, word.length - 2 ) + 'тей'; + } else if ( word.substr( -2 ) === 'ды' ) { + word = word.substr( 0, word.length - 2 ) + 'дов'; + } else if ( word.substr( -3 ) === 'ник' ) { + word = word.substr( 0, word.length - 3 ) + 'ника'; + } + + break; + case 'accusative': // знахідний відмінок + if ( word.substr( -2 ) === 'ія' ) { + word = word.substr( 0, word.length - 2 ) + 'ію'; + } + + break; + } + + return word; + } + } ); + +}( jQuery ) ); |