├── src ├── assets │ ├── .gitkeep │ └── i18n │ │ ├── en.json │ │ └── de.json ├── favicon.ico ├── environments │ ├── environment.prod.ts │ └── environment.ts ├── styles.scss ├── app │ ├── shared │ │ ├── payment-detail.model.ts │ │ └── payment-detail.service.ts │ ├── app.component.html │ ├── app-routing.module.ts │ ├── payment-details │ │ ├── payment-details.component.ts │ │ ├── payment-details.component.html │ │ ├── payment-detail-list │ │ │ ├── payment-detail-list.component.html │ │ │ └── payment-detail-list.component.ts │ │ └── payment-detail │ │ │ ├── payment-detail.component.ts │ │ │ └── payment-detail.component.html │ ├── app.component.scss │ ├── app.component.ts │ ├── app.component.spec.ts │ └── app.module.ts ├── tsconfig.app.json ├── messages.xlf ├── tsconfig.spec.json ├── tslint.json ├── main.ts ├── browserslist ├── styles.css ├── test.ts ├── karma.conf.js ├── index.html └── polyfills.ts ├── bower_components ├── angular │ ├── index.js │ ├── angular.min.js.gzip │ ├── bower.json │ ├── angular-csp.css │ ├── .bower.json │ ├── package.json │ ├── LICENSE.md │ └── README.md └── angular-translate │ ├── bower.json │ ├── README.md │ ├── .bower.json │ ├── angular-translate.min.js │ └── angular-translate.js ├── e2e ├── src │ ├── app.po.ts │ └── app.e2e-spec.ts ├── tsconfig.e2e.json └── protractor.conf.js ├── .editorconfig ├── appStructure.txt ├── tsconfig.json ├── .gitignore ├── README.md ├── package.json ├── tslint.json ├── angular.json └── translations.babel /src/assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /bower_components/angular/index.js: -------------------------------------------------------------------------------- 1 | require('./angular'); 2 | module.exports = angular; 3 | -------------------------------------------------------------------------------- /src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirzaf/payment-registration/HEAD/src/favicon.ico -------------------------------------------------------------------------------- /src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /src/styles.scss: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /bower_components/angular/angular.min.js.gzip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirzaf/payment-registration/HEAD/bower_components/angular/angular.min.js.gzip -------------------------------------------------------------------------------- /src/assets/i18n/en.json: -------------------------------------------------------------------------------- 1 | { 2 | "demo.title" : "Payment Detail Register", 3 | "demo.card-holder-name": "Card Holder Name", 4 | "demo.card-number": "Sixteen digit Card Number" 5 | } 6 | -------------------------------------------------------------------------------- /src/assets/i18n/de.json: -------------------------------------------------------------------------------- 1 | { 2 | "demo.title" : "Betalings detalj register", 3 | "demo.card-holder-name":"Übersetzungs noma", 4 | "demo.card-number": "Verwendung von Übersetzungen" 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/app/shared/payment-detail.model.ts: -------------------------------------------------------------------------------- 1 | export class PaymentDetail { 2 | PMId :number; 3 | CardOwnerName: string; 4 | CardNumber: string; 5 | ExpirationDate: string; 6 | CVV: string; 7 | } 8 | -------------------------------------------------------------------------------- /bower_components/angular/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.7.8", 4 | "license": "MIT", 5 | "main": "./angular.js", 6 | "ignore": [], 7 | "dependencies": { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
| {{pd.CardOwnerName}} | 4 |{{pd.CardNumber}} | 5 |{{pd.ExpirationDate}} | 6 |7 | 8 | | 9 |
633 | * // register translation table for language: 'de_DE'
634 | * $translateProvider.translations('de_DE', {
635 | * 'GREETING': 'Hallo Welt!'
636 | * });
637 | *
638 | * // register another one
639 | * $translateProvider.translations('en_US', {
640 | * 'GREETING': 'Hello world!'
641 | * });
642 | *
643 | *
644 | * When registering multiple translation tables for for the same language key,
645 | * the actual translation table gets extended. This allows you to define module
646 | * specific translation which only get added, once a specific module is loaded in
647 | * your app.
648 | *
649 | * Invoking this method with no arguments returns the translation table which was
650 | * registered with no language key. Invoking it with a language key returns the
651 | * related translation table.
652 | *
653 | * @param {string} langKey A language key.
654 | * @param {object} translationTable A plain old JavaScript object that represents a translation table.
655 | *
656 | */
657 | var translations = function (langKey, translationTable) {
658 |
659 | if (!langKey && !translationTable) {
660 | return $translationTable;
661 | }
662 |
663 | if (langKey && !translationTable) {
664 | if (angular.isString(langKey)) {
665 | return $translationTable[langKey];
666 | }
667 | } else {
668 | if (!angular.isObject($translationTable[langKey])) {
669 | $translationTable[langKey] = {};
670 | }
671 | angular.extend($translationTable[langKey], flatObject(translationTable));
672 | }
673 | return this;
674 | };
675 |
676 | this.translations = translations;
677 |
678 | /**
679 | * @ngdoc function
680 | * @name pascalprecht.translate.$translateProvider#cloakClassName
681 | * @methodOf pascalprecht.translate.$translateProvider
682 | *
683 | * @description
684 | *
685 | * Let's you change the class name for `translate-cloak` directive.
686 | * Default class name is `translate-cloak`.
687 | *
688 | * @param {string} name translate-cloak class name
689 | */
690 | this.cloakClassName = function (name) {
691 | if (!name) {
692 | return $cloakClassName;
693 | }
694 | $cloakClassName = name;
695 | return this;
696 | };
697 |
698 | /**
699 | * @ngdoc function
700 | * @name pascalprecht.translate.$translateProvider#nestedObjectDelimeter
701 | * @methodOf pascalprecht.translate.$translateProvider
702 | *
703 | * @description
704 | *
705 | * Let's you change the delimiter for namespaced translations.
706 | * Default delimiter is `.`.
707 | *
708 | * @param {string} delimiter namespace separator
709 | */
710 | this.nestedObjectDelimeter = function (delimiter) {
711 | if (!delimiter) {
712 | return $nestedObjectDelimeter;
713 | }
714 | $nestedObjectDelimeter = delimiter;
715 | return this;
716 | };
717 |
718 | /**
719 | * @name flatObject
720 | * @private
721 | *
722 | * @description
723 | * Flats an object. This function is used to flatten given translation data with
724 | * namespaces, so they are later accessible via dot notation.
725 | */
726 | var flatObject = function (data, path, result, prevKey) {
727 | var key, keyWithPath, keyWithShortPath, val;
728 |
729 | if (!path) {
730 | path = [];
731 | }
732 | if (!result) {
733 | result = {};
734 | }
735 | for (key in data) {
736 | if (!Object.prototype.hasOwnProperty.call(data, key)) {
737 | continue;
738 | }
739 | val = data[key];
740 | if (angular.isObject(val)) {
741 | flatObject(val, path.concat(key), result, key);
742 | } else {
743 | keyWithPath = path.length ? ('' + path.join($nestedObjectDelimeter) + $nestedObjectDelimeter + key) : key;
744 | if (path.length && key === prevKey) {
745 | // Create shortcut path (foo.bar == foo.bar.bar)
746 | keyWithShortPath = '' + path.join($nestedObjectDelimeter);
747 | // Link it to original path
748 | result[keyWithShortPath] = '@:' + keyWithPath;
749 | }
750 | result[keyWithPath] = val;
751 | }
752 | }
753 | return result;
754 | };
755 | flatObject.displayName = 'flatObject';
756 |
757 | /**
758 | * @ngdoc function
759 | * @name pascalprecht.translate.$translateProvider#addInterpolation
760 | * @methodOf pascalprecht.translate.$translateProvider
761 | *
762 | * @description
763 | * Adds interpolation services to angular-translate, so it can manage them.
764 | *
765 | * @param {object} factory Interpolation service factory
766 | */
767 | this.addInterpolation = function (factory) {
768 | $interpolatorFactories.push(factory);
769 | return this;
770 | };
771 |
772 | /**
773 | * @ngdoc function
774 | * @name pascalprecht.translate.$translateProvider#useMessageFormatInterpolation
775 | * @methodOf pascalprecht.translate.$translateProvider
776 | *
777 | * @description
778 | * Tells angular-translate to use interpolation functionality of messageformat.js.
779 | * This is useful when having high level pluralization and gender selection.
780 | */
781 | this.useMessageFormatInterpolation = function () {
782 | return this.useInterpolation('$translateMessageFormatInterpolation');
783 | };
784 |
785 | /**
786 | * @ngdoc function
787 | * @name pascalprecht.translate.$translateProvider#useInterpolation
788 | * @methodOf pascalprecht.translate.$translateProvider
789 | *
790 | * @description
791 | * Tells angular-translate which interpolation style to use as default, application-wide.
792 | * Simply pass a factory/service name. The interpolation service has to implement
793 | * the correct interface.
794 | *
795 | * @param {string} factory Interpolation service name.
796 | */
797 | this.useInterpolation = function (factory) {
798 | $interpolationFactory = factory;
799 | return this;
800 | };
801 |
802 | /**
803 | * @ngdoc function
804 | * @name pascalprecht.translate.$translateProvider#useSanitizeStrategy
805 | * @methodOf pascalprecht.translate.$translateProvider
806 | *
807 | * @description
808 | * Simply sets a sanitation strategy type.
809 | *
810 | * @param {string} value Strategy type.
811 | */
812 | this.useSanitizeValueStrategy = function (value) {
813 | $translateSanitizationProvider.useStrategy(value);
814 | return this;
815 | };
816 |
817 | /**
818 | * @ngdoc function
819 | * @name pascalprecht.translate.$translateProvider#preferredLanguage
820 | * @methodOf pascalprecht.translate.$translateProvider
821 | *
822 | * @description
823 | * Tells the module which of the registered translation tables to use for translation
824 | * at initial startup by passing a language key. Similar to `$translateProvider#use`
825 | * only that it says which language to **prefer**.
826 | * It is recommended to call this after {@link pascalprecht.translate.$translate#fallbackLanguage fallbackLanguage()}.
827 | *
828 | * @param {string} langKey A language key.
829 | */
830 | this.preferredLanguage = function (langKey) {
831 | if (langKey) {
832 | setupPreferredLanguage(langKey);
833 | return this;
834 | }
835 | return $preferredLanguage;
836 | };
837 | var setupPreferredLanguage = function (langKey) {
838 | if (langKey) {
839 | $preferredLanguage = langKey;
840 | }
841 | return $preferredLanguage;
842 | };
843 | /**
844 | * @ngdoc function
845 | * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicator
846 | * @methodOf pascalprecht.translate.$translateProvider
847 | *
848 | * @description
849 | * Sets an indicator which is used when a translation isn't found. E.g. when
850 | * setting the indicator as 'X' and one tries to translate a translation id
851 | * called `NOT_FOUND`, this will result in `X NOT_FOUND X`.
852 | *
853 | * Internally this methods sets a left indicator and a right indicator using
854 | * `$translateProvider.translationNotFoundIndicatorLeft()` and
855 | * `$translateProvider.translationNotFoundIndicatorRight()`.
856 | *
857 | * **Note**: These methods automatically add a whitespace between the indicators
858 | * and the translation id.
859 | *
860 | * @param {string} indicator An indicator, could be any string.
861 | */
862 | this.translationNotFoundIndicator = function (indicator) {
863 | this.translationNotFoundIndicatorLeft(indicator);
864 | this.translationNotFoundIndicatorRight(indicator);
865 | return this;
866 | };
867 |
868 | /**
869 | * ngdoc function
870 | * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
871 | * @methodOf pascalprecht.translate.$translateProvider
872 | *
873 | * @description
874 | * Sets an indicator which is used when a translation isn't found left to the
875 | * translation id.
876 | *
877 | * @param {string} indicator An indicator.
878 | */
879 | this.translationNotFoundIndicatorLeft = function (indicator) {
880 | if (!indicator) {
881 | return $notFoundIndicatorLeft;
882 | }
883 | $notFoundIndicatorLeft = indicator;
884 | return this;
885 | };
886 |
887 | /**
888 | * ngdoc function
889 | * @name pascalprecht.translate.$translateProvider#translationNotFoundIndicatorLeft
890 | * @methodOf pascalprecht.translate.$translateProvider
891 | *
892 | * @description
893 | * Sets an indicator which is used when a translation isn't found right to the
894 | * translation id.
895 | *
896 | * @param {string} indicator An indicator.
897 | */
898 | this.translationNotFoundIndicatorRight = function (indicator) {
899 | if (!indicator) {
900 | return $notFoundIndicatorRight;
901 | }
902 | $notFoundIndicatorRight = indicator;
903 | return this;
904 | };
905 |
906 | /**
907 | * @ngdoc function
908 | * @name pascalprecht.translate.$translateProvider#fallbackLanguage
909 | * @methodOf pascalprecht.translate.$translateProvider
910 | *
911 | * @description
912 | * Tells the module which of the registered translation tables to use when missing translations
913 | * at initial startup by passing a language key. Similar to `$translateProvider#use`
914 | * only that it says which language to **fallback**.
915 | *
916 | * @param {string||array} langKey A language key.
917 | *
918 | */
919 | this.fallbackLanguage = function (langKey) {
920 | fallbackStack(langKey);
921 | return this;
922 | };
923 |
924 | var fallbackStack = function (langKey) {
925 | if (langKey) {
926 | if (angular.isString(langKey)) {
927 | $fallbackWasString = true;
928 | $fallbackLanguage = [langKey];
929 | } else if (angular.isArray(langKey)) {
930 | $fallbackWasString = false;
931 | $fallbackLanguage = langKey;
932 | }
933 | if (angular.isString($preferredLanguage) && indexOf($fallbackLanguage, $preferredLanguage) < 0) {
934 | $fallbackLanguage.push($preferredLanguage);
935 | }
936 |
937 | return this;
938 | } else {
939 | if ($fallbackWasString) {
940 | return $fallbackLanguage[0];
941 | } else {
942 | return $fallbackLanguage;
943 | }
944 | }
945 | };
946 |
947 | /**
948 | * @ngdoc function
949 | * @name pascalprecht.translate.$translateProvider#use
950 | * @methodOf pascalprecht.translate.$translateProvider
951 | *
952 | * @description
953 | * Set which translation table to use for translation by given language key. When
954 | * trying to 'use' a language which isn't provided, it'll throw an error.
955 | *
956 | * You actually don't have to use this method since `$translateProvider#preferredLanguage`
957 | * does the job too.
958 | *
959 | * @param {string} langKey A language key.
960 | */
961 | this.use = function (langKey) {
962 | if (langKey) {
963 | if (!$translationTable[langKey] && (!$loaderFactory)) {
964 | // only throw an error, when not loading translation data asynchronously
965 | throw new Error('$translateProvider couldn\'t find translationTable for langKey: \'' + langKey + '\'');
966 | }
967 | $uses = langKey;
968 | return this;
969 | }
970 | return $uses;
971 | };
972 |
973 | /**
974 | * @ngdoc function
975 | * @name pascalprecht.translate.$translateProvider#resolveClientLocale
976 | * @methodOf pascalprecht.translate.$translateProvider
977 | *
978 | * @description
979 | * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver.
980 | *
981 | * @returns {string} the current client/browser language key
982 | */
983 | this.resolveClientLocale = function () {
984 | return getLocale();
985 | };
986 |
987 | /**
988 | * @ngdoc function
989 | * @name pascalprecht.translate.$translateProvider#storageKey
990 | * @methodOf pascalprecht.translate.$translateProvider
991 | *
992 | * @description
993 | * Tells the module which key must represent the choosed language by a user in the storage.
994 | *
995 | * @param {string} key A key for the storage.
996 | */
997 | var storageKey = function (key) {
998 | if (!key) {
999 | if ($storagePrefix) {
1000 | return $storagePrefix + $storageKey;
1001 | }
1002 | return $storageKey;
1003 | }
1004 | $storageKey = key;
1005 | return this;
1006 | };
1007 |
1008 | this.storageKey = storageKey;
1009 |
1010 | /**
1011 | * @ngdoc function
1012 | * @name pascalprecht.translate.$translateProvider#useUrlLoader
1013 | * @methodOf pascalprecht.translate.$translateProvider
1014 | *
1015 | * @description
1016 | * Tells angular-translate to use `$translateUrlLoader` extension service as loader.
1017 | *
1018 | * @param {string} url Url
1019 | * @param {Object=} options Optional configuration object
1020 | */
1021 | this.useUrlLoader = function (url, options) {
1022 | return this.useLoader('$translateUrlLoader', angular.extend({url : url}, options));
1023 | };
1024 |
1025 | /**
1026 | * @ngdoc function
1027 | * @name pascalprecht.translate.$translateProvider#useStaticFilesLoader
1028 | * @methodOf pascalprecht.translate.$translateProvider
1029 | *
1030 | * @description
1031 | * Tells angular-translate to use `$translateStaticFilesLoader` extension service as loader.
1032 | *
1033 | * @param {Object=} options Optional configuration object
1034 | */
1035 | this.useStaticFilesLoader = function (options) {
1036 | return this.useLoader('$translateStaticFilesLoader', options);
1037 | };
1038 |
1039 | /**
1040 | * @ngdoc function
1041 | * @name pascalprecht.translate.$translateProvider#useLoader
1042 | * @methodOf pascalprecht.translate.$translateProvider
1043 | *
1044 | * @description
1045 | * Tells angular-translate to use any other service as loader.
1046 | *
1047 | * @param {string} loaderFactory Factory name to use
1048 | * @param {Object=} options Optional configuration object
1049 | */
1050 | this.useLoader = function (loaderFactory, options) {
1051 | $loaderFactory = loaderFactory;
1052 | $loaderOptions = options || {};
1053 | return this;
1054 | };
1055 |
1056 | /**
1057 | * @ngdoc function
1058 | * @name pascalprecht.translate.$translateProvider#useLocalStorage
1059 | * @methodOf pascalprecht.translate.$translateProvider
1060 | *
1061 | * @description
1062 | * Tells angular-translate to use `$translateLocalStorage` service as storage layer.
1063 | *
1064 | */
1065 | this.useLocalStorage = function () {
1066 | return this.useStorage('$translateLocalStorage');
1067 | };
1068 |
1069 | /**
1070 | * @ngdoc function
1071 | * @name pascalprecht.translate.$translateProvider#useCookieStorage
1072 | * @methodOf pascalprecht.translate.$translateProvider
1073 | *
1074 | * @description
1075 | * Tells angular-translate to use `$translateCookieStorage` service as storage layer.
1076 | */
1077 | this.useCookieStorage = function () {
1078 | return this.useStorage('$translateCookieStorage');
1079 | };
1080 |
1081 | /**
1082 | * @ngdoc function
1083 | * @name pascalprecht.translate.$translateProvider#useStorage
1084 | * @methodOf pascalprecht.translate.$translateProvider
1085 | *
1086 | * @description
1087 | * Tells angular-translate to use custom service as storage layer.
1088 | */
1089 | this.useStorage = function (storageFactory) {
1090 | $storageFactory = storageFactory;
1091 | return this;
1092 | };
1093 |
1094 | /**
1095 | * @ngdoc function
1096 | * @name pascalprecht.translate.$translateProvider#storagePrefix
1097 | * @methodOf pascalprecht.translate.$translateProvider
1098 | *
1099 | * @description
1100 | * Sets prefix for storage key.
1101 | *
1102 | * @param {string} prefix Storage key prefix
1103 | */
1104 | this.storagePrefix = function (prefix) {
1105 | if (!prefix) {
1106 | return prefix;
1107 | }
1108 | $storagePrefix = prefix;
1109 | return this;
1110 | };
1111 |
1112 | /**
1113 | * @ngdoc function
1114 | * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandlerLog
1115 | * @methodOf pascalprecht.translate.$translateProvider
1116 | *
1117 | * @description
1118 | * Tells angular-translate to use built-in log handler when trying to translate
1119 | * a translation Id which doesn't exist.
1120 | *
1121 | * This is actually a shortcut method for `useMissingTranslationHandler()`.
1122 | *
1123 | */
1124 | this.useMissingTranslationHandlerLog = function () {
1125 | return this.useMissingTranslationHandler('$translateMissingTranslationHandlerLog');
1126 | };
1127 |
1128 | /**
1129 | * @ngdoc function
1130 | * @name pascalprecht.translate.$translateProvider#useMissingTranslationHandler
1131 | * @methodOf pascalprecht.translate.$translateProvider
1132 | *
1133 | * @description
1134 | * Expects a factory name which later gets instantiated with `$injector`.
1135 | * This method can be used to tell angular-translate to use a custom
1136 | * missingTranslationHandler. Just build a factory which returns a function
1137 | * and expects a translation id as argument.
1138 | *
1139 | * Example:
1140 | *
1141 | * app.config(function ($translateProvider) {
1142 | * $translateProvider.useMissingTranslationHandler('customHandler');
1143 | * });
1144 | *
1145 | * app.factory('customHandler', function (dep1, dep2) {
1146 | * return function (translationId) {
1147 | * // something with translationId and dep1 and dep2
1148 | * };
1149 | * });
1150 | *
1151 | *
1152 | * @param {string} factory Factory name
1153 | */
1154 | this.useMissingTranslationHandler = function (factory) {
1155 | $missingTranslationHandlerFactory = factory;
1156 | return this;
1157 | };
1158 |
1159 | /**
1160 | * @ngdoc function
1161 | * @name pascalprecht.translate.$translateProvider#usePostCompiling
1162 | * @methodOf pascalprecht.translate.$translateProvider
1163 | *
1164 | * @description
1165 | * If post compiling is enabled, all translated values will be processed
1166 | * again with AngularJS' $compile.
1167 | *
1168 | * Example:
1169 | *
1170 | * app.config(function ($translateProvider) {
1171 | * $translateProvider.usePostCompiling(true);
1172 | * });
1173 | *
1174 | *
1175 | * @param {string} factory Factory name
1176 | */
1177 | this.usePostCompiling = function (value) {
1178 | $postCompilingEnabled = !(!value);
1179 | return this;
1180 | };
1181 |
1182 | /**
1183 | * @ngdoc function
1184 | * @name pascalprecht.translate.$translateProvider#forceAsyncReload
1185 | * @methodOf pascalprecht.translate.$translateProvider
1186 | *
1187 | * @description
1188 | * If force async reload is enabled, async loader will always be called
1189 | * even if $translationTable already contains the language key, adding
1190 | * possible new entries to the $translationTable.
1191 | *
1192 | * Example:
1193 | *
1194 | * app.config(function ($translateProvider) {
1195 | * $translateProvider.forceAsyncReload(true);
1196 | * });
1197 | *
1198 | *
1199 | * @param {boolean} value - valid values are true or false
1200 | */
1201 | this.forceAsyncReload = function (value) {
1202 | $forceAsyncReloadEnabled = !(!value);
1203 | return this;
1204 | };
1205 |
1206 | /**
1207 | * @ngdoc function
1208 | * @name pascalprecht.translate.$translateProvider#uniformLanguageTag
1209 | * @methodOf pascalprecht.translate.$translateProvider
1210 | *
1211 | * @description
1212 | * Tells angular-translate which language tag should be used as a result when determining
1213 | * the current browser language.
1214 | *
1215 | * This setting must be set before invoking {@link pascalprecht.translate.$translateProvider#methods_determinePreferredLanguage determinePreferredLanguage()}.
1216 | *
1217 | *
1218 | * $translateProvider
1219 | * .uniformLanguageTag('bcp47')
1220 | * .determinePreferredLanguage()
1221 | *
1222 | *
1223 | * The resolver currently supports:
1224 | * * default
1225 | * (traditionally: hyphens will be converted into underscores, i.e. en-US => en_US)
1226 | * en-US => en_US
1227 | * en_US => en_US
1228 | * en-us => en_us
1229 | * * java
1230 | * like default, but the second part will be always in uppercase
1231 | * en-US => en_US
1232 | * en_US => en_US
1233 | * en-us => en_US
1234 | * * BCP 47 (RFC 4646 & 4647)
1235 | * EN => en
1236 | * en-US => en-US
1237 | * en_US => en-US
1238 | * en-us => en-US
1239 | * sr-latn => sr-Latn
1240 | * sr-latn-rs => sr-Latn-RS
1241 | *
1242 | * See also:
1243 | * * http://en.wikipedia.org/wiki/IETF_language_tag
1244 | * * http://www.w3.org/International/core/langtags/
1245 | * * http://tools.ietf.org/html/bcp47
1246 | *
1247 | * @param {string|object} options - options (or standard)
1248 | * @param {string} options.standard - valid values are 'default', 'bcp47', 'java'
1249 | */
1250 | this.uniformLanguageTag = function (options) {
1251 |
1252 | if (!options) {
1253 | options = {};
1254 | } else if (angular.isString(options)) {
1255 | options = {
1256 | standard : options
1257 | };
1258 | }
1259 |
1260 | uniformLanguageTagResolver = options.standard;
1261 |
1262 | return this;
1263 | };
1264 |
1265 | /**
1266 | * @ngdoc function
1267 | * @name pascalprecht.translate.$translateProvider#determinePreferredLanguage
1268 | * @methodOf pascalprecht.translate.$translateProvider
1269 | *
1270 | * @description
1271 | * Tells angular-translate to try to determine on its own which language key
1272 | * to set as preferred language. When `fn` is given, angular-translate uses it
1273 | * to determine a language key, otherwise it uses the built-in `getLocale()`
1274 | * method.
1275 | *
1276 | * The `getLocale()` returns a language key in the format `[lang]_[country]` or
1277 | * `[lang]` depending on what the browser provides.
1278 | *
1279 | * Use this method at your own risk, since not all browsers return a valid
1280 | * locale (see {@link pascalprecht.translate.$translateProvider#methods_uniformLanguageTag uniformLanguageTag()}).
1281 | *
1282 | * @param {Function=} fn Function to determine a browser's locale
1283 | */
1284 | this.determinePreferredLanguage = function (fn) {
1285 |
1286 | var locale = (fn && angular.isFunction(fn)) ? fn() : getLocale();
1287 |
1288 | if (!$availableLanguageKeys.length) {
1289 | $preferredLanguage = locale;
1290 | } else {
1291 | $preferredLanguage = negotiateLocale(locale) || locale;
1292 | }
1293 |
1294 | return this;
1295 | };
1296 |
1297 | /**
1298 | * @ngdoc function
1299 | * @name pascalprecht.translate.$translateProvider#registerAvailableLanguageKeys
1300 | * @methodOf pascalprecht.translate.$translateProvider
1301 | *
1302 | * @description
1303 | * Registers a set of language keys the app will work with. Use this method in
1304 | * combination with
1305 | * {@link pascalprecht.translate.$translateProvider#determinePreferredLanguage determinePreferredLanguage}.
1306 | * When available languages keys are registered, angular-translate
1307 | * tries to find the best fitting language key depending on the browsers locale,
1308 | * considering your language key convention.
1309 | *
1310 | * @param {object} languageKeys Array of language keys the your app will use
1311 | * @param {object=} aliases Alias map.
1312 | */
1313 | this.registerAvailableLanguageKeys = function (languageKeys, aliases) {
1314 | if (languageKeys) {
1315 | $availableLanguageKeys = languageKeys;
1316 | if (aliases) {
1317 | $languageKeyAliases = aliases;
1318 | }
1319 | return this;
1320 | }
1321 | return $availableLanguageKeys;
1322 | };
1323 |
1324 | /**
1325 | * @ngdoc function
1326 | * @name pascalprecht.translate.$translateProvider#useLoaderCache
1327 | * @methodOf pascalprecht.translate.$translateProvider
1328 | *
1329 | * @description
1330 | * Registers a cache for internal $http based loaders.
1331 | * {@link pascalprecht.translate.$translationCache $translationCache}.
1332 | * When false the cache will be disabled (default). When true or undefined
1333 | * the cache will be a default (see $cacheFactory). When an object it will
1334 | * be treat as a cache object itself: the usage is $http({cache: cache})
1335 | *
1336 | * @param {object} cache boolean, string or cache-object
1337 | */
1338 | this.useLoaderCache = function (cache) {
1339 | if (cache === false) {
1340 | // disable cache
1341 | loaderCache = undefined;
1342 | } else if (cache === true) {
1343 | // enable cache using AJS defaults
1344 | loaderCache = true;
1345 | } else if (typeof(cache) === 'undefined') {
1346 | // enable cache using default
1347 | loaderCache = '$translationCache';
1348 | } else if (cache) {
1349 | // enable cache using given one (see $cacheFactory)
1350 | loaderCache = cache;
1351 | }
1352 | return this;
1353 | };
1354 |
1355 | /**
1356 | * @ngdoc function
1357 | * @name pascalprecht.translate.$translateProvider#directivePriority
1358 | * @methodOf pascalprecht.translate.$translateProvider
1359 | *
1360 | * @description
1361 | * Sets the default priority of the translate directive. The standard value is `0`.
1362 | * Calling this function without an argument will return the current value.
1363 | *
1364 | * @param {number} priority for the translate-directive
1365 | */
1366 | this.directivePriority = function (priority) {
1367 | if (priority === undefined) {
1368 | // getter
1369 | return directivePriority;
1370 | } else {
1371 | // setter with chaining
1372 | directivePriority = priority;
1373 | return this;
1374 | }
1375 | };
1376 |
1377 | /**
1378 | * @ngdoc function
1379 | * @name pascalprecht.translate.$translateProvider#statefulFilter
1380 | * @methodOf pascalprecht.translate.$translateProvider
1381 | *
1382 | * @description
1383 | * Since AngularJS 1.3, filters which are not stateless (depending at the scope)
1384 | * have to explicit define this behavior.
1385 | * Sets whether the translate filter should be stateful or stateless. The standard value is `true`
1386 | * meaning being stateful.
1387 | * Calling this function without an argument will return the current value.
1388 | *
1389 | * @param {boolean} state - defines the state of the filter
1390 | */
1391 | this.statefulFilter = function (state) {
1392 | if (state === undefined) {
1393 | // getter
1394 | return statefulFilter;
1395 | } else {
1396 | // setter with chaining
1397 | statefulFilter = state;
1398 | return this;
1399 | }
1400 | };
1401 |
1402 | /**
1403 | * @ngdoc function
1404 | * @name pascalprecht.translate.$translateProvider#postProcess
1405 | * @methodOf pascalprecht.translate.$translateProvider
1406 | *
1407 | * @description
1408 | * The post processor will be intercept right after the translation result. It can modify the result.
1409 | *
1410 | * @param {object} fn Function or service name (string) to be called after the translation value has been set / resolved. The function itself will enrich every value being processed and then continue the normal resolver process
1411 | */
1412 | this.postProcess = function (fn) {
1413 | if (fn) {
1414 | postProcessFn = fn;
1415 | } else {
1416 | postProcessFn = undefined;
1417 | }
1418 | return this;
1419 | };
1420 |
1421 | /**
1422 | * @ngdoc function
1423 | * @name pascalprecht.translate.$translateProvider#keepContent
1424 | * @methodOf pascalprecht.translate.$translateProvider
1425 | *
1426 | * @description
1427 | * If keepContent is set to true than translate directive will always use innerHTML
1428 | * as a default translation
1429 | *
1430 | * Example:
1431 | *
1432 | * app.config(function ($translateProvider) {
1433 | * $translateProvider.keepContent(true);
1434 | * });
1435 | *
1436 | *
1437 | * @param {boolean} value - valid values are true or false
1438 | */
1439 | this.keepContent = function (value) {
1440 | $keepContent = !(!value);
1441 | return this;
1442 | };
1443 |
1444 | /**
1445 | * @ngdoc object
1446 | * @name pascalprecht.translate.$translate
1447 | * @requires $interpolate
1448 | * @requires $log
1449 | * @requires $rootScope
1450 | * @requires $q
1451 | *
1452 | * @description
1453 | * The `$translate` service is the actual core of angular-translate. It expects a translation id
1454 | * and optional interpolate parameters to translate contents.
1455 | *
1456 | *
1457 | * $translate('HEADLINE_TEXT').then(function (translation) {
1458 | * $scope.translatedText = translation;
1459 | * });
1460 | *
1461 | *
1462 | * @param {string|array} translationId A token which represents a translation id
1463 | * This can be optionally an array of translation ids which
1464 | * results that the function returns an object where each key
1465 | * is the translation id and the value the translation.
1466 | * @param {object=} [interpolateParams={}] An object hash for dynamic values
1467 | * @param {string=} [interpolationId=undefined] The id of the interpolation to use (use default unless set via useInterpolation())
1468 | * @param {string=} [defaultTranslationText=undefined] the optional default translation text that is written as
1469 | * as default text in case it is not found in any configured language
1470 | * @param {string=} [forceLanguage=false] A language to be used instead of the current language
1471 | * @param {string=} [sanitizeStrategy=undefined] force sanitize strategy for this call instead of using the configured one (use default unless set)
1472 | * @returns {object} promise
1473 | */
1474 | this.$get = ['$log', '$injector', '$rootScope', '$q', function ($log, $injector, $rootScope, $q) {
1475 |
1476 | var Storage,
1477 | defaultInterpolator = $injector.get($interpolationFactory || '$translateDefaultInterpolation'),
1478 | pendingLoader = false,
1479 | interpolatorHashMap = {},
1480 | langPromises = {},
1481 | fallbackIndex,
1482 | startFallbackIteration;
1483 |
1484 | var $translate = function (translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage, sanitizeStrategy) {
1485 | if (!$uses && $preferredLanguage) {
1486 | $uses = $preferredLanguage;
1487 | }
1488 | var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses
1489 | (negotiateLocale(forceLanguage) || forceLanguage) : $uses;
1490 |
1491 | // Check forceLanguage is present
1492 | if (forceLanguage) {
1493 | loadTranslationsIfMissing(forceLanguage);
1494 | }
1495 |
1496 | // Duck detection: If the first argument is an array, a bunch of translations was requested.
1497 | // The result is an object.
1498 | if (angular.isArray(translationId)) {
1499 | // Inspired by Q.allSettled by Kris Kowal
1500 | // https://github.com/kriskowal/q/blob/b0fa72980717dc202ffc3cbf03b936e10ebbb9d7/q.js#L1553-1563
1501 | // This transforms all promises regardless resolved or rejected
1502 | var translateAll = function (translationIds) {
1503 | var results = {}; // storing the actual results
1504 | var promises = []; // promises to wait for
1505 | // Wraps the promise a) being always resolved and b) storing the link id->value
1506 | var translate = function (translationId) {
1507 | var deferred = $q.defer();
1508 | var regardless = function (value) {
1509 | results[translationId] = value;
1510 | deferred.resolve([translationId, value]);
1511 | };
1512 | // we don't care whether the promise was resolved or rejected; just store the values
1513 | $translate(translationId, interpolateParams, interpolationId, defaultTranslationText, forceLanguage, sanitizeStrategy).then(regardless, regardless);
1514 | return deferred.promise;
1515 | };
1516 | for (var i = 0, c = translationIds.length; i < c; i++) {
1517 | promises.push(translate(translationIds[i]));
1518 | }
1519 | // wait for all (including storing to results)
1520 | return $q.all(promises).then(function () {
1521 | // return the results
1522 | return results;
1523 | });
1524 | };
1525 | return translateAll(translationId);
1526 | }
1527 |
1528 | var deferred = $q.defer();
1529 |
1530 | // trim off any whitespace
1531 | if (translationId) {
1532 | translationId = trim.apply(translationId);
1533 | }
1534 |
1535 | var promiseToWaitFor = (function () {
1536 | var promise = langPromises[uses] || langPromises[$preferredLanguage];
1537 |
1538 | fallbackIndex = 0;
1539 |
1540 | if ($storageFactory && !promise) {
1541 | // looks like there's no pending promise for $preferredLanguage or
1542 | // $uses. Maybe there's one pending for a language that comes from
1543 | // storage.
1544 | var langKey = Storage.get($storageKey);
1545 | promise = langPromises[langKey];
1546 |
1547 | if ($fallbackLanguage && $fallbackLanguage.length) {
1548 | var index = indexOf($fallbackLanguage, langKey);
1549 | // maybe the language from storage is also defined as fallback language
1550 | // we increase the fallback language index to not search in that language
1551 | // as fallback, since it's probably the first used language
1552 | // in that case the index starts after the first element
1553 | fallbackIndex = (index === 0) ? 1 : 0;
1554 |
1555 | // but we can make sure to ALWAYS fallback to preferred language at least
1556 | if (indexOf($fallbackLanguage, $preferredLanguage) < 0) {
1557 | $fallbackLanguage.push($preferredLanguage);
1558 | }
1559 | }
1560 | }
1561 | return promise;
1562 | }());
1563 |
1564 | if (!promiseToWaitFor) {
1565 | // no promise to wait for? okay. Then there's no loader registered
1566 | // nor is a one pending for language that comes from storage.
1567 | // We can just translate.
1568 | determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy).then(deferred.resolve, deferred.reject);
1569 | } else {
1570 | var promiseResolved = function () {
1571 | // $uses may have changed while waiting
1572 | if (!forceLanguage) {
1573 | uses = $uses;
1574 | }
1575 | determineTranslation(translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy).then(deferred.resolve, deferred.reject);
1576 | };
1577 | promiseResolved.displayName = 'promiseResolved';
1578 |
1579 | promiseToWaitFor['finally'](promiseResolved)['catch'](angular.noop); // we don't care about errors here, already handled
1580 | }
1581 | return deferred.promise;
1582 | };
1583 |
1584 | /**
1585 | * @name applyNotFoundIndicators
1586 | * @private
1587 | *
1588 | * @description
1589 | * Applies not fount indicators to given translation id, if needed.
1590 | * This function gets only executed, if a translation id doesn't exist,
1591 | * which is why a translation id is expected as argument.
1592 | *
1593 | * @param {string} translationId Translation id.
1594 | * @returns {string} Same as given translation id but applied with not found
1595 | * indicators.
1596 | */
1597 | var applyNotFoundIndicators = function (translationId) {
1598 | // applying notFoundIndicators
1599 | if ($notFoundIndicatorLeft) {
1600 | translationId = [$notFoundIndicatorLeft, translationId].join(' ');
1601 | }
1602 | if ($notFoundIndicatorRight) {
1603 | translationId = [translationId, $notFoundIndicatorRight].join(' ');
1604 | }
1605 | return translationId;
1606 | };
1607 |
1608 | /**
1609 | * @name useLanguage
1610 | * @private
1611 | *
1612 | * @description
1613 | * Makes actual use of a language by setting a given language key as used
1614 | * language and informs registered interpolators to also use the given
1615 | * key as locale.
1616 | *
1617 | * @param {string} key Locale key.
1618 | */
1619 | var useLanguage = function (key) {
1620 | $uses = key;
1621 |
1622 | // make sure to store new language key before triggering success event
1623 | if ($storageFactory) {
1624 | Storage.put($translate.storageKey(), $uses);
1625 | }
1626 |
1627 | $rootScope.$emit('$translateChangeSuccess', {language : key});
1628 |
1629 | // inform default interpolator
1630 | defaultInterpolator.setLocale($uses);
1631 |
1632 | var eachInterpolator = function (interpolator, id) {
1633 | interpolatorHashMap[id].setLocale($uses);
1634 | };
1635 | eachInterpolator.displayName = 'eachInterpolatorLocaleSetter';
1636 |
1637 | // inform all others too!
1638 | angular.forEach(interpolatorHashMap, eachInterpolator);
1639 | $rootScope.$emit('$translateChangeEnd', {language : key});
1640 | };
1641 |
1642 | /**
1643 | * @name loadAsync
1644 | * @private
1645 | *
1646 | * @description
1647 | * Kicks off registered async loader using `$injector` and applies existing
1648 | * loader options. When resolved, it updates translation tables accordingly
1649 | * or rejects with given language key.
1650 | *
1651 | * @param {string} key Language key.
1652 | * @return {Promise} A promise.
1653 | */
1654 | var loadAsync = function (key) {
1655 | if (!key) {
1656 | throw 'No language key specified for loading.';
1657 | }
1658 |
1659 | var deferred = $q.defer();
1660 |
1661 | $rootScope.$emit('$translateLoadingStart', {language : key});
1662 | pendingLoader = true;
1663 |
1664 | var cache = loaderCache;
1665 | if (typeof(cache) === 'string') {
1666 | // getting on-demand instance of loader
1667 | cache = $injector.get(cache);
1668 | }
1669 |
1670 | var loaderOptions = angular.extend({}, $loaderOptions, {
1671 | key : key,
1672 | $http : angular.extend({}, {
1673 | cache : cache
1674 | }, $loaderOptions.$http)
1675 | });
1676 |
1677 | var onLoaderSuccess = function (data) {
1678 | var translationTable = {};
1679 | $rootScope.$emit('$translateLoadingSuccess', {language : key});
1680 |
1681 | if (angular.isArray(data)) {
1682 | angular.forEach(data, function (table) {
1683 | angular.extend(translationTable, flatObject(table));
1684 | });
1685 | } else {
1686 | angular.extend(translationTable, flatObject(data));
1687 | }
1688 | pendingLoader = false;
1689 | deferred.resolve({
1690 | key : key,
1691 | table : translationTable
1692 | });
1693 | $rootScope.$emit('$translateLoadingEnd', {language : key});
1694 | };
1695 | onLoaderSuccess.displayName = 'onLoaderSuccess';
1696 |
1697 | var onLoaderError = function (key) {
1698 | $rootScope.$emit('$translateLoadingError', {language : key});
1699 | deferred.reject(key);
1700 | $rootScope.$emit('$translateLoadingEnd', {language : key});
1701 | };
1702 | onLoaderError.displayName = 'onLoaderError';
1703 |
1704 | $injector.get($loaderFactory)(loaderOptions)
1705 | .then(onLoaderSuccess, onLoaderError);
1706 |
1707 | return deferred.promise;
1708 | };
1709 |
1710 | if ($storageFactory) {
1711 | Storage = $injector.get($storageFactory);
1712 |
1713 | if (!Storage.get || !Storage.put) {
1714 | throw new Error('Couldn\'t use storage \'' + $storageFactory + '\', missing get() or put() method!');
1715 | }
1716 | }
1717 |
1718 | // if we have additional interpolations that were added via
1719 | // $translateProvider.addInterpolation(), we have to map'em
1720 | if ($interpolatorFactories.length) {
1721 | var eachInterpolationFactory = function (interpolatorFactory) {
1722 | var interpolator = $injector.get(interpolatorFactory);
1723 | // setting initial locale for each interpolation service
1724 | interpolator.setLocale($preferredLanguage || $uses);
1725 | // make'em recognizable through id
1726 | interpolatorHashMap[interpolator.getInterpolationIdentifier()] = interpolator;
1727 | };
1728 | eachInterpolationFactory.displayName = 'interpolationFactoryAdder';
1729 |
1730 | angular.forEach($interpolatorFactories, eachInterpolationFactory);
1731 | }
1732 |
1733 | /**
1734 | * @name getTranslationTable
1735 | * @private
1736 | *
1737 | * @description
1738 | * Returns a promise that resolves to the translation table
1739 | * or is rejected if an error occurred.
1740 | *
1741 | * @param langKey
1742 | * @returns {Q.promise}
1743 | */
1744 | var getTranslationTable = function (langKey) {
1745 | var deferred = $q.defer();
1746 | if (Object.prototype.hasOwnProperty.call($translationTable, langKey)) {
1747 | deferred.resolve($translationTable[langKey]);
1748 | } else if (langPromises[langKey]) {
1749 | var onResolve = function (data) {
1750 | translations(data.key, data.table);
1751 | deferred.resolve(data.table);
1752 | };
1753 | onResolve.displayName = 'translationTableResolver';
1754 | langPromises[langKey].then(onResolve, deferred.reject);
1755 | } else {
1756 | deferred.reject();
1757 | }
1758 | return deferred.promise;
1759 | };
1760 |
1761 | /**
1762 | * @name getFallbackTranslation
1763 | * @private
1764 | *
1765 | * @description
1766 | * Returns a promise that will resolve to the translation
1767 | * or be rejected if no translation was found for the language.
1768 | * This function is currently only used for fallback language translation.
1769 | *
1770 | * @param langKey The language to translate to.
1771 | * @param translationId
1772 | * @param interpolateParams
1773 | * @param Interpolator
1774 | * @param sanitizeStrategy
1775 | * @returns {Q.promise}
1776 | */
1777 | var getFallbackTranslation = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1778 | var deferred = $q.defer();
1779 |
1780 | var onResolve = function (translationTable) {
1781 | if (Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) {
1782 | Interpolator.setLocale(langKey);
1783 | var translation = translationTable[translationId];
1784 | if (translation.substr(0, 2) === '@:') {
1785 | getFallbackTranslation(langKey, translation.substr(2), interpolateParams, Interpolator, sanitizeStrategy)
1786 | .then(deferred.resolve, deferred.reject);
1787 | } else {
1788 | var interpolatedValue = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'service', sanitizeStrategy, translationId);
1789 | interpolatedValue = applyPostProcessing(translationId, translationTable[translationId], interpolatedValue, interpolateParams, langKey);
1790 |
1791 | deferred.resolve(interpolatedValue);
1792 |
1793 | }
1794 | Interpolator.setLocale($uses);
1795 | } else {
1796 | deferred.reject();
1797 | }
1798 | };
1799 | onResolve.displayName = 'fallbackTranslationResolver';
1800 |
1801 | getTranslationTable(langKey).then(onResolve, deferred.reject);
1802 |
1803 | return deferred.promise;
1804 | };
1805 |
1806 | /**
1807 | * @name getFallbackTranslationInstant
1808 | * @private
1809 | *
1810 | * @description
1811 | * Returns a translation
1812 | * This function is currently only used for fallback language translation.
1813 | *
1814 | * @param langKey The language to translate to.
1815 | * @param translationId
1816 | * @param interpolateParams
1817 | * @param Interpolator
1818 | * @param sanitizeStrategy sanitize strategy override
1819 | *
1820 | * @returns {string} translation
1821 | */
1822 | var getFallbackTranslationInstant = function (langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1823 | var result, translationTable = $translationTable[langKey];
1824 |
1825 | if (translationTable && Object.prototype.hasOwnProperty.call(translationTable, translationId) && translationTable[translationId] !== null) {
1826 | Interpolator.setLocale(langKey);
1827 | result = Interpolator.interpolate(translationTable[translationId], interpolateParams, 'filter', sanitizeStrategy, translationId);
1828 | result = applyPostProcessing(translationId, translationTable[translationId], result, interpolateParams, langKey, sanitizeStrategy);
1829 | // workaround for TrustedValueHolderType
1830 | if (!angular.isString(result) && angular.isFunction(result.$$unwrapTrustedValue)) {
1831 | var result2 = result.$$unwrapTrustedValue();
1832 | if (result2.substr(0, 2) === '@:') {
1833 | return getFallbackTranslationInstant(langKey, result2.substr(2), interpolateParams, Interpolator, sanitizeStrategy);
1834 | }
1835 | } else if (result.substr(0, 2) === '@:') {
1836 | return getFallbackTranslationInstant(langKey, result.substr(2), interpolateParams, Interpolator, sanitizeStrategy);
1837 | }
1838 | Interpolator.setLocale($uses);
1839 | }
1840 |
1841 | return result;
1842 | };
1843 |
1844 |
1845 | /**
1846 | * @name translateByHandler
1847 | * @private
1848 | *
1849 | * Translate by missing translation handler.
1850 | *
1851 | * @param translationId
1852 | * @param interpolateParams
1853 | * @param defaultTranslationText
1854 | * @param sanitizeStrategy sanitize strategy override
1855 | *
1856 | * @returns translation created by $missingTranslationHandler or translationId is $missingTranslationHandler is
1857 | * absent
1858 | */
1859 | var translateByHandler = function (translationId, interpolateParams, defaultTranslationText, sanitizeStrategy) {
1860 | // If we have a handler factory - we might also call it here to determine if it provides
1861 | // a default text for a translationid that can't be found anywhere in our tables
1862 | if ($missingTranslationHandlerFactory) {
1863 | return $injector.get($missingTranslationHandlerFactory)(translationId, $uses, interpolateParams, defaultTranslationText, sanitizeStrategy);
1864 | } else {
1865 | return translationId;
1866 | }
1867 | };
1868 |
1869 | /**
1870 | * @name resolveForFallbackLanguage
1871 | * @private
1872 | *
1873 | * Recursive helper function for fallbackTranslation that will sequentially look
1874 | * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1875 | *
1876 | * @param fallbackLanguageIndex
1877 | * @param translationId
1878 | * @param interpolateParams
1879 | * @param Interpolator
1880 | * @param defaultTranslationText
1881 | * @param sanitizeStrategy
1882 | * @returns {Q.promise} Promise that will resolve to the translation.
1883 | */
1884 | var resolveForFallbackLanguage = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) {
1885 | var deferred = $q.defer();
1886 |
1887 | if (fallbackLanguageIndex < $fallbackLanguage.length) {
1888 | var langKey = $fallbackLanguage[fallbackLanguageIndex];
1889 | getFallbackTranslation(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy).then(
1890 | function (data) {
1891 | deferred.resolve(data);
1892 | },
1893 | function () {
1894 | // Look in the next fallback language for a translation.
1895 | // It delays the resolving by passing another promise to resolve.
1896 | return resolveForFallbackLanguage(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy).then(deferred.resolve, deferred.reject);
1897 | }
1898 | );
1899 | } else {
1900 | // No translation found in any fallback language
1901 | // if a default translation text is set in the directive, then return this as a result
1902 | if (defaultTranslationText) {
1903 | deferred.resolve(defaultTranslationText);
1904 | } else {
1905 | var missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText);
1906 |
1907 | // if no default translation is set and an error handler is defined, send it to the handler
1908 | // and then return the result if it isn't undefined
1909 | if ($missingTranslationHandlerFactory && missingTranslationHandlerTranslation) {
1910 | deferred.resolve(missingTranslationHandlerTranslation);
1911 | } else {
1912 | deferred.reject(applyNotFoundIndicators(translationId));
1913 | }
1914 | }
1915 | }
1916 | return deferred.promise;
1917 | };
1918 |
1919 | /**
1920 | * @name resolveForFallbackLanguageInstant
1921 | * @private
1922 | *
1923 | * Recursive helper function for fallbackTranslation that will sequentially look
1924 | * for a translation in the fallbackLanguages starting with fallbackLanguageIndex.
1925 | *
1926 | * @param fallbackLanguageIndex
1927 | * @param translationId
1928 | * @param interpolateParams
1929 | * @param Interpolator
1930 | * @param sanitizeStrategy
1931 | * @returns {string} translation
1932 | */
1933 | var resolveForFallbackLanguageInstant = function (fallbackLanguageIndex, translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1934 | var result;
1935 |
1936 | if (fallbackLanguageIndex < $fallbackLanguage.length) {
1937 | var langKey = $fallbackLanguage[fallbackLanguageIndex];
1938 | result = getFallbackTranslationInstant(langKey, translationId, interpolateParams, Interpolator, sanitizeStrategy);
1939 | if (!result && result !== '') {
1940 | result = resolveForFallbackLanguageInstant(fallbackLanguageIndex + 1, translationId, interpolateParams, Interpolator);
1941 | }
1942 | }
1943 | return result;
1944 | };
1945 |
1946 | /**
1947 | * Translates with the usage of the fallback languages.
1948 | *
1949 | * @param translationId
1950 | * @param interpolateParams
1951 | * @param Interpolator
1952 | * @param defaultTranslationText
1953 | * @param sanitizeStrategy
1954 | * @returns {Q.promise} Promise, that resolves to the translation.
1955 | */
1956 | var fallbackTranslation = function (translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy) {
1957 | // Start with the fallbackLanguage with index 0
1958 | return resolveForFallbackLanguage((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy);
1959 | };
1960 |
1961 | /**
1962 | * Translates with the usage of the fallback languages.
1963 | *
1964 | * @param translationId
1965 | * @param interpolateParams
1966 | * @param Interpolator
1967 | * @param sanitizeStrategy
1968 | * @returns {String} translation
1969 | */
1970 | var fallbackTranslationInstant = function (translationId, interpolateParams, Interpolator, sanitizeStrategy) {
1971 | // Start with the fallbackLanguage with index 0
1972 | return resolveForFallbackLanguageInstant((startFallbackIteration > 0 ? startFallbackIteration : fallbackIndex), translationId, interpolateParams, Interpolator, sanitizeStrategy);
1973 | };
1974 |
1975 | var determineTranslation = function (translationId, interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy) {
1976 |
1977 | var deferred = $q.defer();
1978 |
1979 | var table = uses ? $translationTable[uses] : $translationTable,
1980 | Interpolator = (interpolationId) ? interpolatorHashMap[interpolationId] : defaultInterpolator;
1981 |
1982 | // if the translation id exists, we can just interpolate it
1983 | if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) {
1984 | var translation = table[translationId];
1985 |
1986 | // If using link, rerun $translate with linked translationId and return it
1987 | if (translation.substr(0, 2) === '@:') {
1988 |
1989 | $translate(translation.substr(2), interpolateParams, interpolationId, defaultTranslationText, uses, sanitizeStrategy)
1990 | .then(deferred.resolve, deferred.reject);
1991 | } else {
1992 | //
1993 | var resolvedTranslation = Interpolator.interpolate(translation, interpolateParams, 'service', sanitizeStrategy, translationId);
1994 | resolvedTranslation = applyPostProcessing(translationId, translation, resolvedTranslation, interpolateParams, uses);
1995 | deferred.resolve(resolvedTranslation);
1996 | }
1997 | } else {
1998 | var missingTranslationHandlerTranslation;
1999 | // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
2000 | if ($missingTranslationHandlerFactory && !pendingLoader) {
2001 | missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, defaultTranslationText);
2002 | }
2003 |
2004 | // since we couldn't translate the inital requested translation id,
2005 | // we try it now with one or more fallback languages, if fallback language(s) is
2006 | // configured.
2007 | if (uses && $fallbackLanguage && $fallbackLanguage.length) {
2008 | fallbackTranslation(translationId, interpolateParams, Interpolator, defaultTranslationText, sanitizeStrategy)
2009 | .then(function (translation) {
2010 | deferred.resolve(translation);
2011 | }, function (_translationId) {
2012 | deferred.reject(applyNotFoundIndicators(_translationId));
2013 | });
2014 | } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
2015 | // looks like the requested translation id doesn't exists.
2016 | // Now, if there is a registered handler for missing translations and no
2017 | // asyncLoader is pending, we execute the handler
2018 | if (defaultTranslationText) {
2019 | deferred.resolve(defaultTranslationText);
2020 | } else {
2021 | deferred.resolve(missingTranslationHandlerTranslation);
2022 | }
2023 | } else {
2024 | if (defaultTranslationText) {
2025 | deferred.resolve(defaultTranslationText);
2026 | } else {
2027 | deferred.reject(applyNotFoundIndicators(translationId));
2028 | }
2029 | }
2030 | }
2031 | return deferred.promise;
2032 | };
2033 |
2034 | var determineTranslationInstant = function (translationId, interpolateParams, interpolationId, uses, sanitizeStrategy) {
2035 |
2036 | var result, table = uses ? $translationTable[uses] : $translationTable,
2037 | Interpolator = defaultInterpolator;
2038 |
2039 | // if the interpolation id exists use custom interpolator
2040 | if (interpolatorHashMap && Object.prototype.hasOwnProperty.call(interpolatorHashMap, interpolationId)) {
2041 | Interpolator = interpolatorHashMap[interpolationId];
2042 | }
2043 |
2044 | // if the translation id exists, we can just interpolate it
2045 | if (table && Object.prototype.hasOwnProperty.call(table, translationId) && table[translationId] !== null) {
2046 | var translation = table[translationId];
2047 |
2048 | // If using link, rerun $translate with linked translationId and return it
2049 | if (translation.substr(0, 2) === '@:') {
2050 | result = determineTranslationInstant(translation.substr(2), interpolateParams, interpolationId, uses, sanitizeStrategy);
2051 | } else {
2052 | result = Interpolator.interpolate(translation, interpolateParams, 'filter', sanitizeStrategy, translationId);
2053 | result = applyPostProcessing(translationId, translation, result, interpolateParams, uses, sanitizeStrategy);
2054 | }
2055 | } else {
2056 | var missingTranslationHandlerTranslation;
2057 | // for logging purposes only (as in $translateMissingTranslationHandlerLog), value is not returned to promise
2058 | if ($missingTranslationHandlerFactory && !pendingLoader) {
2059 | missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
2060 | }
2061 |
2062 | // since we couldn't translate the inital requested translation id,
2063 | // we try it now with one or more fallback languages, if fallback language(s) is
2064 | // configured.
2065 | if (uses && $fallbackLanguage && $fallbackLanguage.length) {
2066 | fallbackIndex = 0;
2067 | result = fallbackTranslationInstant(translationId, interpolateParams, Interpolator, sanitizeStrategy);
2068 | } else if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
2069 | // looks like the requested translation id doesn't exists.
2070 | // Now, if there is a registered handler for missing translations and no
2071 | // asyncLoader is pending, we execute the handler
2072 | result = missingTranslationHandlerTranslation;
2073 | } else {
2074 | result = applyNotFoundIndicators(translationId);
2075 | }
2076 | }
2077 |
2078 | return result;
2079 | };
2080 |
2081 | var clearNextLangAndPromise = function (key) {
2082 | if ($nextLang === key) {
2083 | $nextLang = undefined;
2084 | }
2085 | langPromises[key] = undefined;
2086 | };
2087 |
2088 | var applyPostProcessing = function (translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy) {
2089 | var fn = postProcessFn;
2090 |
2091 | if (fn) {
2092 |
2093 | if (typeof(fn) === 'string') {
2094 | // getting on-demand instance
2095 | fn = $injector.get(fn);
2096 | }
2097 | if (fn) {
2098 | return fn(translationId, translation, resolvedTranslation, interpolateParams, uses, sanitizeStrategy);
2099 | }
2100 | }
2101 |
2102 | return resolvedTranslation;
2103 | };
2104 |
2105 | var loadTranslationsIfMissing = function (key) {
2106 | if (!$translationTable[key] && $loaderFactory && !langPromises[key]) {
2107 | langPromises[key] = loadAsync(key).then(function (translation) {
2108 | translations(translation.key, translation.table);
2109 | return translation;
2110 | });
2111 | }
2112 | };
2113 |
2114 | /**
2115 | * @ngdoc function
2116 | * @name pascalprecht.translate.$translate#preferredLanguage
2117 | * @methodOf pascalprecht.translate.$translate
2118 | *
2119 | * @description
2120 | * Returns the language key for the preferred language.
2121 | *
2122 | * @param {string} langKey language String or Array to be used as preferredLanguage (changing at runtime)
2123 | *
2124 | * @return {string} preferred language key
2125 | */
2126 | $translate.preferredLanguage = function (langKey) {
2127 | if (langKey) {
2128 | setupPreferredLanguage(langKey);
2129 | }
2130 | return $preferredLanguage;
2131 | };
2132 |
2133 | /**
2134 | * @ngdoc function
2135 | * @name pascalprecht.translate.$translate#cloakClassName
2136 | * @methodOf pascalprecht.translate.$translate
2137 | *
2138 | * @description
2139 | * Returns the configured class name for `translate-cloak` directive.
2140 | *
2141 | * @return {string} cloakClassName
2142 | */
2143 | $translate.cloakClassName = function () {
2144 | return $cloakClassName;
2145 | };
2146 |
2147 | /**
2148 | * @ngdoc function
2149 | * @name pascalprecht.translate.$translate#nestedObjectDelimeter
2150 | * @methodOf pascalprecht.translate.$translate
2151 | *
2152 | * @description
2153 | * Returns the configured delimiter for nested namespaces.
2154 | *
2155 | * @return {string} nestedObjectDelimeter
2156 | */
2157 | $translate.nestedObjectDelimeter = function () {
2158 | return $nestedObjectDelimeter;
2159 | };
2160 |
2161 | /**
2162 | * @ngdoc function
2163 | * @name pascalprecht.translate.$translate#fallbackLanguage
2164 | * @methodOf pascalprecht.translate.$translate
2165 | *
2166 | * @description
2167 | * Returns the language key for the fallback languages or sets a new fallback stack.
2168 | * It is recommended to call this before {@link pascalprecht.translate.$translateProvider#preferredLanguage preferredLanguage()}.
2169 | *
2170 | * @param {string=} langKey language String or Array of fallback languages to be used (to change stack at runtime)
2171 | *
2172 | * @return {string||array} fallback language key
2173 | */
2174 | $translate.fallbackLanguage = function (langKey) {
2175 | if (langKey !== undefined && langKey !== null) {
2176 | fallbackStack(langKey);
2177 |
2178 | // as we might have an async loader initiated and a new translation language might have been defined
2179 | // we need to add the promise to the stack also. So - iterate.
2180 | if ($loaderFactory) {
2181 | if ($fallbackLanguage && $fallbackLanguage.length) {
2182 | for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2183 | if (!langPromises[$fallbackLanguage[i]]) {
2184 | langPromises[$fallbackLanguage[i]] = loadAsync($fallbackLanguage[i]);
2185 | }
2186 | }
2187 | }
2188 | }
2189 | $translate.use($translate.use());
2190 | }
2191 | if ($fallbackWasString) {
2192 | return $fallbackLanguage[0];
2193 | } else {
2194 | return $fallbackLanguage;
2195 | }
2196 |
2197 | };
2198 |
2199 | /**
2200 | * @ngdoc function
2201 | * @name pascalprecht.translate.$translate#useFallbackLanguage
2202 | * @methodOf pascalprecht.translate.$translate
2203 | *
2204 | * @description
2205 | * Sets the first key of the fallback language stack to be used for translation.
2206 | * Therefore all languages in the fallback array BEFORE this key will be skipped!
2207 | *
2208 | * @param {string=} langKey Contains the langKey the iteration shall start with. Set to false if you want to
2209 | * get back to the whole stack
2210 | */
2211 | $translate.useFallbackLanguage = function (langKey) {
2212 | if (langKey !== undefined && langKey !== null) {
2213 | if (!langKey) {
2214 | startFallbackIteration = 0;
2215 | } else {
2216 | var langKeyPosition = indexOf($fallbackLanguage, langKey);
2217 | if (langKeyPosition > -1) {
2218 | startFallbackIteration = langKeyPosition;
2219 | }
2220 | }
2221 |
2222 | }
2223 |
2224 | };
2225 |
2226 | /**
2227 | * @ngdoc function
2228 | * @name pascalprecht.translate.$translate#proposedLanguage
2229 | * @methodOf pascalprecht.translate.$translate
2230 | *
2231 | * @description
2232 | * Returns the language key of language that is currently loaded asynchronously.
2233 | *
2234 | * @return {string} language key
2235 | */
2236 | $translate.proposedLanguage = function () {
2237 | return $nextLang;
2238 | };
2239 |
2240 | /**
2241 | * @ngdoc function
2242 | * @name pascalprecht.translate.$translate#storage
2243 | * @methodOf pascalprecht.translate.$translate
2244 | *
2245 | * @description
2246 | * Returns registered storage.
2247 | *
2248 | * @return {object} Storage
2249 | */
2250 | $translate.storage = function () {
2251 | return Storage;
2252 | };
2253 |
2254 | /**
2255 | * @ngdoc function
2256 | * @name pascalprecht.translate.$translate#negotiateLocale
2257 | * @methodOf pascalprecht.translate.$translate
2258 | *
2259 | * @description
2260 | * Returns a language key based on available languages and language aliases. If a
2261 | * language key cannot be resolved, returns undefined.
2262 | *
2263 | * If no or a falsy key is given, returns undefined.
2264 | *
2265 | * @param {string} [key] Language key
2266 | * @return {string|undefined} Language key or undefined if no language key is found.
2267 | */
2268 | $translate.negotiateLocale = negotiateLocale;
2269 |
2270 | /**
2271 | * @ngdoc function
2272 | * @name pascalprecht.translate.$translate#use
2273 | * @methodOf pascalprecht.translate.$translate
2274 | *
2275 | * @description
2276 | * Tells angular-translate which language to use by given language key. This method is
2277 | * used to change language at runtime. It also takes care of storing the language
2278 | * key in a configured store to let your app remember the choosed language.
2279 | *
2280 | * When trying to 'use' a language which isn't available it tries to load it
2281 | * asynchronously with registered loaders.
2282 | *
2283 | * Returns promise object with loaded language file data or string of the currently used language.
2284 | *
2285 | * If no or a falsy key is given it returns the currently used language key.
2286 | * The returned string will be ```undefined``` if setting up $translate hasn't finished.
2287 | * @example
2288 | * $translate.use("en_US").then(function(data){
2289 | * $scope.text = $translate("HELLO");
2290 | * });
2291 | *
2292 | * @param {string=} key Language key
2293 | * @return {object|string} Promise with loaded language data or the language key if a falsy param was given.
2294 | */
2295 | $translate.use = function (key) {
2296 | if (!key) {
2297 | return $uses;
2298 | }
2299 |
2300 | var deferred = $q.defer();
2301 | deferred.promise.then(null, angular.noop); // AJS "Possibly unhandled rejection"
2302 |
2303 | $rootScope.$emit('$translateChangeStart', {language : key});
2304 |
2305 | // Try to get the aliased language key
2306 | var aliasedKey = negotiateLocale(key);
2307 | // Ensure only registered language keys will be loaded
2308 | if ($availableLanguageKeys.length > 0 && !aliasedKey) {
2309 | return $q.reject(key);
2310 | }
2311 |
2312 | if (aliasedKey) {
2313 | key = aliasedKey;
2314 | }
2315 |
2316 | // if there isn't a translation table for the language we've requested,
2317 | // we load it asynchronously
2318 | $nextLang = key;
2319 | if (($forceAsyncReloadEnabled || !$translationTable[key]) && $loaderFactory && !langPromises[key]) {
2320 | langPromises[key] = loadAsync(key).then(function (translation) {
2321 | translations(translation.key, translation.table);
2322 | deferred.resolve(translation.key);
2323 | if ($nextLang === key) {
2324 | useLanguage(translation.key);
2325 | }
2326 | return translation;
2327 | }, function (key) {
2328 | $rootScope.$emit('$translateChangeError', {language : key});
2329 | deferred.reject(key);
2330 | $rootScope.$emit('$translateChangeEnd', {language : key});
2331 | return $q.reject(key);
2332 | });
2333 | langPromises[key]['finally'](function () {
2334 | clearNextLangAndPromise(key);
2335 | })['catch'](angular.noop); // we don't care about errors (clearing)
2336 | } else if (langPromises[key]) {
2337 | // we are already loading this asynchronously
2338 | // resolve our new deferred when the old langPromise is resolved
2339 | langPromises[key].then(function (translation) {
2340 | if ($nextLang === translation.key) {
2341 | useLanguage(translation.key);
2342 | }
2343 | deferred.resolve(translation.key);
2344 | return translation;
2345 | }, function (key) {
2346 | // find first available fallback language if that request has failed
2347 | if (!$uses && $fallbackLanguage && $fallbackLanguage.length > 0 && $fallbackLanguage[0] !== key) {
2348 | return $translate.use($fallbackLanguage[0]).then(deferred.resolve, deferred.reject);
2349 | } else {
2350 | return deferred.reject(key);
2351 | }
2352 | });
2353 | } else {
2354 | deferred.resolve(key);
2355 | useLanguage(key);
2356 | }
2357 |
2358 | return deferred.promise;
2359 | };
2360 |
2361 | /**
2362 | * @ngdoc function
2363 | * @name pascalprecht.translate.$translate#resolveClientLocale
2364 | * @methodOf pascalprecht.translate.$translate
2365 | *
2366 | * @description
2367 | * This returns the current browser/client's language key. The result is processed with the configured uniform tag resolver.
2368 | *
2369 | * @returns {string} the current client/browser language key
2370 | */
2371 | $translate.resolveClientLocale = function () {
2372 | return getLocale();
2373 | };
2374 |
2375 | /**
2376 | * @ngdoc function
2377 | * @name pascalprecht.translate.$translate#storageKey
2378 | * @methodOf pascalprecht.translate.$translate
2379 | *
2380 | * @description
2381 | * Returns the key for the storage.
2382 | *
2383 | * @return {string} storage key
2384 | */
2385 | $translate.storageKey = function () {
2386 | return storageKey();
2387 | };
2388 |
2389 | /**
2390 | * @ngdoc function
2391 | * @name pascalprecht.translate.$translate#isPostCompilingEnabled
2392 | * @methodOf pascalprecht.translate.$translate
2393 | *
2394 | * @description
2395 | * Returns whether post compiling is enabled or not
2396 | *
2397 | * @return {bool} storage key
2398 | */
2399 | $translate.isPostCompilingEnabled = function () {
2400 | return $postCompilingEnabled;
2401 | };
2402 |
2403 | /**
2404 | * @ngdoc function
2405 | * @name pascalprecht.translate.$translate#isForceAsyncReloadEnabled
2406 | * @methodOf pascalprecht.translate.$translate
2407 | *
2408 | * @description
2409 | * Returns whether force async reload is enabled or not
2410 | *
2411 | * @return {boolean} forceAsyncReload value
2412 | */
2413 | $translate.isForceAsyncReloadEnabled = function () {
2414 | return $forceAsyncReloadEnabled;
2415 | };
2416 |
2417 | /**
2418 | * @ngdoc function
2419 | * @name pascalprecht.translate.$translate#isKeepContent
2420 | * @methodOf pascalprecht.translate.$translate
2421 | *
2422 | * @description
2423 | * Returns whether keepContent or not
2424 | *
2425 | * @return {boolean} keepContent value
2426 | */
2427 | $translate.isKeepContent = function () {
2428 | return $keepContent;
2429 | };
2430 |
2431 | /**
2432 | * @ngdoc function
2433 | * @name pascalprecht.translate.$translate#refresh
2434 | * @methodOf pascalprecht.translate.$translate
2435 | *
2436 | * @description
2437 | * Refreshes a translation table pointed by the given langKey. If langKey is not specified,
2438 | * the module will drop all existent translation tables and load new version of those which
2439 | * are currently in use.
2440 | *
2441 | * Refresh means that the module will drop target translation table and try to load it again.
2442 | *
2443 | * In case there are no loaders registered the refresh() method will throw an Error.
2444 | *
2445 | * If the module is able to refresh translation tables refresh() method will broadcast
2446 | * $translateRefreshStart and $translateRefreshEnd events.
2447 | *
2448 | * @example
2449 | * // this will drop all currently existent translation tables and reload those which are
2450 | * // currently in use
2451 | * $translate.refresh();
2452 | * // this will refresh a translation table for the en_US language
2453 | * $translate.refresh('en_US');
2454 | *
2455 | * @param {string} langKey A language key of the table, which has to be refreshed
2456 | *
2457 | * @return {promise} Promise, which will be resolved in case a translation tables refreshing
2458 | * process is finished successfully, and reject if not.
2459 | */
2460 | $translate.refresh = function (langKey) {
2461 | if (!$loaderFactory) {
2462 | throw new Error('Couldn\'t refresh translation table, no loader registered!');
2463 | }
2464 |
2465 | $rootScope.$emit('$translateRefreshStart', {language : langKey});
2466 |
2467 | var deferred = $q.defer(), updatedLanguages = {};
2468 |
2469 | //private helper
2470 | function loadNewData(languageKey) {
2471 | var promise = loadAsync(languageKey);
2472 | //update the load promise cache for this language
2473 | langPromises[languageKey] = promise;
2474 | //register a data handler for the promise
2475 | promise.then(function (data) {
2476 | //clear the cache for this language
2477 | $translationTable[languageKey] = {};
2478 | //add the new data for this language
2479 | translations(languageKey, data.table);
2480 | //track that we updated this language
2481 | updatedLanguages[languageKey] = true;
2482 | },
2483 | //handle rejection to appease the $q validation
2484 | angular.noop);
2485 | return promise;
2486 | }
2487 |
2488 | //set up post-processing
2489 | deferred.promise.then(
2490 | function () {
2491 | for (var key in $translationTable) {
2492 | if ($translationTable.hasOwnProperty(key)) {
2493 | //delete cache entries that were not updated
2494 | if (!(key in updatedLanguages)) {
2495 | delete $translationTable[key];
2496 | }
2497 | }
2498 | }
2499 | if ($uses) {
2500 | useLanguage($uses);
2501 | }
2502 | },
2503 | //handle rejection to appease the $q validation
2504 | angular.noop
2505 | )['finally'](
2506 | function () {
2507 | $rootScope.$emit('$translateRefreshEnd', {language : langKey});
2508 | }
2509 | );
2510 |
2511 | if (!langKey) {
2512 | // if there's no language key specified we refresh ALL THE THINGS!
2513 | var languagesToReload = $fallbackLanguage && $fallbackLanguage.slice() || [];
2514 | if ($uses && languagesToReload.indexOf($uses) === -1) {
2515 | languagesToReload.push($uses);
2516 | }
2517 | $q.all(languagesToReload.map(loadNewData)).then(deferred.resolve, deferred.reject);
2518 |
2519 | } else if ($translationTable[langKey]) {
2520 | //just refresh the specified language cache
2521 | loadNewData(langKey).then(deferred.resolve, deferred.reject);
2522 |
2523 | } else {
2524 | deferred.reject();
2525 | }
2526 |
2527 | return deferred.promise;
2528 | };
2529 |
2530 | /**
2531 | * @ngdoc function
2532 | * @name pascalprecht.translate.$translate#instant
2533 | * @methodOf pascalprecht.translate.$translate
2534 | *
2535 | * @description
2536 | * Returns a translation instantly from the internal state of loaded translation. All rules
2537 | * regarding the current language, the preferred language of even fallback languages will be
2538 | * used except any promise handling. If a language was not found, an asynchronous loading
2539 | * will be invoked in the background.
2540 | *
2541 | * @param {string|array} translationId A token which represents a translation id
2542 | * This can be optionally an array of translation ids which
2543 | * results that the function's promise returns an object where
2544 | * each key is the translation id and the value the translation.
2545 | * @param {object=} [interpolateParams={}] Params
2546 | * @param {string=} [interpolationId=undefined] The id of the interpolation to use (use default unless set via useInterpolation())
2547 | * @param {string=} [forceLanguage=false] A language to be used instead of the current language
2548 | * @param {string=} [sanitizeStrategy=undefined] force sanitize strategy for this call instead of using the configured one (use default unless set)
2549 | *
2550 | * @return {string|object} translation
2551 | */
2552 | $translate.instant = function (translationId, interpolateParams, interpolationId, forceLanguage, sanitizeStrategy) {
2553 |
2554 | // we don't want to re-negotiate $uses
2555 | var uses = (forceLanguage && forceLanguage !== $uses) ? // we don't want to re-negotiate $uses
2556 | (negotiateLocale(forceLanguage) || forceLanguage) : $uses;
2557 |
2558 | // Detect undefined and null values to shorten the execution and prevent exceptions
2559 | if (translationId === null || angular.isUndefined(translationId)) {
2560 | return translationId;
2561 | }
2562 |
2563 | // Check forceLanguage is present
2564 | if (forceLanguage) {
2565 | loadTranslationsIfMissing(forceLanguage);
2566 | }
2567 |
2568 | // Duck detection: If the first argument is an array, a bunch of translations was requested.
2569 | // The result is an object.
2570 | if (angular.isArray(translationId)) {
2571 | var results = {};
2572 | for (var i = 0, c = translationId.length; i < c; i++) {
2573 | results[translationId[i]] = $translate.instant(translationId[i], interpolateParams, interpolationId, forceLanguage, sanitizeStrategy);
2574 | }
2575 | return results;
2576 | }
2577 |
2578 | // We discarded unacceptable values. So we just need to verify if translationId is empty String
2579 | if (angular.isString(translationId) && translationId.length < 1) {
2580 | return translationId;
2581 | }
2582 |
2583 | // trim off any whitespace
2584 | if (translationId) {
2585 | translationId = trim.apply(translationId);
2586 | }
2587 |
2588 | var result, possibleLangKeys = [];
2589 | if ($preferredLanguage) {
2590 | possibleLangKeys.push($preferredLanguage);
2591 | }
2592 | if (uses) {
2593 | possibleLangKeys.push(uses);
2594 | }
2595 | if ($fallbackLanguage && $fallbackLanguage.length) {
2596 | possibleLangKeys = possibleLangKeys.concat($fallbackLanguage);
2597 | }
2598 | for (var j = 0, d = possibleLangKeys.length; j < d; j++) {
2599 | var possibleLangKey = possibleLangKeys[j];
2600 | if ($translationTable[possibleLangKey]) {
2601 | if (typeof $translationTable[possibleLangKey][translationId] !== 'undefined') {
2602 | result = determineTranslationInstant(translationId, interpolateParams, interpolationId, uses, sanitizeStrategy);
2603 | }
2604 | }
2605 | if (typeof result !== 'undefined') {
2606 | break;
2607 | }
2608 | }
2609 |
2610 | if (!result && result !== '') {
2611 | if ($notFoundIndicatorLeft || $notFoundIndicatorRight) {
2612 | result = applyNotFoundIndicators(translationId);
2613 | } else {
2614 | // Return translation of default interpolator if not found anything.
2615 | result = defaultInterpolator.interpolate(translationId, interpolateParams, 'filter', sanitizeStrategy);
2616 |
2617 | // looks like the requested translation id doesn't exists.
2618 | // Now, if there is a registered handler for missing translations and no
2619 | // asyncLoader is pending, we execute the handler
2620 | var missingTranslationHandlerTranslation;
2621 | if ($missingTranslationHandlerFactory && !pendingLoader) {
2622 | missingTranslationHandlerTranslation = translateByHandler(translationId, interpolateParams, sanitizeStrategy);
2623 | }
2624 |
2625 | if ($missingTranslationHandlerFactory && !pendingLoader && missingTranslationHandlerTranslation) {
2626 | result = missingTranslationHandlerTranslation;
2627 | }
2628 | }
2629 | }
2630 |
2631 | return result;
2632 | };
2633 |
2634 | /**
2635 | * @ngdoc function
2636 | * @name pascalprecht.translate.$translate#versionInfo
2637 | * @methodOf pascalprecht.translate.$translate
2638 | *
2639 | * @description
2640 | * Returns the current version information for the angular-translate library
2641 | *
2642 | * @return {string} angular-translate version
2643 | */
2644 | $translate.versionInfo = function () {
2645 | return version;
2646 | };
2647 |
2648 | /**
2649 | * @ngdoc function
2650 | * @name pascalprecht.translate.$translate#loaderCache
2651 | * @methodOf pascalprecht.translate.$translate
2652 | *
2653 | * @description
2654 | * Returns the defined loaderCache.
2655 | *
2656 | * @return {boolean|string|object} current value of loaderCache
2657 | */
2658 | $translate.loaderCache = function () {
2659 | return loaderCache;
2660 | };
2661 |
2662 | // internal purpose only
2663 | $translate.directivePriority = function () {
2664 | return directivePriority;
2665 | };
2666 |
2667 | // internal purpose only
2668 | $translate.statefulFilter = function () {
2669 | return statefulFilter;
2670 | };
2671 |
2672 | /**
2673 | * @ngdoc function
2674 | * @name pascalprecht.translate.$translate#isReady
2675 | * @methodOf pascalprecht.translate.$translate
2676 | *
2677 | * @description
2678 | * Returns whether the service is "ready" to translate (i.e. loading 1st language).
2679 | *
2680 | * See also {@link pascalprecht.translate.$translate#methods_onReady onReady()}.
2681 | *
2682 | * @return {boolean} current value of ready
2683 | */
2684 | $translate.isReady = function () {
2685 | return $isReady;
2686 | };
2687 |
2688 | var $onReadyDeferred = $q.defer();
2689 | $onReadyDeferred.promise.then(function () {
2690 | $isReady = true;
2691 | });
2692 |
2693 | /**
2694 | * @ngdoc function
2695 | * @name pascalprecht.translate.$translate#onReady
2696 | * @methodOf pascalprecht.translate.$translate
2697 | *
2698 | * @description
2699 | * Calls the function provided or resolved the returned promise after the service is "ready" to translate (i.e. loading 1st language).
2700 | *
2701 | * See also {@link pascalprecht.translate.$translate#methods_isReady isReady()}.
2702 | *
2703 | * @param {Function=} fn Function to invoke when service is ready
2704 | * @return {object} Promise resolved when service is ready
2705 | */
2706 | $translate.onReady = function (fn) {
2707 | var deferred = $q.defer();
2708 | if (angular.isFunction(fn)) {
2709 | deferred.promise.then(fn);
2710 | }
2711 | if ($isReady) {
2712 | deferred.resolve();
2713 | } else {
2714 | $onReadyDeferred.promise.then(deferred.resolve);
2715 | }
2716 | return deferred.promise;
2717 | };
2718 |
2719 | /**
2720 | * @ngdoc function
2721 | * @name pascalprecht.translate.$translate#getAvailableLanguageKeys
2722 | * @methodOf pascalprecht.translate.$translate
2723 | *
2724 | * @description
2725 | * This function simply returns the registered language keys being defined before in the config phase
2726 | * With this, an application can use the array to provide a language selection dropdown or similar
2727 | * without any additional effort
2728 | *
2729 | * @returns {object} returns the list of possibly registered language keys and mapping or null if not defined
2730 | */
2731 | $translate.getAvailableLanguageKeys = function () {
2732 | if ($availableLanguageKeys.length > 0) {
2733 | return $availableLanguageKeys;
2734 | }
2735 | return null;
2736 | };
2737 |
2738 | /**
2739 | * @ngdoc function
2740 | * @name pascalprecht.translate.$translate#getTranslationTable
2741 | * @methodOf pascalprecht.translate.$translate
2742 | *
2743 | * @description
2744 | * Returns translation table by the given language key.
2745 | *
2746 | * Unless a language is provided it returns a translation table of the current one.
2747 | * Note: If translation dictionary is currently downloading or in progress
2748 | * it will return null.
2749 | *
2750 | * @param {string} langKey A token which represents a translation id
2751 | *
2752 | * @return {object} a copy of angular-translate $translationTable
2753 | */
2754 | $translate.getTranslationTable = function (langKey) {
2755 | langKey = langKey || $translate.use();
2756 | if (langKey && $translationTable[langKey]) {
2757 | return angular.copy($translationTable[langKey]);
2758 | }
2759 | return null;
2760 | };
2761 |
2762 | // Whenever $translateReady is being fired, this will ensure the state of $isReady
2763 | var globalOnReadyListener = $rootScope.$on('$translateReady', function () {
2764 | $onReadyDeferred.resolve();
2765 | globalOnReadyListener(); // one time only
2766 | globalOnReadyListener = null;
2767 | });
2768 | var globalOnChangeListener = $rootScope.$on('$translateChangeEnd', function () {
2769 | $onReadyDeferred.resolve();
2770 | globalOnChangeListener(); // one time only
2771 | globalOnChangeListener = null;
2772 | });
2773 |
2774 | if ($loaderFactory) {
2775 |
2776 | // If at least one async loader is defined and there are no
2777 | // (default) translations available we should try to load them.
2778 | if (angular.equals($translationTable, {})) {
2779 | if ($translate.use()) {
2780 | $translate.use($translate.use());
2781 | }
2782 | }
2783 |
2784 | // Also, if there are any fallback language registered, we start
2785 | // loading them asynchronously as soon as we can.
2786 | if ($fallbackLanguage && $fallbackLanguage.length) {
2787 | var processAsyncResult = function (translation) {
2788 | translations(translation.key, translation.table);
2789 | $rootScope.$emit('$translateChangeEnd', {language : translation.key});
2790 | return translation;
2791 | };
2792 | for (var i = 0, len = $fallbackLanguage.length; i < len; i++) {
2793 | var fallbackLanguageId = $fallbackLanguage[i];
2794 | if ($forceAsyncReloadEnabled || !$translationTable[fallbackLanguageId]) {
2795 | langPromises[fallbackLanguageId] = loadAsync(fallbackLanguageId).then(processAsyncResult);
2796 | }
2797 | }
2798 | }
2799 | } else {
2800 | $rootScope.$emit('$translateReady', {language : $translate.use()});
2801 | }
2802 |
2803 | return $translate;
2804 | }];
2805 | }
2806 |
2807 | $translate.displayName = 'displayName';
2808 |
2809 | /**
2810 | * @ngdoc object
2811 | * @name pascalprecht.translate.$translateDefaultInterpolation
2812 | * @requires $interpolate
2813 | *
2814 | * @description
2815 | * Uses angular's `$interpolate` services to interpolate strings against some values.
2816 | *
2817 | * Be aware to configure a proper sanitization strategy.
2818 | *
2819 | * See also:
2820 | * * {@link pascalprecht.translate.$translateSanitization}
2821 | *
2822 | * @return {object} $translateDefaultInterpolation Interpolator service
2823 | */
2824 | angular.module('pascalprecht.translate').factory('$translateDefaultInterpolation', $translateDefaultInterpolation);
2825 |
2826 | function $translateDefaultInterpolation ($interpolate, $translateSanitization) {
2827 |
2828 | 'use strict';
2829 |
2830 | var $translateInterpolator = {},
2831 | $locale,
2832 | $identifier = 'default';
2833 |
2834 | /**
2835 | * @ngdoc function
2836 | * @name pascalprecht.translate.$translateDefaultInterpolation#setLocale
2837 | * @methodOf pascalprecht.translate.$translateDefaultInterpolation
2838 | *
2839 | * @description
2840 | * Sets current locale (this is currently not use in this interpolation).
2841 | *
2842 | * @param {string} locale Language key or locale.
2843 | */
2844 | $translateInterpolator.setLocale = function (locale) {
2845 | $locale = locale;
2846 | };
2847 |
2848 | /**
2849 | * @ngdoc function
2850 | * @name pascalprecht.translate.$translateDefaultInterpolation#getInterpolationIdentifier
2851 | * @methodOf pascalprecht.translate.$translateDefaultInterpolation
2852 | *
2853 | * @description
2854 | * Returns an identifier for this interpolation service.
2855 | *
2856 | * @returns {string} $identifier
2857 | */
2858 | $translateInterpolator.getInterpolationIdentifier = function () {
2859 | return $identifier;
2860 | };
2861 |
2862 | /**
2863 | * @deprecated will be removed in 3.0
2864 | * @see {@link pascalprecht.translate.$translateSanitization}
2865 | */
2866 | $translateInterpolator.useSanitizeValueStrategy = function (value) {
2867 | $translateSanitization.useStrategy(value);
2868 | return this;
2869 | };
2870 |
2871 | /**
2872 | * @ngdoc function
2873 | * @name pascalprecht.translate.$translateDefaultInterpolation#interpolate
2874 | * @methodOf pascalprecht.translate.$translateDefaultInterpolation
2875 | *
2876 | * @description
2877 | * Interpolates given value agains given interpolate params using angulars
2878 | * `$interpolate` service.
2879 | *
2880 | * Since AngularJS 1.5, `value` must not be a string but can be anything input.
2881 | *
2882 | * @param {string} value translation
2883 | * @param {object} [interpolationParams={}] interpolation params
2884 | * @param {string} [context=undefined] current context (filter, directive, service)
2885 | * @param {string} [sanitizeStrategy=undefined] sanitize strategy (use default unless set)
2886 | * @param {string} translationId current translationId
2887 | *
2888 | * @returns {string} interpolated string
2889 | */
2890 | $translateInterpolator.interpolate = function (value, interpolationParams, context, sanitizeStrategy, translationId) { // jshint ignore:line
2891 | interpolationParams = interpolationParams || {};
2892 | interpolationParams = $translateSanitization.sanitize(interpolationParams, 'params', sanitizeStrategy, context);
2893 |
2894 | var interpolatedText;
2895 | if (angular.isNumber(value)) {
2896 | // numbers are safe
2897 | interpolatedText = '' + value;
2898 | } else if (angular.isString(value)) {
2899 | // strings must be interpolated (that's the job here)
2900 | interpolatedText = $interpolate(value)(interpolationParams);
2901 | interpolatedText = $translateSanitization.sanitize(interpolatedText, 'text', sanitizeStrategy, context);
2902 | } else {
2903 | // neither a number or a string, cant interpolate => empty string
2904 | interpolatedText = '';
2905 | }
2906 |
2907 | return interpolatedText;
2908 | };
2909 |
2910 | return $translateInterpolator;
2911 | }
2912 |
2913 | $translateDefaultInterpolation.displayName = '$translateDefaultInterpolation';
2914 |
2915 | angular.module('pascalprecht.translate').constant('$STORAGE_KEY', 'NG_TRANSLATE_LANG_KEY');
2916 |
2917 | angular.module('pascalprecht.translate')
2918 | /**
2919 | * @ngdoc directive
2920 | * @name pascalprecht.translate.directive:translate
2921 | * @requires $interpolate,
2922 | * @requires $compile,
2923 | * @requires $parse,
2924 | * @requires $rootScope
2925 | * @restrict AE
2926 | *
2927 | * @description
2928 | * Translates given translation id either through attribute or DOM content.
2929 | * Internally it uses $translate service to translate the translation id. It possible to
2930 | * pass an optional `translate-values` object literal as string into translation id.
2931 | *
2932 | * @param {string=} translate Translation id which could be either string or interpolated string.
2933 | * @param {string=} translate-values Values to pass into translation id. Can be passed as object literal string or interpolated object.
2934 | * @param {string=} translate-attr-ATTR translate Translation id and put it into ATTR attribute.
2935 | * @param {string=} translate-default will be used unless translation was successful
2936 | * @param {string=} translate-sanitize-strategy defines locally sanitize strategy
2937 | * @param {boolean=} translate-compile (default true if present) defines locally activation of {@link pascalprecht.translate.$translateProvider#methods_usePostCompiling}
2938 | * @param {boolean=} translate-keep-content (default true if present) defines that in case a KEY could not be translated, that the existing content is left in the innerHTML}
2939 | *
2940 | * @example
2941 | TRANSLATION_ID2947 | 2948 | 2949 |
{{translationId}}
2950 |
2951 | WITH_VALUES2952 | 2953 |
WITH_VALUES2954 | 2955 | 2956 | 2957 |
TRANSLATION_ID
')($rootScope); 2994 | $rootScope.$digest(); 2995 | expect(element.text()).toBe('Hello there!'); 2996 | 2997 | element = $compile('{{translationId}}
')($rootScope); 2998 | $rootScope.$digest(); 2999 | expect(element.text()).toBe('Hello there!'); 3000 | 3001 | element = $compile('')($rootScope); 3002 | $rootScope.$digest(); 3003 | expect(element.attr('title')).toBe('Hello there!'); 3004 | 3005 | element = $compile('')($rootScope); 3006 | $rootScope.$digest(); 3007 | expect(element.text()).toBe('The interpolation key is camel cased: Hello'); 3008 | }); 3009 | }); 3010 |{{ 'TRANSLATION_ID' | translate }}
3702 | {{ translationId | translate }}
3703 | {{ 'WITH_VALUES' | translate:'{value: 5}' }}
3704 | {{ 'WITH_VALUES' | translate:values }}
3705 |
3706 |