From 6ec7c032b3a87159f14d6d2138e03d73fc8b57b0 Mon Sep 17 00:00:00 2001 From: Rob Cresswell Date: Fri, 28 Oct 2016 13:25:57 +0200 Subject: [PATCH] Update xstatic-angular-gettext to 2.3.8.0 Change-Id: Ic0c5929299b6f5ad9347b579b0f9d60d1921bfd9 --- MANIFEST.in | 4 +- setup.cfg | 20 + setup.py | 8 +- xstatic/pkg/angular_gettext/__init__.py | 12 +- .../angular_gettext/data/angular-gettext.js | 550 ++++++++++++++++-- 5 files changed, 540 insertions(+), 54 deletions(-) create mode 100644 setup.cfg diff --git a/MANIFEST.in b/MANIFEST.in index 399e4d1..af3cdd3 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,8 +1,6 @@ include README.txt -recursive-include xstatic/pkg/angular_gettext * - +recursive-include xstatic * global-exclude *.pyc global-exclude *.pyo global-exclude *.orig global-exclude *.rej - diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..6f4a213 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,20 @@ +[metadata] +name = XStatic-Angular-Gettext +description = Angular-Gettext 2.3.8 (XStatic packaging standard) +description-file = README.rst +maintainer = Rob Cresswell +maintainer-email = robert.cresswell@outlook.com +home-page = https://angular-gettext.rocketeer.be/ +keywords = angular_gettext xstatic +license = MIT +zip_safe = False +namespace_packages = + xstatic + xstatic.pkg + +[files] +packages = + xstatic + +[bdist_wheel] +universal = True diff --git a/setup.py b/setup.py index 04abe30..8258e22 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,10 @@ +from setuptools import setup, find_packages from xstatic.pkg import angular_gettext as xs # The README.txt file should be written in reST so that PyPI can use # it to generate your project's PyPI page. long_description = open('README.txt').read() -from setuptools import setup, find_packages - setup( name=xs.PACKAGE_NAME, version=xs.PACKAGE_VERSION, @@ -19,9 +18,8 @@ setup( url=xs.HOMEPAGE, platforms=xs.PLATFORMS, packages=find_packages(), - namespace_packages=['xstatic', 'xstatic.pkg', ], + namespace_packages=['xstatic', 'xstatic.pkg'], include_package_data=True, zip_safe=False, - install_requires=[], # nothing! :) - # if you like, you MAY use the 'XStatic' package. + install_requires=[], ) diff --git a/xstatic/pkg/angular_gettext/__init__.py b/xstatic/pkg/angular_gettext/__init__.py index a46370e..0de8860 100644 --- a/xstatic/pkg/angular_gettext/__init__.py +++ b/xstatic/pkg/angular_gettext/__init__.py @@ -11,9 +11,9 @@ NAME = __name__.split('.')[-1] # package name (e.g. 'foo' or 'foo_bar') # please use a all-lowercase valid python # package name -VERSION = '2.1.0' # version of the packaged files, please use the upstream +VERSION = '2.3.8' # version of the packaged files, please use the upstream # version number -BUILD = '1' # our package build number, so we can release new builds +BUILD = '0' # our package build number, so we can release new builds # with fixes for xstatic stuff. PACKAGE_VERSION = VERSION + '.' + BUILD # version used for PyPi @@ -24,20 +24,22 @@ CLASSIFIERS = [] KEYWORDS = '%s xstatic' % NAME # XStatic-* package maintainer: -MAINTAINER = 'Thai Tran' -MAINTAINER_EMAIL = 'tqtran@us.ibm.com' +MAINTAINER = 'Rob Cresswell' +MAINTAINER_EMAIL = 'robert.cresswell@outlook.com' # this refers to the project homepage of the stuff we packaged: HOMEPAGE = 'https://angular-gettext.rocketeer.be/' # this refers to all files: -LICENSE = '(same as %s)' % DISPLAY_NAME +LICENSE = 'MIT' from os.path import join, dirname BASE_DIR = join(dirname(__file__), 'data') # linux package maintainers just can point to their file locations like this: #BASE_DIR = '/usr/share/javascript/jquery' +MAIN='angular-gettext.js' + LOCATIONS = { # CDN locations (if no public CDN exists, use an empty dict) # if value is a string, it is a base location, just append relative diff --git a/xstatic/pkg/angular_gettext/data/angular-gettext.js b/xstatic/pkg/angular_gettext/data/angular-gettext.js index 0103653..198c067 100644 --- a/xstatic/pkg/angular_gettext/data/angular-gettext.js +++ b/xstatic/pkg/angular_gettext/data/angular-gettext.js @@ -1,5 +1,45 @@ +/** + * @ngdoc module + * @name gettext + * @packageName angular-gettext + * @description Super simple Gettext for Angular.JS + * + * A sample application can be found at https://github.com/rubenv/angular-gettext-example. + * This is an adaptation of the [TodoMVC](http://todomvc.com/) example. You can use this as a guideline while adding {@link angular-gettext angular-gettext} to your own application. + */ +/** + * @ngdoc factory + * @module gettext + * @name gettextPlurals + * @param {String} [langCode=en] language code + * @param {Number} [n=0] number to calculate form for + * @returns {Number} plural form number + * @description Provides correct plural form id for the given language + * + * Example + * ```js + * gettextPlurals('ru', 10); // 1 + * gettextPlurals('en', 1); // 0 + * gettextPlurals(); // 1 + * ``` + */ angular.module('gettext', []); - +/** + * @ngdoc object + * @module gettext + * @name gettext + * @kind function + * @param {String} str annotation key + * @description Gettext constant function for annotating strings + * + * ```js + * angular.module('myApp', ['gettext']).config(function(gettext) { + * /// MyApp document title + * gettext('my-app.title'); + * ... + * }) + * ``` + */ angular.module('gettext').constant('gettext', function (str) { /* * Does nothing, simply returns the input string. @@ -10,7 +50,19 @@ angular.module('gettext').constant('gettext', function (str) { return str; }); -angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", "$cacheFactory", "$interpolate", "$rootScope", function (gettextPlurals, $http, $cacheFactory, $interpolate, $rootScope) { +/** + * @ngdoc service + * @module gettext + * @name gettextCatalog + * @requires gettextPlurals + * @requires gettextFallbackLanguage + * @requires https://docs.angularjs.org/api/ng/service/$http $http + * @requires https://docs.angularjs.org/api/ng/service/$cacheFactory $cacheFactory + * @requires https://docs.angularjs.org/api/ng/service/$interpolate $interpolate + * @requires https://docs.angularjs.org/api/ng/service/$rootScope $rootScope + * @description Provides set of method to translate stings + */ +angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "gettextFallbackLanguage", "$http", "$cacheFactory", "$interpolate", "$rootScope", function (gettextPlurals, gettextFallbackLanguage, $http, $cacheFactory, $interpolate, $rootScope) { var catalog; var noContext = '$$noContext'; @@ -38,34 +90,134 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", }; function broadcastUpdated() { + /** + * @ngdoc event + * @name gettextCatalog#gettextLanguageChanged + * @eventType broadcast on $rootScope + * @description Fires language change notification without any additional parameters. + */ $rootScope.$broadcast('gettextLanguageChanged'); } catalog = { + /** + * @ngdoc property + * @name gettextCatalog#debug + * @public + * @type {Boolean} false + * @see gettextCatalog#debug + * @description Whether or not to prefix untranslated strings with `[MISSING]:` or a custom prefix. + */ debug: false, + /** + * @ngdoc property + * @name gettextCatalog#debugPrefix + * @public + * @type {String} [MISSING]: + * @description Custom prefix for untranslated strings when {@link gettextCatalog#debug gettextCatalog#debug} set to `true`. + */ debugPrefix: '[MISSING]: ', + /** + * @ngdoc property + * @name gettextCatalog#showTranslatedMarkers + * @public + * @type {Boolean} false + * @description Whether or not to wrap all processed text with markers. + * + * Example output: `[Welcome]` + */ showTranslatedMarkers: false, + /** + * @ngdoc property + * @name gettextCatalog#translatedMarkerPrefix + * @public + * @type {String} [ + * @description Custom prefix to mark strings that have been run through {@link angular-gettext angular-gettext}. + */ translatedMarkerPrefix: '[', + /** + * @ngdoc property + * @name gettextCatalog#translatedMarkerSuffix + * @public + * @type {String} ] + * @description Custom suffix to mark strings that have been run through {@link angular-gettext angular-gettext}. + */ translatedMarkerSuffix: ']', + /** + * @ngdoc property + * @name gettextCatalog#strings + * @private + * @type {Object} + * @description An object of loaded translation strings. Shouldn't be used directly. + */ strings: {}, + /** + * @ngdoc property + * @name gettextCatalog#baseLanguage + * @protected + * @deprecated + * @since 2.0 + * @type {String} en + * @description The default language, in which you're application is written. + * + * This defaults to English and it's generally a bad idea to use anything else: + * if your language has different pluralization rules you'll end up with incorrect translations. + */ baseLanguage: 'en', + /** + * @ngdoc property + * @name gettextCatalog#currentLanguage + * @public + * @type {String} + * @description Active language. + */ currentLanguage: 'en', + /** + * @ngdoc property + * @name gettextCatalog#cache + * @public + * @type {String} en + * @description Language cache for lazy load + */ cache: $cacheFactory('strings'), + /** + * @ngdoc method + * @name gettextCatalog#setCurrentLanguage + * @public + * @param {String} lang language name + * @description Sets the current language and makes sure that all translations get updated correctly. + */ setCurrentLanguage: function (lang) { this.currentLanguage = lang; broadcastUpdated(); }, + /** + * @ngdoc method + * @name gettextCatalog#getCurrentLanguage + * @public + * @returns {String} current language + * @description Returns the current language. + */ getCurrentLanguage: function () { return this.currentLanguage; }, + /** + * @ngdoc method + * @name gettextCatalog#setStrings + * @public + * @param {String} language language name + * @param {Object.} strings set of strings where the key is the translation `key` and `value` is the translated text + * @description Processes an object of string definitions. {@link guide:manual-setstrings More details here}. + */ setStrings: function (language, strings) { if (!this.strings[language]) { this.strings[language] = {}; } + var defaultPlural = gettextPlurals(language, 1); for (var key in strings) { var val = strings[key]; @@ -84,7 +236,10 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", // Expand single strings for each context. for (var context in val) { var str = val[context]; - val[context] = angular.isArray(str) ? str : [str]; + if (!angular.isArray(str)) { + val[context] = []; + val[context][defaultPlural] = str; + } } this.strings[language][key] = val; } @@ -92,22 +247,73 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", broadcastUpdated(); }, - getStringForm: function (string, n, context) { - var stringTable = this.strings[this.currentLanguage] || {}; + /** + * @ngdoc method + * @name gettextCatalog#getStringFormFor + * @protected + * @param {String} language language name + * @param {String} string translation key + * @param {Number=} n number to build sting form for + * @param {String=} context translation key context, e.g. {@link doc:context Verb, Noun} + * @returns {String|Null} translated or annotated string or null if language is not set + * @description Translate a string with the given language, count and context. + */ + getStringFormFor: function (language, string, n, context) { + if (!language) { + return null; + } + var stringTable = this.strings[language] || {}; var contexts = stringTable[string] || {}; var plurals = contexts[context || noContext] || []; - return plurals[n]; + return plurals[gettextPlurals(language, n)]; }, + /** + * @ngdoc method + * @name gettextCatalog#getString + * @public + * @param {String} string translation key + * @param {$rootScope.Scope=} scope scope to do interpolation against + * @param {String=} context translation key context, e.g. {@link doc:context Verb, Noun} + * @returns {String} translated or annotated string + * @description Translate a string with the given scope and context. + * + * First it tries {@link gettextCatalog#currentLanguage gettextCatalog#currentLanguage} (e.g. `en-US`) then {@link gettextFallbackLanguage fallback} (e.g. `en`). + * + * When `scope` is supplied it uses Angular.JS interpolation, so something like this will do what you expect: + * ```js + * var hello = gettextCatalog.getString("Hello {{name}}!", { name: "Ruben" }); + * // var hello will be "Hallo Ruben!" in Dutch. + * ``` + * Avoid using scopes - this skips interpolation and is a lot faster. + */ getString: function (string, scope, context) { - string = this.getStringForm(string, 0, context) || prefixDebug(string); + var fallbackLanguage = gettextFallbackLanguage(this.currentLanguage); + string = this.getStringFormFor(this.currentLanguage, string, 1, context) || + this.getStringFormFor(fallbackLanguage, string, 1, context) || + prefixDebug(string); string = scope ? $interpolate(string)(scope) : string; return addTranslatedMarkers(string); }, + /** + * @ngdoc method + * @name gettextCatalog#getPlural + * @public + * @param {Number} n number to build sting form for + * @param {String} string translation key + * @param {String} stringPlural plural translation key + * @param {$rootScope.Scope=} scope scope to do interpolation against + * @param {String=} context translation key context, e.g. {@link doc:context Verb, Noun} + * @returns {String} translated or annotated string + * @see {@link gettextCatalog#getString gettextCatalog#getString} for details + * @description Translate a plural string with the given context. + */ getPlural: function (n, string, stringPlural, scope, context) { - var form = gettextPlurals(this.currentLanguage, n); - string = this.getStringForm(string, form, context) || prefixDebug(n === 1 ? string : stringPlural); + var fallbackLanguage = gettextFallbackLanguage(this.currentLanguage); + string = this.getStringFormFor(this.currentLanguage, string, n, context) || + this.getStringFormFor(fallbackLanguage, string, n, context) || + prefixDebug(n === 1 ? string : stringPlural); if (scope) { scope.$count = n; string = $interpolate(string)(scope); @@ -115,15 +321,27 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", return addTranslatedMarkers(string); }, + /** + * @ngdoc method + * @name gettextCatalog#loadRemote + * @public + * @param {String} url location of the translations + * @description Load a set of translation strings from a given URL. + * + * This should be a JSON catalog generated with [angular-gettext-tools](https://github.com/rubenv/angular-gettext-tools). + * {@link guide:lazy-loading More details here}. + */ loadRemote: function (url) { return $http({ method: 'GET', url: url, cache: catalog.cache - }).success(function (data) { + }).then(function (response) { + var data = response.data; for (var lang in data) { catalog.setStrings(lang, data[lang]); } + return response; }); } }; @@ -131,37 +349,105 @@ angular.module('gettext').factory('gettextCatalog', ["gettextPlurals", "$http", return catalog; }]); -angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$animate", "$compile", "$window", function (gettextCatalog, $parse, $animate, $compile, $window) { - // Trim polyfill for old browsers (instead of jQuery) - // Based on AngularJS-v1.2.2 (angular.js#620) - var trim = (function () { - if (!String.prototype.trim) { - return function (value) { - return (typeof value === 'string') ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; - }; - } - return function (value) { - return (typeof value === 'string') ? value.trim() : value; - }; - })(); +/** + * @ngdoc directive + * @module gettext + * @name translate + * @requires gettextCatalog + * @requires gettextUtil + * @requires https://docs.angularjs.org/api/ng/service/$parse $parse + * @requires https://docs.angularjs.org/api/ng/service/$animate $animate + * @requires https://docs.angularjs.org/api/ng/service/$compile $compile + * @requires https://docs.angularjs.org/api/ng/service/$window $window + * @restrict AE + * @param {String} [translatePlural] plural form + * @param {Number} translateN value to watch to substitute correct plural form + * @param {String} translateContext context value, e.g. {@link doc:context Verb, Noun} + * @description Annotates and translates text inside directive + * + * Full interpolation support is available in translated strings, so the following will work as expected: + * ```js + *
Hello {{name}}!
+ * ``` + * + * You can also use custom context parameters while interpolating. This approach allows usage + * of angular filters as well as custom logic inside your translated messages without unnecessary impact on translations. + * + * So for example when you have message like this: + * ```js + *
Last modified {{modificationDate | date:'yyyy-MM-dd HH:mm:ss Z'}} by {{author}}.
+ * ``` + * you will have it extracted in exact same version so it would look like this: + * `Last modified {{modificationDate | date:'yyyy-MM-dd HH:mm:ss Z'}} by {{author}}`. + * To start with it might be too complicated to read and handle by non technical translator. It's easy to make mistake + * when copying format for example. Secondly if you decide to change format by some point of the project translation will broke + * as it won't be the same string anymore. + * + * Instead your translator should only be concerned to place {{modificationDate}} correctly and you should have a free hand + * to modify implementation details on how to present the results. This is how you can achieve the goal: + * ```js + *
Last modified {{modificationDate}} by {{author}}.
+ * ``` + * + * There's a few more things worth to point out: + * 1. You can use as many parameters as you want. Each parameter begins with `translate-params-` followed by snake-case parameter name. + * Each parameter will be available for interpolation in camelCase manner (just like angular directive works by default). + * ```js + *
Param {{myCustomParam}} has been changed by {{name}}.
+ * ``` + * 2. You can rename your variables from current scope to simple ones if you like. + * ```js + *
Today's date is: {{date}}.
+ * ``` + * 3. You can use translate-params only for some interpolations. Rest would be treated as usual. + * ```js + *
This product: {{product}} costs {{cost}}.
+ * ``` + */ +angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$animate", "$compile", "$window", "gettextUtil", function (gettextCatalog, $parse, $animate, $compile, $window, gettextUtil) { + var msie = parseInt((/msie (\d+)/.exec(angular.lowercase($window.navigator.userAgent)) || [])[1], 10); + var PARAMS_PREFIX = 'translateParams'; - function assert(condition, missing, found) { - if (!condition) { - throw new Error('You should add a ' + missing + ' attribute whenever you add a ' + found + ' attribute.'); - } + function getCtxAttr(key) { + return gettextUtil.lcFirst(key.replace(PARAMS_PREFIX, '')); } - var msie = parseInt((/msie (\d+)/.exec(angular.lowercase($window.navigator.userAgent)) || [])[1], 10); + function handleInterpolationContext(scope, attrs, update) { + var attributes = Object.keys(attrs).filter(function (key) { + return gettextUtil.startsWith(key, PARAMS_PREFIX) && key !== PARAMS_PREFIX; + }); + + if (!attributes.length) { + return null; + } + + var interpolationContext = angular.extend({}, scope); + var unwatchers = []; + attributes.forEach(function (attribute) { + var unwatch = scope.$watch(attrs[attribute], function (newVal) { + var key = getCtxAttr(attribute); + interpolationContext[key] = newVal; + update(interpolationContext); + }); + unwatchers.push(unwatch); + }); + scope.$on('$destroy', function () { + unwatchers.forEach(function (unwatch) { + unwatch(); + }); + }); + return interpolationContext; + } return { restrict: 'AE', terminal: true, compile: function compile(element, attrs) { // Validate attributes - assert(!attrs.translatePlural || attrs.translateN, 'translate-n', 'translate-plural'); - assert(!attrs.translateN || attrs.translatePlural, 'translate-plural', 'translate-n'); + gettextUtil.assert(!attrs.translatePlural || attrs.translateN, 'translate-n', 'translate-plural'); + gettextUtil.assert(!attrs.translateN || attrs.translatePlural, 'translate-plural', 'translate-n'); - var msgid = trim(element.html()); + var msgid = gettextUtil.trim(element.html()); var translatePlural = attrs.translatePlural; var translateContext = attrs.translateContext; @@ -179,17 +465,18 @@ angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$ var pluralScope = null; var linking = true; - function update() { + function update(interpolationContext) { + interpolationContext = interpolationContext || null; + // Fetch correct translated string. var translated; if (translatePlural) { scope = pluralScope || (pluralScope = scope.$new()); scope.$count = countFn(scope); - translated = gettextCatalog.getPlural(scope.$count, msgid, translatePlural, null, translateContext); + translated = gettextCatalog.getPlural(scope.$count, msgid, translatePlural, interpolationContext, translateContext); } else { - translated = gettextCatalog.getString(msgid, null, translateContext); + translated = gettextCatalog.getString(msgid, interpolationContext, translateContext); } - var oldContents = element.contents(); if (oldContents.length === 0){ @@ -197,7 +484,7 @@ angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$ } // Avoid redundant swaps - if (translated === trim(oldContents.html())){ + if (translated === gettextUtil.trim(oldContents.html())){ // Take care of unlinked content if (linking){ $compile(oldContents)(scope); @@ -214,20 +501,86 @@ angular.module('gettext').directive('translate', ["gettextCatalog", "$parse", "$ $animate.leave(oldContents); } + var interpolationContext = handleInterpolationContext(scope, attrs, update); + update(interpolationContext); + linking = false; + if (attrs.translateN) { - scope.$watch(attrs.translateN, update); + scope.$watch(attrs.translateN, function () { + update(interpolationContext); + }); } - scope.$on('gettextLanguageChanged', update); + /** + * @ngdoc event + * @name translate#gettextLanguageChanged + * @eventType listen on scope + * @description Listens for language updates and changes translation accordingly + */ + scope.$on('gettextLanguageChanged', function () { + update(interpolationContext); + }); - update(); - linking = false; } }; } }; }]); +/** + * @ngdoc factory + * @module gettext + * @name gettextFallbackLanguage + * @param {String} langCode language code + * @returns {String|Null} fallback language + * @description Strips regional code and returns language code only + * + * Example + * ```js + * gettextFallbackLanguage('ru'); // "null" + * gettextFallbackLanguage('en_GB'); // "en" + * gettextFallbackLanguage(); // null + * ``` + */ +angular.module("gettext").factory("gettextFallbackLanguage", function () { + var cache = {}; + var pattern = /([^_]+)_[^_]+$/; + + return function (langCode) { + if (cache[langCode]) { + return cache[langCode]; + } + + var matches = pattern.exec(langCode); + if (matches) { + cache[langCode] = matches[1]; + return matches[1]; + } + + return null; + }; +}); +/** + * @ngdoc filter + * @module gettext + * @name translate + * @requires gettextCatalog + * @param {String} input translation key + * @param {String} context context to evaluate key against + * @returns {String} translated string or annotated key + * @see {@link doc:context Verb, Noun} + * @description Takes key and returns string + * + * Sometimes it's not an option to use an attribute (e.g. when you want to annotate an attribute value). + * There's a `translate` filter available for this purpose. + * + * ```html + * + * ``` + * This filter does not support plural strings. + * + * You may want to use {@link guide:custom-annotations custom annotations} to avoid using the `translate` filter all the time. * Is + */ angular.module('gettext').filter('translate', ["gettextCatalog", function (gettextCatalog) { function filter(input, context) { return gettextCatalog.getString(input, null, context); @@ -238,8 +591,12 @@ angular.module('gettext').filter('translate', ["gettextCatalog", function (gette // Do not edit this file, it is autogenerated using genplurals.py! angular.module("gettext").factory("gettextPlurals", function () { + var languageCodes = { + "pt_BR": "pt_BR", + "pt-BR": "pt_BR" + }; return function (langCode, n) { - switch (langCode) { + switch (getLanguageCode(langCode)) { case "ay": // Aymará case "bo": // Tibetan case "cgg": // Chiga @@ -348,5 +705,116 @@ angular.module("gettext").factory("gettextPlurals", function () { default: // Everything else return n != 1 ? 1 : 0; } + }; + + /** + * Method extracts iso639-2 language code from code with locale e.g. pl_PL, en_US, etc. + * If it's provided with standalone iso639-2 language code it simply returns it. + * @param {String} langCode + * @returns {String} iso639-2 language Code + */ + function getLanguageCode(langCode) { + if (!languageCodes[langCode]) { + languageCodes[langCode] = langCode.split(/\-|_/).shift(); + } + return languageCodes[langCode]; } }); + +/** + * @ngdoc factory + * @module gettext + * @name gettextUtil + * @description Utility service for common operations and polyfills. + */ +angular.module('gettext').factory('gettextUtil', function gettextUtil() { + /** + * @ngdoc method + * @name gettextUtil#trim + * @public + * @param {string} value String to be trimmed. + * @description Trim polyfill for old browsers (instead of jQuery). Based on AngularJS-v1.2.2 (angular.js#620). + * + * Example + * ```js + * gettextUtil.assert(' no blanks '); // "no blanks" + * ``` + */ + var trim = (function () { + if (!String.prototype.trim) { + return function (value) { + return (typeof value === 'string') ? value.replace(/^\s*/, '').replace(/\s*$/, '') : value; + }; + } + return function (value) { + return (typeof value === 'string') ? value.trim() : value; + }; + })(); + + /** + * @ngdoc method + * @name gettextUtil#assert + * @public + * @param {bool} condition condition to check + * @param {String} missing name of the directive missing attribute + * @param {String} found name of attribute that has been used with directive + * @description Throws error if condition is not met, which means that directive was used with certain parameter + * that requires another one (which is missing). + * + * Example + * ```js + * gettextUtil.assert(!attrs.translatePlural || attrs.translateN, 'translate-n', 'translate-plural'); + * //You should add a translate-n attribute whenever you add a translate-plural attribute. + * ``` + */ + function assert(condition, missing, found) { + if (!condition) { + throw new Error('You should add a ' + missing + ' attribute whenever you add a ' + found + ' attribute.'); + } + } + + /** + * @ngdoc method + * @name gettextUtil#startsWith + * @public + * @param {string} target String on which checking will occur. + * @param {string} query String expected to be at the beginning of target. + * @returns {boolean} Returns true if object has no ownProperties. For arrays returns true if length == 0. + * @description Checks if string starts with another string. + * + * Example + * ```js + * gettextUtil.startsWith('Home sweet home.', 'Home'); //true + * gettextUtil.startsWith('Home sweet home.', 'sweet'); //false + * ``` + */ + function startsWith(target, query) { + return target.indexOf(query) === 0; + } + + /** + * @ngdoc method + * @name gettextUtil#lcFirst + * @public + * @param {string} target String to transform. + * @returns {string} Strings beginning with lowercase letter. + * @description Makes first letter of the string lower case + * + * Example + * ```js + * gettextUtil.lcFirst('Home Sweet Home.'); //'home Sweet Home' + * gettextUtil.lcFirst('ShouldBeCamelCase.'); //'shouldBeCamelCase' + * ``` + */ + function lcFirst(target) { + var first = target.charAt(0).toLowerCase(); + return first + target.substr(1); + } + + return { + trim: trim, + assert: assert, + startsWith: startsWith, + lcFirst: lcFirst + }; +});