├── 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 |
2 | 3 | 4 | 5 |
6 | 7 | -------------------------------------------------------------------------------- /src/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "types": [] 6 | }, 7 | "exclude": [ 8 | "test.ts", 9 | "**/*.spec.ts" 10 | ] 11 | } 12 | -------------------------------------------------------------------------------- /src/messages.xlf: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo() { 5 | return browser.get('/'); 6 | } 7 | 8 | getParagraphText() { 9 | return element(by.css('app-root h1')).getText(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /e2e/tsconfig.e2e.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/app", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.md] 12 | max_line_length = off 13 | trim_trailing_whitespace = false 14 | -------------------------------------------------------------------------------- /src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | const routes: Routes = []; 5 | 6 | @NgModule({ 7 | imports: [RouterModule.forRoot(routes)], 8 | exports: [RouterModule] 9 | }) 10 | export class AppRoutingModule { } 11 | -------------------------------------------------------------------------------- /src/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "test.ts", 12 | "polyfills.ts" 13 | ], 14 | "include": [ 15 | "**/*.spec.ts", 16 | "**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /bower_components/angular-translate/bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-translate", 3 | "description": "A translation module for AngularJS", 4 | "version": "2.18.1", 5 | "main": "./angular-translate.js", 6 | "ignore": [], 7 | "author": "Pascal Precht", 8 | "license": "MIT", 9 | "dependencies": { 10 | "angular": ">=1.2.26 <1.8" 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | 3 | describe('workspace-project App', () => { 4 | let page: AppPage; 5 | 6 | beforeEach(() => { 7 | page = new AppPage(); 8 | }); 9 | 10 | it('should display welcome message', () => { 11 | page.navigateTo(); 12 | expect(page.getParagraphText()).toEqual('Welcome to Angular7!'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /src/app/payment-details/payment-details.component.ts: -------------------------------------------------------------------------------- 1 | import { Component, OnInit } from '@angular/core'; 2 | 3 | @Component({ 4 | selector: 'app-payment-details', 5 | templateUrl: './payment-details.component.html', 6 | styles: [] 7 | }) 8 | export class PaymentDetailsComponent implements OnInit { 9 | 10 | constructor() { } 11 | 12 | ngOnInit() { 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tslint.json", 3 | "rules": { 4 | "directive-selector": [ 5 | true, 6 | "attribute", 7 | "app", 8 | "camelCase" 9 | ], 10 | "component-selector": [ 11 | true, 12 | "element", 13 | "app", 14 | "kebab-case" 15 | ] 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /src/app/app.component.scss: -------------------------------------------------------------------------------- 1 | div 2 | { 3 | font-family: Arial, Helvetica, sans-serif; 4 | } 5 | 6 | p, .translated 7 | { 8 | background-color: #aee2f3; 9 | padding:1rem; 10 | } 11 | 12 | button { 13 | background-color: #008CBA; 14 | border: none; 15 | color: white; 16 | padding: 15px 32px; 17 | text-align: center; 18 | text-decoration: none; 19 | display: inline-block; 20 | font-size: 16px; 21 | } -------------------------------------------------------------------------------- /src/app/payment-details/payment-details.component.html: -------------------------------------------------------------------------------- 1 |
2 |

{{ 'demo.title' | translate }}

3 |
4 |
5 |
6 | 7 |
8 |
9 | 10 |
11 |
12 |
-------------------------------------------------------------------------------- /src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /src/browserslist: -------------------------------------------------------------------------------- 1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | # 5 | # For IE 9-11 support, please remove 'not' from the last line of the file and adjust as needed 6 | 7 | > 0.5% 8 | last 2 versions 9 | Firefox ESR 10 | not dead 11 | not IE 9-11 -------------------------------------------------------------------------------- /src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | input.ng-touched.ng-invalid{ 3 | border-color: #dc3545; 4 | } 5 | 6 | input.ng-valid{ 7 | border-color: #28a745; 8 | } 9 | 10 | .green-icon{ 11 | color: #28a745; 12 | } 13 | .red-icon{ 14 | color: #dc3545; 15 | } 16 | 17 | #toast-container > div { 18 | opacity:1; 19 | } 20 | 21 | table tr:hover{ 22 | cursor: pointer; 23 | } -------------------------------------------------------------------------------- /src/app/payment-details/payment-detail-list/payment-detail-list.component.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 |
{{pd.CardOwnerName}}{{pd.CardNumber}}{{pd.ExpirationDate}} 7 | 8 |
-------------------------------------------------------------------------------- /bower_components/angular/angular-csp.css: -------------------------------------------------------------------------------- 1 | /* Include this file in your html if you are using the CSP mode. */ 2 | 3 | @charset "UTF-8"; 4 | 5 | [ng\:cloak], 6 | [ng-cloak], 7 | [data-ng-cloak], 8 | [x-ng-cloak], 9 | .ng-cloak, 10 | .x-ng-cloak, 11 | .ng-hide:not(.ng-hide-animate) { 12 | display: none !important; 13 | } 14 | 15 | ng\:form { 16 | display: block; 17 | } 18 | 19 | .ng-animate-shim { 20 | visibility:hidden; 21 | } 22 | 23 | .ng-anchor { 24 | position:absolute; 25 | } 26 | -------------------------------------------------------------------------------- /appStructure.txt: -------------------------------------------------------------------------------- 1 | ● src 2 | +---● app 3 | | +--● payment-details 4 | | | |--payment-details.component.ts|.html|.css 5 | | | +--● payment-detail 6 | | | | |--payment-detail.component.ts|.html|.css 7 | | | | 8 | | | +--● payment-detail-list 9 | | | | |--payment-detail-list.component.ts|.html|.css 10 | | | | 11 | | | +--● shared 12 | | | |--payment-detail.service.ts 13 | | | |--payment-detail.model.ts 14 | | | 15 | | |--app.module.ts 16 | | 17 | |--index.html (cdn path for bootstrap & fa icons) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "module": "es2015", 9 | "moduleResolution": "node", 10 | "emitDecoratorMetadata": true, 11 | "experimentalDecorators": true, 12 | "importHelpers": true, 13 | "target": "es5", 14 | "typeRoots": [ 15 | "node_modules/@types" 16 | ], 17 | "lib": [ 18 | "es2018", 19 | "dom" 20 | ] 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /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 | "homepage": "https://github.com/angular/bower-angular", 9 | "_release": "1.7.8", 10 | "_resolution": { 11 | "type": "version", 12 | "tag": "v1.7.8", 13 | "commit": "56690b5e545ad36d04e369a65171de1adbe3ce64" 14 | }, 15 | "_source": "https://github.com/angular/bower-angular.git", 16 | "_target": ">=1.2.26 <1.8", 17 | "_originalSource": "angular" 18 | } -------------------------------------------------------------------------------- /src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import {Component, OnInit} from '@angular/core'; 2 | import {_} from '@biesbjerg/ngx-translate-extract/dist/utils/utils'; 3 | import {TranslateService} from '@ngx-translate/core'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrls: [] 9 | }) 10 | export class AppComponent { 11 | constructor(private translate: TranslateService) { 12 | translate.setDefaultLang('en'); 13 | } 14 | //fromCode = _('demo.text-in-code'); 15 | useLanguage(language: string) { 16 | this.translate.use(language); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bower_components/angular/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular", 3 | "version": "1.7.8", 4 | "description": "HTML enhanced for web apps", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/angular/angular.js.git" 12 | }, 13 | "keywords": [ 14 | "angular", 15 | "framework", 16 | "browser", 17 | "client-side" 18 | ], 19 | "author": "Angular Core Team ", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/angular/angular.js/issues" 23 | }, 24 | "homepage": "http://angularjs.org" 25 | } 26 | -------------------------------------------------------------------------------- /src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: any; 11 | 12 | // First, initialize the Angular testing environment. 13 | getTestBed().initTestEnvironment( 14 | BrowserDynamicTestingModule, 15 | platformBrowserDynamicTesting() 16 | ); 17 | // Then we find all the tests. 18 | const context = require.context('./', true, /\.spec\.ts$/); 19 | // And load the modules. 20 | context.keys().map(context); 21 | -------------------------------------------------------------------------------- /bower_components/angular-translate/README.md: -------------------------------------------------------------------------------- 1 | # angular-translate (bower shadow repository) 2 | 3 | This is the _Bower shadow_ repository for *angular-translate*. 4 | 5 | ## Bugs and issues 6 | 7 | Please file any issues and bugs in our main repository at [angular-translate/angular-translate](https://github.com/angular-translate/angular-translate/issues). 8 | 9 | ## Usage 10 | 11 | ### via Bower 12 | 13 | ```bash 14 | $ bower install angular-translate 15 | ``` 16 | 17 | ### via cdnjs 18 | 19 | Please have a look at https://cdnjs.com/libraries/angular-translate for specific versions. 20 | 21 | ## License 22 | 23 | Licensed under MIT. See more details at [angular-translate/angular-translate](https://github.com/angular-translate/angular-translate). 24 | -------------------------------------------------------------------------------- /src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /bower_components/angular-translate/.bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-translate", 3 | "description": "A translation module for AngularJS", 4 | "version": "2.18.1", 5 | "main": "./angular-translate.js", 6 | "ignore": [], 7 | "author": "Pascal Precht", 8 | "license": "MIT", 9 | "dependencies": { 10 | "angular": ">=1.2.26 <1.8" 11 | }, 12 | "homepage": "https://github.com/PascalPrecht/bower-angular-translate", 13 | "_release": "2.18.1", 14 | "_resolution": { 15 | "type": "version", 16 | "tag": "2.18.1", 17 | "commit": "a37d868d24a8aee0eabd457773e1d35297e9bd4f" 18 | }, 19 | "_source": "https://github.com/PascalPrecht/bower-angular-translate.git", 20 | "_target": "^2.18.1", 21 | "_originalSource": "angular-translate", 22 | "_direct": true 23 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events.json 15 | speed-measure-plugin.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // Protractor configuration file, see link for more information 2 | // https://github.com/angular/protractor/blob/master/lib/config.ts 3 | 4 | const { SpecReporter } = require('jasmine-spec-reporter'); 5 | 6 | exports.config = { 7 | allScriptsTimeout: 11000, 8 | specs: [ 9 | './src/**/*.e2e-spec.ts' 10 | ], 11 | capabilities: { 12 | 'browserName': 'chrome' 13 | }, 14 | directConnect: true, 15 | baseUrl: 'http://localhost:4200/', 16 | framework: 'jasmine', 17 | jasmineNodeOpts: { 18 | showColors: true, 19 | defaultTimeoutInterval: 30000, 20 | print: function() {} 21 | }, 22 | onPrepare() { 23 | require('ts-node').register({ 24 | project: require('path').join(__dirname, './tsconfig.e2e.json') 25 | }); 26 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 27 | } 28 | }; -------------------------------------------------------------------------------- /src/app/shared/payment-detail.service.ts: -------------------------------------------------------------------------------- 1 | import { PaymentDetail } from './payment-detail.model'; 2 | import { Injectable } from '@angular/core'; 3 | import { HttpClient } from "@angular/common/http"; 4 | 5 | @Injectable({ 6 | providedIn: 'root' 7 | }) 8 | export class PaymentDetailService { 9 | formData: PaymentDetail; 10 | readonly rootURL = 'http://localhost:59035/api'; 11 | list : PaymentDetail[]; 12 | 13 | constructor(private http: HttpClient) { } 14 | 15 | postPaymentDetail() { 16 | return this.http.post(this.rootURL + '/PaymentDetail', this.formData); 17 | } 18 | putPaymentDetail() { 19 | return this.http.put(this.rootURL + '/PaymentDetail/'+ this.formData.PMId, this.formData); 20 | } 21 | deletePaymentDetail(id) { 22 | return this.http.delete(this.rootURL + '/PaymentDetail/'+ id); 23 | } 24 | 25 | refreshList(){ 26 | this.http.get(this.rootURL + '/PaymentDetail') 27 | .toPromise() 28 | .then(res => this.list = res as PaymentDetail[]); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, '../coverage'), 20 | reports: ['html', 'lcovonly'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false 30 | }); 31 | }; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Angular7 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 7.3.6. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /bower_components/angular/LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Angular 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.debugElement.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'translation-demo'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.debugElement.componentInstance; 26 | expect(app.title).toEqual('translation-demo'); 27 | }); 28 | 29 | it('should render title in a h1 tag', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.debugElement.nativeElement; 33 | expect(compiled.querySelector('h1').textContent).toContain('Welcome to translation-demo!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /src/app/payment-details/payment-detail-list/payment-detail-list.component.ts: -------------------------------------------------------------------------------- 1 | import { PaymentDetail } from './../../shared/payment-detail.model'; 2 | import { PaymentDetailService } from './../../shared/payment-detail.service'; 3 | import { Component, OnInit } from '@angular/core'; 4 | import { ToastrService } from 'ngx-toastr'; 5 | 6 | @Component({ 7 | selector: 'app-payment-detail-list', 8 | templateUrl: './payment-detail-list.component.html', 9 | styles: [] 10 | }) 11 | export class PaymentDetailListComponent implements OnInit { 12 | 13 | constructor(private service: PaymentDetailService, 14 | private toastr: ToastrService) { } 15 | 16 | ngOnInit() { 17 | this.service.refreshList(); 18 | } 19 | 20 | populateForm(pd: PaymentDetail) { 21 | this.service.formData = Object.assign({}, pd); 22 | } 23 | 24 | onDelete(PMId) { 25 | if (confirm('Are you sure to delete this record ?')) { 26 | this.service.deletePaymentDetail(PMId) 27 | .subscribe(res => { 28 | debugger; 29 | this.service.refreshList(); 30 | this.toastr.warning('Deleted successfully', 'Payment Detail Register'); 31 | }, 32 | err => { 33 | debugger; 34 | console.log(err); 35 | }) 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Angular7 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular7", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~7.2.0", 15 | "@angular/common": "~7.2.0", 16 | "@angular/compiler": "~7.2.0", 17 | "@angular/core": "~7.2.0", 18 | "@angular/forms": "~7.2.0", 19 | "@angular/platform-browser": "~7.2.0", 20 | "@angular/platform-browser-dynamic": "~7.2.0", 21 | "@angular/router": "~7.2.0", 22 | "core-js": "^2.5.4", 23 | "ngx-toastr": "^10.0.2", 24 | "rxjs": "~6.3.3", 25 | "tslib": "^1.9.0", 26 | "zone.js": "~0.8.26" 27 | }, 28 | "devDependencies": { 29 | "@angular-devkit/build-angular": "~0.13.0", 30 | "@angular/cli": "~7.3.6", 31 | "@angular/compiler-cli": "~7.2.0", 32 | "@angular/language-service": "~7.2.0", 33 | "@types/node": "~8.9.4", 34 | "@types/jasmine": "~2.8.8", 35 | "@types/jasminewd2": "~2.0.3", 36 | "codelyzer": "~4.5.0", 37 | "jasmine-core": "~2.99.1", 38 | "jasmine-spec-reporter": "~4.2.1", 39 | "karma": "~4.0.0", 40 | "karma-chrome-launcher": "~2.2.0", 41 | "karma-coverage-istanbul-reporter": "~2.0.1", 42 | "karma-jasmine": "~1.1.2", 43 | "karma-jasmine-html-reporter": "^0.2.2", 44 | "protractor": "~5.4.0", 45 | "ts-node": "~7.0.0", 46 | "tslint": "~5.11.0", 47 | "typescript": "~3.2.2" 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { NgModule } from '@angular/core'; 3 | import {AppRoutingModule} from './app-routing.module'; 4 | import { FormsModule } from "@angular/forms"; 5 | import { HttpClientModule, HttpClient } from "@angular/common/http"; 6 | import { BrowserAnimationsModule } from '@angular/platform-browser/animations'; 7 | import { ToastrModule } from 'ngx-toastr'; 8 | import { AppComponent } from './app.component'; 9 | import { PaymentDetailsComponent } from './payment-details/payment-details.component'; 10 | import { PaymentDetailComponent } from './payment-details/payment-detail/payment-detail.component'; 11 | import { PaymentDetailListComponent } from './payment-details/payment-detail-list/payment-detail-list.component'; 12 | import { PaymentDetailService } from './shared/payment-detail.service'; 13 | import {TranslateLoader, TranslateModule} from '@ngx-translate/core'; 14 | import {TranslateHttpLoader} from '@ngx-translate/http-loader'; 15 | 16 | @NgModule({ 17 | declarations: [ 18 | AppComponent, 19 | PaymentDetailsComponent, 20 | PaymentDetailComponent, 21 | PaymentDetailListComponent, 22 | ], 23 | imports: [ 24 | BrowserModule, 25 | AppRoutingModule, 26 | BrowserModule, 27 | FormsModule, 28 | HttpClientModule, 29 | BrowserAnimationsModule, 30 | TranslateModule.forRoot({ 31 | loader: { 32 | provide: TranslateLoader, 33 | useFactory: HttpLoaderFactory, 34 | deps: [HttpClient] 35 | } 36 | }), 37 | ToastrModule.forRoot(), 38 | ], 39 | providers: [PaymentDetailService], 40 | bootstrap: [AppComponent] 41 | }) 42 | export class AppModule { } 43 | 44 | // required for AOT compilation 45 | export function HttpLoaderFactory(http: HttpClient) { 46 | return new TranslateHttpLoader(http); 47 | } 48 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rulesDirectory": [ 4 | "codelyzer" 5 | ], 6 | "rules": { 7 | "array-type": false, 8 | "arrow-parens": false, 9 | "deprecation": { 10 | "severity": "warn" 11 | }, 12 | "import-blacklist": [ 13 | true, 14 | "rxjs/Rx" 15 | ], 16 | "interface-name": false, 17 | "max-classes-per-file": false, 18 | "max-line-length": [ 19 | true, 20 | 140 21 | ], 22 | "member-access": false, 23 | "member-ordering": [ 24 | true, 25 | { 26 | "order": [ 27 | "static-field", 28 | "instance-field", 29 | "static-method", 30 | "instance-method" 31 | ] 32 | } 33 | ], 34 | "no-consecutive-blank-lines": false, 35 | "no-console": [ 36 | true, 37 | "debug", 38 | "info", 39 | "time", 40 | "timeEnd", 41 | "trace" 42 | ], 43 | "no-empty": false, 44 | "no-inferrable-types": [ 45 | true, 46 | "ignore-params" 47 | ], 48 | "no-non-null-assertion": true, 49 | "no-redundant-jsdoc": true, 50 | "no-switch-case-fall-through": true, 51 | "no-use-before-declare": true, 52 | "no-var-requires": false, 53 | "object-literal-key-quotes": [ 54 | true, 55 | "as-needed" 56 | ], 57 | "object-literal-sort-keys": false, 58 | "ordered-imports": false, 59 | "quotemark": [ 60 | true, 61 | "single" 62 | ], 63 | "trailing-comma": false, 64 | "no-output-on-prefix": true, 65 | "use-input-property-decorator": true, 66 | "use-output-property-decorator": true, 67 | "use-host-property-decorator": true, 68 | "no-input-rename": true, 69 | "no-output-rename": true, 70 | "use-life-cycle-interface": true, 71 | "use-pipe-transform-interface": true, 72 | "component-class-suffix": true, 73 | "directive-class-suffix": true 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/app/payment-details/payment-detail/payment-detail.component.ts: -------------------------------------------------------------------------------- 1 | import { PaymentDetailService } from './../../shared/payment-detail.service'; 2 | import { Component, OnInit } from '@angular/core'; 3 | import { NgForm } from '@angular/forms'; 4 | import { ToastrService } from 'ngx-toastr'; 5 | import {_} from '@biesbjerg/ngx-translate-extract/dist/utils/utils'; 6 | 7 | 8 | @Component({ 9 | selector: 'app-payment-detail', 10 | templateUrl: './payment-detail.component.html', 11 | styles: [] 12 | }) 13 | export class PaymentDetailComponent implements OnInit { 14 | 15 | constructor(private service: PaymentDetailService, 16 | private toastr: ToastrService) { } 17 | 18 | ngOnInit() { 19 | this.resetForm(); 20 | } 21 | 22 | 23 | resetForm(form?: NgForm) { 24 | if (form != null) 25 | form.form.reset(); 26 | this.service.formData = { 27 | PMId: 0, 28 | CardOwnerName: '', 29 | CardNumber: '', 30 | ExpirationDate: '', 31 | CVV: '' 32 | } 33 | } 34 | 35 | onSubmit(form: NgForm) { 36 | if (this.service.formData.PMId == 0) 37 | this.insertRecord(form); 38 | else 39 | this.updateRecord(form); 40 | } 41 | 42 | insertRecord(form: NgForm) { 43 | this.service.postPaymentDetail().subscribe( 44 | res => { 45 | debugger; 46 | this.resetForm(form); 47 | this.toastr.success('Submitted successfully', 'Payment Detail Register'); 48 | this.service.refreshList(); 49 | }, 50 | err => { 51 | debugger; 52 | console.log(err); 53 | } 54 | ) 55 | } 56 | updateRecord(form: NgForm) { 57 | this.service.putPaymentDetail().subscribe( 58 | res => { 59 | this.resetForm(form); 60 | this.toastr.info('Submitted successfully', 'Payment Detail Register'); 61 | this.service.refreshList(); 62 | }, 63 | err => { 64 | console.log(err); 65 | } 66 | ) 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /bower_components/angular/README.md: -------------------------------------------------------------------------------- 1 | # packaged angular 2 | 3 | This repo is for distribution on `npm` and `bower`. The source for this module is in the 4 | [main AngularJS repo](https://github.com/angular/angular.js). 5 | Please file issues and pull requests against that repo. 6 | 7 | ## Install 8 | 9 | You can install this package either with `npm` or with `bower`. 10 | 11 | ### npm 12 | 13 | ```shell 14 | npm install angular 15 | ``` 16 | 17 | Then add a ` 21 | ``` 22 | 23 | Or `require('angular')` from your code. 24 | 25 | ### bower 26 | 27 | ```shell 28 | bower install angular 29 | ``` 30 | 31 | Then add a ` 35 | ``` 36 | 37 | ## Documentation 38 | 39 | Documentation is available on the 40 | [AngularJS docs site](http://docs.angularjs.org/). 41 | 42 | ## License 43 | 44 | The MIT License 45 | 46 | Copyright (c) 2010-2015 Google, Inc. http://angularjs.org 47 | 48 | Permission is hereby granted, free of charge, to any person obtaining a copy 49 | of this software and associated documentation files (the "Software"), to deal 50 | in the Software without restriction, including without limitation the rights 51 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 52 | copies of the Software, and to permit persons to whom the Software is 53 | furnished to do so, subject to the following conditions: 54 | 55 | The above copyright notice and this permission notice shall be included in 56 | all copies or substantial portions of the Software. 57 | 58 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 59 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 60 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 61 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 62 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 63 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 64 | THE SOFTWARE. 65 | -------------------------------------------------------------------------------- /src/app/payment-details/payment-detail/payment-detail.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 |
4 |
5 |
6 |
7 |
8 |
9 | 10 |
11 |
12 | 14 |
15 |
16 |
17 |
18 |
19 |
20 | 21 |
22 |
23 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | 33 |
34 |
35 | 37 |
38 |
39 |
40 |
41 |
42 |
43 | 44 |
45 |
46 | 48 |
49 |
50 |
51 |
52 | 53 |
54 |
-------------------------------------------------------------------------------- /src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE9, IE10 and IE11 requires all of the following polyfills. **/ 22 | // import 'core-js/es6/symbol'; 23 | // import 'core-js/es6/object'; 24 | // import 'core-js/es6/function'; 25 | // import 'core-js/es6/parse-int'; 26 | // import 'core-js/es6/parse-float'; 27 | // import 'core-js/es6/number'; 28 | // import 'core-js/es6/math'; 29 | // import 'core-js/es6/string'; 30 | // import 'core-js/es6/date'; 31 | // import 'core-js/es6/array'; 32 | // import 'core-js/es6/regexp'; 33 | // import 'core-js/es6/map'; 34 | // import 'core-js/es6/weak-map'; 35 | // import 'core-js/es6/set'; 36 | 37 | /** 38 | * If the application will be indexed by Google Search, the following is required. 39 | * Googlebot uses a renderer based on Chrome 41. 40 | * https://developers.google.com/search/docs/guides/rendering 41 | **/ 42 | // import 'core-js/es6/array'; 43 | 44 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 45 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 46 | 47 | /** IE10 and IE11 requires the following for the Reflect API. */ 48 | // import 'core-js/es6/reflect'; 49 | 50 | /** 51 | * Web Animations `@angular/platform-browser/animations` 52 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 53 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 54 | **/ 55 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 56 | 57 | /** 58 | * By default, zone.js will patch all possible macroTask and DomEvents 59 | * user can disable parts of macroTask/DomEvents patch by setting following flags 60 | */ 61 | 62 | // (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 63 | // (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 64 | // (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 65 | 66 | /* 67 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 68 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 69 | */ 70 | // (window as any).__Zone_enable_cross_context_check = true; 71 | 72 | /*************************************************************************************************** 73 | * Zone JS is required by default for Angular itself. 74 | */ 75 | import 'zone.js/dist/zone'; // Included with Angular CLI. 76 | 77 | 78 | /*************************************************************************************************** 79 | * APPLICATION IMPORTS 80 | */ 81 | -------------------------------------------------------------------------------- /angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "Angular7": { 7 | "root": "", 8 | "sourceRoot": "src", 9 | "projectType": "application", 10 | "prefix": "app", 11 | "schematics": {}, 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/Angular7", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "src/tsconfig.app.json", 21 | "assets": [ 22 | "src/favicon.ico", 23 | "src/assets" 24 | ], 25 | "styles": [ 26 | "src/styles.css" 27 | ], 28 | "scripts": [], 29 | "es5BrowserSupport": true 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "aot": true, 45 | "extractLicenses": true, 46 | "vendorChunk": false, 47 | "buildOptimizer": true, 48 | "budgets": [ 49 | { 50 | "type": "initial", 51 | "maximumWarning": "2mb", 52 | "maximumError": "5mb" 53 | } 54 | ] 55 | } 56 | } 57 | }, 58 | "serve": { 59 | "builder": "@angular-devkit/build-angular:dev-server", 60 | "options": { 61 | "browserTarget": "Angular7:build" 62 | }, 63 | "configurations": { 64 | "production": { 65 | "browserTarget": "Angular7:build:production" 66 | } 67 | } 68 | }, 69 | "extract-i18n": { 70 | "builder": "@angular-devkit/build-angular:extract-i18n", 71 | "options": { 72 | "browserTarget": "Angular7:build" 73 | } 74 | }, 75 | "test": { 76 | "builder": "@angular-devkit/build-angular:karma", 77 | "options": { 78 | "main": "src/test.ts", 79 | "polyfills": "src/polyfills.ts", 80 | "tsConfig": "src/tsconfig.spec.json", 81 | "karmaConfig": "src/karma.conf.js", 82 | "styles": [ 83 | "src/styles.css" 84 | ], 85 | "scripts": [], 86 | "assets": [ 87 | "src/favicon.ico", 88 | "src/assets" 89 | ] 90 | } 91 | }, 92 | "lint": { 93 | "builder": "@angular-devkit/build-angular:tslint", 94 | "options": { 95 | "tsConfig": [ 96 | "src/tsconfig.app.json", 97 | "src/tsconfig.spec.json" 98 | ], 99 | "exclude": [ 100 | "**/node_modules/**" 101 | ] 102 | } 103 | } 104 | } 105 | }, 106 | "Angular7-e2e": { 107 | "root": "e2e/", 108 | "projectType": "application", 109 | "prefix": "", 110 | "architect": { 111 | "e2e": { 112 | "builder": "@angular-devkit/build-angular:protractor", 113 | "options": { 114 | "protractorConfig": "e2e/protractor.conf.js", 115 | "devServerTarget": "Angular7:serve" 116 | }, 117 | "configurations": { 118 | "production": { 119 | "devServerTarget": "Angular7:serve:production" 120 | } 121 | } 122 | }, 123 | "lint": { 124 | "builder": "@angular-devkit/build-angular:tslint", 125 | "options": { 126 | "tsConfig": "e2e/tsconfig.e2e.json", 127 | "exclude": [ 128 | "**/node_modules/**" 129 | ] 130 | } 131 | } 132 | } 133 | } 134 | }, 135 | "defaultProject": "Angular7" 136 | } -------------------------------------------------------------------------------- /translations.babel: -------------------------------------------------------------------------------- 1 | 2 | 3 | ngx-translate 4 | Translations.babel 5 | 6 | 7 | 8 | 9 | demo 10 | 11 | 12 | greeting 13 | false 14 | 15 | 16 | 17 | 18 | 19 | de-DE 20 | false 21 | 22 | 23 | en-US 24 | false 25 | 26 | 27 | 28 | 29 | text 30 | false 31 | 32 | 33 | 34 | 35 | 36 | de-DE 37 | false 38 | 39 | 40 | en-US 41 | false 42 | 43 | 44 | 45 | 46 | text-in-code 47 | false 48 | 49 | 50 | 51 | 52 | 53 | de-DE 54 | false 55 | 56 | 57 | en-US 58 | false 59 | 60 | 61 | 62 | 63 | title 64 | false 65 | 66 | 67 | 68 | 69 | 70 | de-DE 71 | false 72 | 73 | 74 | en-US 75 | false 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | en-US 86 | src/assets/i18n/en.json 87 | 88 | 89 | de-DE 90 | src/assets/i18n/de.json 91 | 92 | 93 | 94 | 95 | {{'%1' | translate}} 96 | [translate]="'%1'" 97 | _('%1') 98 | 99 | 100 | 101 | 102 | tab 103 | json 104 | 105 | 106 | -------------------------------------------------------------------------------- /bower_components/angular-translate/angular-translate.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * angular-translate - v2.18.1 - 2018-05-19 3 | * 4 | * Copyright (c) 2018 The angular-translate team, Pascal Precht; Licensed MIT 5 | */ 6 | !function(t,e){"function"==typeof define&&define.amd?define([],function(){return e()}):"object"==typeof module&&module.exports?module.exports=e():e()}(0,function(){function t(e){"use strict";var n=e.storageKey(),a=e.storage(),t=function(){var t=e.preferredLanguage();angular.isString(t)?e.use(t):a.put(n,e.use())};t.displayName="fallbackFromIncorrectStorageValue",a?a.get(n)?e.use(a.get(n)).catch(t):t():angular.isString(e.preferredLanguage())&&e.use(e.preferredLanguage())}function e(t,r,e,i){"use strict";var z,c,T,x,F,I,_,n,V,R,D,K,U,M,H,G,q={},Y=[],B=t,J=[],Q="translate-cloak",W=!1,X=!1,Z=".",tt=!1,et=!1,nt=0,at=!0,a="default",s={default:function(t){return(t||"").split("-").join("_")},java:function(t){var e=(t||"").split("-").join("_"),n=e.split("_");return 1");return e.text(t),e.html()},i=function(t){if(!n)throw new Error("pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as 'escape'.");return n(t)},s=function(t){if(!a)throw new Error("pascalprecht.translate.$translateSanitization: Error cannot find $sce service.");return a.trustAsHtml(t)},o=function(t,n,a){if(angular.isDate(t))return t;if(angular.isObject(t)){var r=angular.isArray(t)?[]:{};if(a){if(-1 110 | *
sanitize
111 | *
Sanitizes HTML in the translation text using $sanitize
112 | *
escape
113 | *
Escapes HTML in the translation
114 | *
sanitizeParameters
115 | *
Sanitizes HTML in the values of the interpolation parameters using $sanitize
116 | *
escapeParameters
117 | *
Escapes HTML in the values of the interpolation parameters
118 | *
escaped
119 | *
Support legacy strategy name 'escaped' for backwards compatibility (will be removed in 3.0)
120 | * 121 | * 122 | */ 123 | 124 | strategies = { 125 | sanitize: function (value, mode/*, context*/) { 126 | if (mode === 'text') { 127 | value = htmlSanitizeValue(value); 128 | } 129 | return value; 130 | }, 131 | escape: function (value, mode/*, context*/) { 132 | if (mode === 'text') { 133 | value = htmlEscapeValue(value); 134 | } 135 | return value; 136 | }, 137 | sanitizeParameters: function (value, mode/*, context*/) { 138 | if (mode === 'params') { 139 | value = mapInterpolationParameters(value, htmlSanitizeValue); 140 | } 141 | return value; 142 | }, 143 | escapeParameters: function (value, mode/*, context*/) { 144 | if (mode === 'params') { 145 | value = mapInterpolationParameters(value, htmlEscapeValue); 146 | } 147 | return value; 148 | }, 149 | sce: function (value, mode, context) { 150 | if (mode === 'text') { 151 | value = htmlTrustValue(value); 152 | } else if (mode === 'params') { 153 | if (context !== 'filter') { 154 | // do html escape in filter context #1101 155 | value = mapInterpolationParameters(value, htmlEscapeValue); 156 | } 157 | } 158 | return value; 159 | }, 160 | sceParameters: function (value, mode/*, context*/) { 161 | if (mode === 'params') { 162 | value = mapInterpolationParameters(value, htmlTrustValue); 163 | } 164 | return value; 165 | } 166 | }; 167 | // Support legacy strategy name 'escaped' for backwards compatibility. 168 | // TODO should be removed in 3.0 169 | strategies.escaped = strategies.escapeParameters; 170 | 171 | /** 172 | * @ngdoc function 173 | * @name pascalprecht.translate.$translateSanitizationProvider#addStrategy 174 | * @methodOf pascalprecht.translate.$translateSanitizationProvider 175 | * 176 | * @description 177 | * Adds a sanitization strategy to the list of known strategies. 178 | * 179 | * @param {string} strategyName - unique key for a strategy 180 | * @param {StrategyFunction} strategyFunction - strategy function 181 | * @returns {object} this 182 | */ 183 | this.addStrategy = function (strategyName, strategyFunction) { 184 | strategies[strategyName] = strategyFunction; 185 | return this; 186 | }; 187 | 188 | /** 189 | * @ngdoc function 190 | * @name pascalprecht.translate.$translateSanitizationProvider#removeStrategy 191 | * @methodOf pascalprecht.translate.$translateSanitizationProvider 192 | * 193 | * @description 194 | * Removes a sanitization strategy from the list of known strategies. 195 | * 196 | * @param {string} strategyName - unique key for a strategy 197 | * @returns {object} this 198 | */ 199 | this.removeStrategy = function (strategyName) { 200 | delete strategies[strategyName]; 201 | return this; 202 | }; 203 | 204 | /** 205 | * @ngdoc function 206 | * @name pascalprecht.translate.$translateSanitizationProvider#useStrategy 207 | * @methodOf pascalprecht.translate.$translateSanitizationProvider 208 | * 209 | * @description 210 | * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. 211 | * 212 | * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. 213 | * @returns {object} this 214 | */ 215 | this.useStrategy = function (strategy) { 216 | hasConfiguredStrategy = true; 217 | currentStrategy = strategy; 218 | return this; 219 | }; 220 | 221 | /** 222 | * @ngdoc object 223 | * @name pascalprecht.translate.$translateSanitization 224 | * @requires $injector 225 | * @requires $log 226 | * 227 | * @description 228 | * Sanitizes interpolation parameters and translated texts. 229 | * 230 | */ 231 | this.$get = ['$injector', '$log', function ($injector, $log) { 232 | 233 | var cachedStrategyMap = {}; 234 | 235 | var applyStrategies = function (value, mode, context, selectedStrategies) { 236 | angular.forEach(selectedStrategies, function (selectedStrategy) { 237 | if (angular.isFunction(selectedStrategy)) { 238 | value = selectedStrategy(value, mode, context); 239 | } else if (angular.isFunction(strategies[selectedStrategy])) { 240 | value = strategies[selectedStrategy](value, mode, context); 241 | } else if (angular.isString(strategies[selectedStrategy])) { 242 | if (!cachedStrategyMap[strategies[selectedStrategy]]) { 243 | try { 244 | cachedStrategyMap[strategies[selectedStrategy]] = $injector.get(strategies[selectedStrategy]); 245 | } catch (e) { 246 | cachedStrategyMap[strategies[selectedStrategy]] = function() {}; 247 | throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); 248 | } 249 | } 250 | value = cachedStrategyMap[strategies[selectedStrategy]](value, mode, context); 251 | } else { 252 | throw new Error('pascalprecht.translate.$translateSanitization: Unknown sanitization strategy: \'' + selectedStrategy + '\''); 253 | } 254 | }); 255 | return value; 256 | }; 257 | 258 | // TODO: should be removed in 3.0 259 | var showNoStrategyConfiguredWarning = function () { 260 | if (!hasConfiguredStrategy && !hasShownNoStrategyConfiguredWarning) { 261 | $log.warn('pascalprecht.translate.$translateSanitization: No sanitization strategy has been configured. This can have serious security implications. See http://angular-translate.github.io/docs/#/guide/19_security for details.'); 262 | hasShownNoStrategyConfiguredWarning = true; 263 | } 264 | }; 265 | 266 | if ($injector.has('$sanitize')) { 267 | $sanitize = $injector.get('$sanitize'); 268 | } 269 | if ($injector.has('$sce')) { 270 | $sce = $injector.get('$sce'); 271 | } 272 | 273 | return { 274 | /** 275 | * @ngdoc function 276 | * @name pascalprecht.translate.$translateSanitization#useStrategy 277 | * @methodOf pascalprecht.translate.$translateSanitization 278 | * 279 | * @description 280 | * Selects a sanitization strategy. When an array is provided the strategies will be executed in order. 281 | * 282 | * @param {string|StrategyFunction|array} strategy The sanitization strategy / strategies which should be used. Either a name of an existing strategy, a custom strategy function, or an array consisting of multiple names and / or custom functions. 283 | */ 284 | useStrategy: (function (self) { 285 | return function (strategy) { 286 | self.useStrategy(strategy); 287 | }; 288 | })(this), 289 | 290 | /** 291 | * @ngdoc function 292 | * @name pascalprecht.translate.$translateSanitization#sanitize 293 | * @methodOf pascalprecht.translate.$translateSanitization 294 | * 295 | * @description 296 | * Sanitizes a value. 297 | * 298 | * @param {string|object} value The value which should be sanitized. 299 | * @param {string} mode The current sanitization mode, either 'params' or 'text'. 300 | * @param {string|StrategyFunction|array} [strategy] Optional custom strategy which should be used instead of the currently selected strategy. 301 | * @param {string} [context] The context of this call: filter, service. Default is service 302 | * @returns {string|object} sanitized value 303 | */ 304 | sanitize: function (value, mode, strategy, context) { 305 | if (!currentStrategy) { 306 | showNoStrategyConfiguredWarning(); 307 | } 308 | 309 | if (!strategy && strategy !== null) { 310 | strategy = currentStrategy; 311 | } 312 | 313 | if (!strategy) { 314 | return value; 315 | } 316 | 317 | if (!context) { 318 | context = 'service'; 319 | } 320 | 321 | var selectedStrategies = angular.isArray(strategy) ? strategy : [strategy]; 322 | return applyStrategies(value, mode, context, selectedStrategies); 323 | } 324 | }; 325 | }]; 326 | 327 | var htmlEscapeValue = function (value) { 328 | var element = angular.element('
'); 329 | element.text(value); // not chainable, see #1044 330 | return element.html(); 331 | }; 332 | 333 | var htmlSanitizeValue = function (value) { 334 | if (!$sanitize) { 335 | throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sanitize service. Either include the ngSanitize module (https://docs.angularjs.org/api/ngSanitize) or use a sanitization strategy which does not depend on $sanitize, such as \'escape\'.'); 336 | } 337 | return $sanitize(value); 338 | }; 339 | 340 | var htmlTrustValue = function (value) { 341 | if (!$sce) { 342 | throw new Error('pascalprecht.translate.$translateSanitization: Error cannot find $sce service.'); 343 | } 344 | return $sce.trustAsHtml(value); 345 | }; 346 | 347 | var mapInterpolationParameters = function (value, iteratee, stack) { 348 | if (angular.isDate(value)) { 349 | return value; 350 | } else if (angular.isObject(value)) { 351 | var result = angular.isArray(value) ? [] : {}; 352 | 353 | if (!stack) { 354 | stack = []; 355 | } else { 356 | if (stack.indexOf(value) > -1) { 357 | throw new Error('pascalprecht.translate.$translateSanitization: Error cannot interpolate parameter due recursive object'); 358 | } 359 | } 360 | 361 | stack.push(value); 362 | angular.forEach(value, function (propertyValue, propertyKey) { 363 | 364 | /* Skipping function properties. */ 365 | if (angular.isFunction(propertyValue)) { 366 | return; 367 | } 368 | 369 | result[propertyKey] = mapInterpolationParameters(propertyValue, iteratee, stack); 370 | }); 371 | stack.splice(-1, 1); // remove last 372 | 373 | return result; 374 | } else if (angular.isNumber(value)) { 375 | return value; 376 | } else if (value === true || value === false) { 377 | return value; 378 | } else if (!angular.isUndefined(value) && value !== null) { 379 | return iteratee(value); 380 | } else { 381 | return value; 382 | } 383 | }; 384 | } 385 | 386 | /** 387 | * @ngdoc object 388 | * @name pascalprecht.translate.$translateProvider 389 | * @description 390 | * 391 | * $translateProvider allows developers to register translation-tables, asynchronous loaders 392 | * and similar to configure translation behavior directly inside of a module. 393 | * 394 | */ 395 | angular.module('pascalprecht.translate') 396 | .constant('pascalprechtTranslateOverrider', {}) 397 | .provider('$translate', $translate); 398 | 399 | function $translate($STORAGE_KEY, $windowProvider, $translateSanitizationProvider, pascalprechtTranslateOverrider) { 400 | 401 | 'use strict'; 402 | 403 | var $translationTable = {}, 404 | $preferredLanguage, 405 | $availableLanguageKeys = [], 406 | $languageKeyAliases, 407 | $fallbackLanguage, 408 | $fallbackWasString, 409 | $uses, 410 | $nextLang, 411 | $storageFactory, 412 | $storageKey = $STORAGE_KEY, 413 | $storagePrefix, 414 | $missingTranslationHandlerFactory, 415 | $interpolationFactory, 416 | $interpolatorFactories = [], 417 | $loaderFactory, 418 | $cloakClassName = 'translate-cloak', 419 | $loaderOptions, 420 | $notFoundIndicatorLeft, 421 | $notFoundIndicatorRight, 422 | $postCompilingEnabled = false, 423 | $forceAsyncReloadEnabled = false, 424 | $nestedObjectDelimeter = '.', 425 | $isReady = false, 426 | $keepContent = false, 427 | loaderCache, 428 | directivePriority = 0, 429 | statefulFilter = true, 430 | postProcessFn, 431 | uniformLanguageTagResolver = 'default', 432 | languageTagResolver = { 433 | 'default' : function (tag) { 434 | return (tag || '').split('-').join('_'); 435 | }, 436 | java : function (tag) { 437 | var temp = (tag || '').split('-').join('_'); 438 | var parts = temp.split('_'); 439 | return parts.length > 1 ? (parts[0].toLowerCase() + '_' + parts[1].toUpperCase()) : temp; 440 | }, 441 | bcp47 : function (tag) { 442 | var temp = (tag || '').split('_').join('-'); 443 | var parts = temp.split('-'); 444 | 445 | switch (parts.length) { 446 | case 1: // language only 447 | parts[0] = parts[0].toLowerCase(); 448 | break; 449 | case 2: // language-script or language-region 450 | parts[0] = parts[0].toLowerCase(); 451 | if (parts[1].length === 4) { // parts[1] is script 452 | parts[1] = parts[1].charAt(0).toUpperCase() + parts[1].slice(1).toLowerCase(); 453 | } else { // parts[1] is region 454 | parts[1] = parts[1].toUpperCase(); 455 | } 456 | break; 457 | case 3: // language-script-region 458 | parts[0] = parts[0].toLowerCase(); 459 | parts[1] = parts[1].charAt(0).toUpperCase() + parts[1].slice(1).toLowerCase(); 460 | parts[2] = parts[2].toUpperCase(); 461 | break; 462 | default: 463 | return temp; 464 | } 465 | 466 | return parts.join('-'); 467 | }, 468 | 'iso639-1' : function (tag) { 469 | var temp = (tag || '').split('_').join('-'); 470 | var parts = temp.split('-'); 471 | return parts[0].toLowerCase(); 472 | } 473 | }; 474 | 475 | var version = '2.18.1'; 476 | 477 | // tries to determine the browsers language 478 | var getFirstBrowserLanguage = function () { 479 | 480 | // internal purpose only 481 | if (angular.isFunction(pascalprechtTranslateOverrider.getLocale)) { 482 | return pascalprechtTranslateOverrider.getLocale(); 483 | } 484 | 485 | var nav = $windowProvider.$get().navigator, 486 | browserLanguagePropertyKeys = ['language', 'browserLanguage', 'systemLanguage', 'userLanguage'], 487 | i, 488 | language; 489 | 490 | // support for HTML 5.1 "navigator.languages" 491 | if (angular.isArray(nav.languages)) { 492 | for (i = 0; i < nav.languages.length; i++) { 493 | language = nav.languages[i]; 494 | if (language && language.length) { 495 | return language; 496 | } 497 | } 498 | } 499 | 500 | // support for other well known properties in browsers 501 | for (i = 0; i < browserLanguagePropertyKeys.length; i++) { 502 | language = nav[browserLanguagePropertyKeys[i]]; 503 | if (language && language.length) { 504 | return language; 505 | } 506 | } 507 | 508 | return null; 509 | }; 510 | getFirstBrowserLanguage.displayName = 'angular-translate/service: getFirstBrowserLanguage'; 511 | 512 | // tries to determine the browsers locale 513 | var getLocale = function () { 514 | var locale = getFirstBrowserLanguage() || ''; 515 | if (languageTagResolver[uniformLanguageTagResolver]) { 516 | locale = languageTagResolver[uniformLanguageTagResolver](locale); 517 | } 518 | return locale; 519 | }; 520 | getLocale.displayName = 'angular-translate/service: getLocale'; 521 | 522 | /** 523 | * @name indexOf 524 | * @private 525 | * 526 | * @description 527 | * indexOf polyfill. Kinda sorta. 528 | * 529 | * @param {array} array Array to search in. 530 | * @param {string} searchElement Element to search for. 531 | * 532 | * @returns {int} Index of search element. 533 | */ 534 | var indexOf = function (array, searchElement) { 535 | for (var i = 0, len = array.length; i < len; i++) { 536 | if (array[i] === searchElement) { 537 | return i; 538 | } 539 | } 540 | return -1; 541 | }; 542 | 543 | /** 544 | * @name trim 545 | * @private 546 | * 547 | * @description 548 | * trim polyfill 549 | * 550 | * @returns {string} The string stripped of whitespace from both ends 551 | */ 552 | var trim = function () { 553 | return this.toString().replace(/^\s+|\s+$/g, ''); 554 | }; 555 | 556 | /** 557 | * @name lowercase 558 | * @private 559 | * 560 | * @description 561 | * Return the lowercase string only if the type is string 562 | * 563 | * @returns {string} The string all in lowercase 564 | */ 565 | var lowercase = function (string) { 566 | return angular.isString(string) ? string.toLowerCase() : string; 567 | }; 568 | 569 | var negotiateLocale = function (preferred) { 570 | if (!preferred) { 571 | return; 572 | } 573 | 574 | var avail = [], 575 | locale = lowercase(preferred), 576 | i = 0, 577 | n = $availableLanguageKeys.length; 578 | 579 | for (; i < n; i++) { 580 | avail.push(lowercase($availableLanguageKeys[i])); 581 | } 582 | 583 | // Check for an exact match in our list of available keys 584 | i = indexOf(avail, locale); 585 | if (i > -1) { 586 | return $availableLanguageKeys[i]; 587 | } 588 | 589 | if ($languageKeyAliases) { 590 | var alias; 591 | for (var langKeyAlias in $languageKeyAliases) { 592 | if ($languageKeyAliases.hasOwnProperty(langKeyAlias)) { 593 | var hasWildcardKey = false; 594 | var hasExactKey = Object.prototype.hasOwnProperty.call($languageKeyAliases, langKeyAlias) && 595 | lowercase(langKeyAlias) === lowercase(preferred); 596 | 597 | if (langKeyAlias.slice(-1) === '*') { 598 | hasWildcardKey = lowercase(langKeyAlias.slice(0, -1)) === lowercase(preferred.slice(0, langKeyAlias.length - 1)); 599 | } 600 | if (hasExactKey || hasWildcardKey) { 601 | alias = $languageKeyAliases[langKeyAlias]; 602 | if (indexOf(avail, lowercase(alias)) > -1) { 603 | return alias; 604 | } 605 | } 606 | } 607 | } 608 | } 609 | 610 | // Check for a language code without region 611 | var parts = preferred.split('_'); 612 | 613 | if (parts.length > 1 && indexOf(avail, lowercase(parts[0])) > -1) { 614 | return parts[0]; 615 | } 616 | 617 | // If everything fails, return undefined. 618 | return; 619 | }; 620 | 621 | /** 622 | * @ngdoc function 623 | * @name pascalprecht.translate.$translateProvider#translations 624 | * @methodOf pascalprecht.translate.$translateProvider 625 | * 626 | * @description 627 | * Registers a new translation table for specific language key. 628 | * 629 | * To register a translation table for specific language, pass a defined language 630 | * key as first parameter. 631 | * 632 | *
 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 | 2942 | 2943 |
2944 | 2945 |

2946 |         
TRANSLATION_ID
2947 |

2948 |         

2949 |         
{{translationId}}
2950 |

2951 |         
WITH_VALUES
2952 |

2953 |         
WITH_VALUES
2954 |

2955 |         

2956 | 
2957 |       
2958 |
2959 | 2960 | angular.module('ngView', ['pascalprecht.translate']) 2961 | 2962 | .config(function ($translateProvider) { 2963 | 2964 | $translateProvider.translations('en',{ 2965 | 'TRANSLATION_ID': 'Hello there!', 2966 | 'WITH_VALUES': 'The following value is dynamic: {{value}}', 2967 | 'WITH_CAMEL_CASE_KEY': 'The interpolation key is camel cased: {{camelCaseKey}}' 2968 | }).preferredLanguage('en'); 2969 | 2970 | }); 2971 | 2972 | angular.module('ngView').controller('TranslateCtrl', function ($scope) { 2973 | $scope.translationId = 'TRANSLATION_ID'; 2974 | 2975 | $scope.values = { 2976 | value: 78 2977 | }; 2978 | }); 2979 | 2980 | 2981 | it('should translate', function () { 2982 | inject(function ($rootScope, $compile) { 2983 | $rootScope.translationId = 'TRANSLATION_ID'; 2984 | 2985 | element = $compile('

')($rootScope); 2986 | $rootScope.$digest(); 2987 | expect(element.text()).toBe('Hello there!'); 2988 | 2989 | element = $compile('

')($rootScope); 2990 | $rootScope.$digest(); 2991 | expect(element.text()).toBe('Hello there!'); 2992 | 2993 | element = $compile('

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 |
3011 |
3012 | */ 3013 | .directive('translate', translateDirective); 3014 | function translateDirective($translate, $interpolate, $compile, $parse, $rootScope) { 3015 | 3016 | 'use strict'; 3017 | 3018 | /** 3019 | * @name trim 3020 | * @private 3021 | * 3022 | * @description 3023 | * trim polyfill 3024 | * 3025 | * @returns {string} The string stripped of whitespace from both ends 3026 | */ 3027 | var trim = function() { 3028 | return this.toString().replace(/^\s+|\s+$/g, ''); 3029 | }; 3030 | 3031 | /** 3032 | * @name lowercase 3033 | * @private 3034 | * 3035 | * @description 3036 | * Return the lowercase string only if the type is string 3037 | * 3038 | * @returns {string} The string all in lowercase 3039 | */ 3040 | var lowercase = function (string) { 3041 | return angular.isString(string) ? string.toLowerCase() : string; 3042 | }; 3043 | 3044 | return { 3045 | restrict: 'AE', 3046 | scope: true, 3047 | priority: $translate.directivePriority(), 3048 | compile: function (tElement, tAttr) { 3049 | 3050 | var translateValuesExist = (tAttr.translateValues) ? 3051 | tAttr.translateValues : undefined; 3052 | 3053 | var translateInterpolation = (tAttr.translateInterpolation) ? 3054 | tAttr.translateInterpolation : undefined; 3055 | 3056 | var translateSanitizeStrategyExist = (tAttr.translateSanitizeStrategy) ? 3057 | tAttr.translateSanitizeStrategy : undefined; 3058 | 3059 | var translateValueExist = tElement[0].outerHTML.match(/translate-value-+/i); 3060 | 3061 | var interpolateRegExp = '^(.*)(' + $interpolate.startSymbol() + '.*' + $interpolate.endSymbol() + ')(.*)', 3062 | watcherRegExp = '^(.*)' + $interpolate.startSymbol() + '(.*)' + $interpolate.endSymbol() + '(.*)'; 3063 | 3064 | return function linkFn(scope, iElement, iAttr) { 3065 | 3066 | scope.interpolateParams = {}; 3067 | scope.preText = ''; 3068 | scope.postText = ''; 3069 | scope.translateNamespace = getTranslateNamespace(scope); 3070 | var translationIds = {}; 3071 | 3072 | var initInterpolationParams = function (interpolateParams, iAttr, tAttr) { 3073 | // initial setup 3074 | if (iAttr.translateValues) { 3075 | angular.extend(interpolateParams, $parse(iAttr.translateValues)(scope.$parent)); 3076 | } 3077 | // initially fetch all attributes if existing and fill the params 3078 | if (translateValueExist) { 3079 | for (var attr in tAttr) { 3080 | if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { 3081 | var attributeName = lowercase(attr.substr(14, 1)) + attr.substr(15); 3082 | interpolateParams[attributeName] = tAttr[attr]; 3083 | } 3084 | } 3085 | } 3086 | }; 3087 | 3088 | // Ensures any change of the attribute "translate" containing the id will 3089 | // be re-stored to the scope's "translationId". 3090 | // If the attribute has no content, the element's text value (white spaces trimmed off) will be used. 3091 | var observeElementTranslation = function (translationId) { 3092 | 3093 | // Remove any old watcher 3094 | if (angular.isFunction(observeElementTranslation._unwatchOld)) { 3095 | observeElementTranslation._unwatchOld(); 3096 | observeElementTranslation._unwatchOld = undefined; 3097 | } 3098 | 3099 | if (angular.equals(translationId , '') || !angular.isDefined(translationId)) { 3100 | var iElementText = trim.apply(iElement.text()); 3101 | 3102 | // Resolve translation id by inner html if required 3103 | var interpolateMatches = iElementText.match(interpolateRegExp); 3104 | // Interpolate translation id if required 3105 | if (angular.isArray(interpolateMatches)) { 3106 | scope.preText = interpolateMatches[1]; 3107 | scope.postText = interpolateMatches[3]; 3108 | translationIds.translate = $interpolate(interpolateMatches[2])(scope.$parent); 3109 | var watcherMatches = iElementText.match(watcherRegExp); 3110 | if (angular.isArray(watcherMatches) && watcherMatches[2] && watcherMatches[2].length) { 3111 | observeElementTranslation._unwatchOld = scope.$watch(watcherMatches[2], function (newValue) { 3112 | translationIds.translate = newValue; 3113 | updateTranslations(); 3114 | }); 3115 | } 3116 | } else { 3117 | // do not assigne the translation id if it is empty. 3118 | translationIds.translate = !iElementText ? undefined : iElementText; 3119 | } 3120 | } else { 3121 | translationIds.translate = translationId; 3122 | } 3123 | updateTranslations(); 3124 | }; 3125 | 3126 | var observeAttributeTranslation = function (translateAttr) { 3127 | iAttr.$observe(translateAttr, function (translationId) { 3128 | translationIds[translateAttr] = translationId; 3129 | updateTranslations(); 3130 | }); 3131 | }; 3132 | 3133 | // initial setup with values 3134 | initInterpolationParams(scope.interpolateParams, iAttr, tAttr); 3135 | 3136 | var firstAttributeChangedEvent = true; 3137 | iAttr.$observe('translate', function (translationId) { 3138 | if (typeof translationId === 'undefined') { 3139 | // case of element "xyz" 3140 | observeElementTranslation(''); 3141 | } else { 3142 | // case of regular attribute 3143 | if (translationId !== '' || !firstAttributeChangedEvent) { 3144 | translationIds.translate = translationId; 3145 | updateTranslations(); 3146 | } 3147 | } 3148 | firstAttributeChangedEvent = false; 3149 | }); 3150 | 3151 | for (var translateAttr in iAttr) { 3152 | if (iAttr.hasOwnProperty(translateAttr) && translateAttr.substr(0, 13) === 'translateAttr' && translateAttr.length > 13) { 3153 | observeAttributeTranslation(translateAttr); 3154 | } 3155 | } 3156 | 3157 | iAttr.$observe('translateDefault', function (value) { 3158 | scope.defaultText = value; 3159 | updateTranslations(); 3160 | }); 3161 | 3162 | if (translateSanitizeStrategyExist) { 3163 | iAttr.$observe('translateSanitizeStrategy', function (value) { 3164 | scope.sanitizeStrategy = $parse(value)(scope.$parent); 3165 | updateTranslations(); 3166 | }); 3167 | } 3168 | 3169 | if (translateValuesExist) { 3170 | iAttr.$observe('translateValues', function (interpolateParams) { 3171 | if (interpolateParams) { 3172 | scope.$parent.$watch(function () { 3173 | angular.extend(scope.interpolateParams, $parse(interpolateParams)(scope.$parent)); 3174 | }); 3175 | } 3176 | }); 3177 | } 3178 | 3179 | if (translateValueExist) { 3180 | var observeValueAttribute = function (attrName) { 3181 | iAttr.$observe(attrName, function (value) { 3182 | var attributeName = lowercase(attrName.substr(14, 1)) + attrName.substr(15); 3183 | scope.interpolateParams[attributeName] = value; 3184 | }); 3185 | }; 3186 | for (var attr in iAttr) { 3187 | if (Object.prototype.hasOwnProperty.call(iAttr, attr) && attr.substr(0, 14) === 'translateValue' && attr !== 'translateValues') { 3188 | observeValueAttribute(attr); 3189 | } 3190 | } 3191 | } 3192 | 3193 | // Master update function 3194 | var updateTranslations = function () { 3195 | for (var key in translationIds) { 3196 | if (translationIds.hasOwnProperty(key) && translationIds[key] !== undefined) { 3197 | updateTranslation(key, translationIds[key], scope, scope.interpolateParams, scope.defaultText, scope.translateNamespace); 3198 | } 3199 | } 3200 | }; 3201 | 3202 | // Put translation processing function outside loop 3203 | var updateTranslation = function(translateAttr, translationId, scope, interpolateParams, defaultTranslationText, translateNamespace) { 3204 | if (translationId) { 3205 | // if translation id starts with '.' and translateNamespace given, prepend namespace 3206 | if (translateNamespace && translationId.charAt(0) === '.') { 3207 | translationId = translateNamespace + translationId; 3208 | } 3209 | 3210 | $translate(translationId, interpolateParams, translateInterpolation, defaultTranslationText, scope.translateLanguage, scope.sanitizeStrategy) 3211 | .then(function (translation) { 3212 | applyTranslation(translation, scope, true, translateAttr); 3213 | }, function (translationId) { 3214 | applyTranslation(translationId, scope, false, translateAttr); 3215 | }); 3216 | } else { 3217 | // as an empty string cannot be translated, we can solve this using successful=false 3218 | applyTranslation(translationId, scope, false, translateAttr); 3219 | } 3220 | }; 3221 | 3222 | var applyTranslation = function (value, scope, successful, translateAttr) { 3223 | if (!successful) { 3224 | if (typeof scope.defaultText !== 'undefined') { 3225 | value = scope.defaultText; 3226 | } 3227 | } 3228 | if (translateAttr === 'translate') { 3229 | // default translate into innerHTML 3230 | if (successful || (!successful && !$translate.isKeepContent() && typeof iAttr.translateKeepContent === 'undefined')) { 3231 | iElement.empty().append(scope.preText + value + scope.postText); 3232 | } 3233 | var globallyEnabled = $translate.isPostCompilingEnabled(); 3234 | var locallyDefined = typeof tAttr.translateCompile !== 'undefined'; 3235 | var locallyEnabled = locallyDefined && tAttr.translateCompile !== 'false'; 3236 | if ((globallyEnabled && !locallyDefined) || locallyEnabled) { 3237 | $compile(iElement.contents())(scope); 3238 | } 3239 | } else { 3240 | // translate attribute 3241 | var attributeName = iAttr.$attr[translateAttr]; 3242 | if (attributeName.substr(0, 5) === 'data-') { 3243 | // ensure html5 data prefix is stripped 3244 | attributeName = attributeName.substr(5); 3245 | } 3246 | attributeName = attributeName.substr(15); 3247 | iElement.attr(attributeName, value); 3248 | } 3249 | }; 3250 | 3251 | if (translateValuesExist || translateValueExist || iAttr.translateDefault) { 3252 | scope.$watch('interpolateParams', updateTranslations, true); 3253 | } 3254 | 3255 | // Replaced watcher on translateLanguage with event listener 3256 | scope.$on('translateLanguageChanged', updateTranslations); 3257 | 3258 | // Ensures the text will be refreshed after the current language was changed 3259 | // w/ $translate.use(...) 3260 | var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); 3261 | 3262 | // ensure translation will be looked up at least one 3263 | if (iElement.text().length) { 3264 | if (iAttr.translate) { 3265 | observeElementTranslation(iAttr.translate); 3266 | } else { 3267 | observeElementTranslation(''); 3268 | } 3269 | } else if (iAttr.translate) { 3270 | // ensure attribute will be not skipped 3271 | observeElementTranslation(iAttr.translate); 3272 | } 3273 | updateTranslations(); 3274 | scope.$on('$destroy', unbind); 3275 | }; 3276 | } 3277 | }; 3278 | } 3279 | 3280 | /** 3281 | * Returns the scope's namespace. 3282 | * @private 3283 | * @param scope 3284 | * @returns {string} 3285 | */ 3286 | function getTranslateNamespace(scope) { 3287 | 'use strict'; 3288 | if (scope.translateNamespace) { 3289 | return scope.translateNamespace; 3290 | } 3291 | if (scope.$parent) { 3292 | return getTranslateNamespace(scope.$parent); 3293 | } 3294 | } 3295 | 3296 | translateDirective.displayName = 'translateDirective'; 3297 | 3298 | angular.module('pascalprecht.translate') 3299 | /** 3300 | * @ngdoc directive 3301 | * @name pascalprecht.translate.directive:translate-attr 3302 | * @restrict A 3303 | * 3304 | * @description 3305 | * Translates attributes like translate-attr-ATTR, but with an object like ng-class. 3306 | * Internally it uses `translate` service to translate translation id. It possible to 3307 | * pass an optional `translate-values` object literal as string into translation id. 3308 | * 3309 | * @param {string=} translate-attr Object literal mapping attributes to translation ids. 3310 | * @param {string=} translate-values Values to pass into the translation ids. Can be passed as object literal string. 3311 | * @param {string=} translate-sanitize-strategy defines locally sanitize strategy 3312 | * 3313 | * @example 3314 | 3315 | 3316 |
3317 | 3318 | 3319 | 3320 |
3321 |
3322 | 3323 | angular.module('ngView', ['pascalprecht.translate']) 3324 | 3325 | .config(function ($translateProvider) { 3326 | 3327 | $translateProvider.translations('en',{ 3328 | 'TRANSLATION_ID': 'Hello there!', 3329 | 'WITH_VALUES': 'The following value is dynamic: {{value}}', 3330 | }).preferredLanguage('en'); 3331 | 3332 | }); 3333 | 3334 | angular.module('ngView').controller('TranslateCtrl', function ($scope) { 3335 | $scope.translationId = 'TRANSLATION_ID'; 3336 | 3337 | $scope.values = { 3338 | value: 78 3339 | }; 3340 | }); 3341 | 3342 | 3343 | it('should translate', function () { 3344 | inject(function ($rootScope, $compile) { 3345 | $rootScope.translationId = 'TRANSLATION_ID'; 3346 | 3347 | element = $compile('')($rootScope); 3348 | $rootScope.$digest(); 3349 | expect(element.attr('placeholder)).toBe('Hello there!'); 3350 | expect(element.attr('title)).toBe('The following value is dynamic: 5'); 3351 | }); 3352 | }); 3353 | 3354 |
3355 | */ 3356 | .directive('translateAttr', translateAttrDirective); 3357 | function translateAttrDirective($translate, $rootScope) { 3358 | 3359 | 'use strict'; 3360 | 3361 | return { 3362 | restrict: 'A', 3363 | priority: $translate.directivePriority(), 3364 | link: function linkFn(scope, element, attr) { 3365 | 3366 | var translateAttr, 3367 | translateValues, 3368 | translateSanitizeStrategy, 3369 | previousAttributes = {}; 3370 | 3371 | // Main update translations function 3372 | var updateTranslations = function () { 3373 | angular.forEach(translateAttr, function (translationId, attributeName) { 3374 | if (!translationId) { 3375 | return; 3376 | } 3377 | previousAttributes[attributeName] = true; 3378 | 3379 | // if translation id starts with '.' and translateNamespace given, prepend namespace 3380 | if (scope.translateNamespace && translationId.charAt(0) === '.') { 3381 | translationId = scope.translateNamespace + translationId; 3382 | } 3383 | $translate(translationId, translateValues, attr.translateInterpolation, undefined, scope.translateLanguage, translateSanitizeStrategy) 3384 | .then(function (translation) { 3385 | element.attr(attributeName, translation); 3386 | }, function (translationId) { 3387 | element.attr(attributeName, translationId); 3388 | }); 3389 | }); 3390 | 3391 | // Removing unused attributes that were previously used 3392 | angular.forEach(previousAttributes, function (flag, attributeName) { 3393 | if (!translateAttr[attributeName]) { 3394 | element.removeAttr(attributeName); 3395 | delete previousAttributes[attributeName]; 3396 | } 3397 | }); 3398 | }; 3399 | 3400 | // Watch for attribute changes 3401 | watchAttribute( 3402 | scope, 3403 | attr.translateAttr, 3404 | function (newValue) { translateAttr = newValue; }, 3405 | updateTranslations 3406 | ); 3407 | // Watch for value changes 3408 | watchAttribute( 3409 | scope, 3410 | attr.translateValues, 3411 | function (newValue) { translateValues = newValue; }, 3412 | updateTranslations 3413 | ); 3414 | // Watch for sanitize strategy changes 3415 | watchAttribute( 3416 | scope, 3417 | attr.translateSanitizeStrategy, 3418 | function (newValue) { translateSanitizeStrategy = newValue; }, 3419 | updateTranslations 3420 | ); 3421 | 3422 | if (attr.translateValues) { 3423 | scope.$watch(attr.translateValues, updateTranslations, true); 3424 | } 3425 | 3426 | // Replaced watcher on translateLanguage with event listener 3427 | scope.$on('translateLanguageChanged', updateTranslations); 3428 | 3429 | // Ensures the text will be refreshed after the current language was changed 3430 | // w/ $translate.use(...) 3431 | var unbind = $rootScope.$on('$translateChangeSuccess', updateTranslations); 3432 | 3433 | updateTranslations(); 3434 | scope.$on('$destroy', unbind); 3435 | } 3436 | }; 3437 | } 3438 | 3439 | function watchAttribute(scope, attribute, valueCallback, changeCallback) { 3440 | 'use strict'; 3441 | if (!attribute) { 3442 | return; 3443 | } 3444 | if (attribute.substr(0, 2) === '::') { 3445 | attribute = attribute.substr(2); 3446 | } else { 3447 | scope.$watch(attribute, function(newValue) { 3448 | valueCallback(newValue); 3449 | changeCallback(); 3450 | }, true); 3451 | } 3452 | valueCallback(scope.$eval(attribute)); 3453 | } 3454 | 3455 | translateAttrDirective.displayName = 'translateAttrDirective'; 3456 | 3457 | angular.module('pascalprecht.translate') 3458 | /** 3459 | * @ngdoc directive 3460 | * @name pascalprecht.translate.directive:translateCloak 3461 | * @requires $translate 3462 | * @restrict A 3463 | * 3464 | * $description 3465 | * Adds a `translate-cloak` class name to the given element where this directive 3466 | * is applied initially and removes it, once a loader has finished loading. 3467 | * 3468 | * This directive can be used to prevent initial flickering when loading translation 3469 | * data asynchronously. 3470 | * 3471 | * The class name is defined in 3472 | * {@link pascalprecht.translate.$translateProvider#cloakClassName $translate.cloakClassName()}. 3473 | * 3474 | * @param {string=} translate-cloak If a translationId is provided, it will be used for showing 3475 | * or hiding the cloak. Basically it relies on the translation 3476 | * resolve. 3477 | */ 3478 | .directive('translateCloak', translateCloakDirective); 3479 | 3480 | function translateCloakDirective($translate, $rootScope) { 3481 | 3482 | 'use strict'; 3483 | 3484 | return { 3485 | compile : function (tElement) { 3486 | var applyCloak = function (element) { 3487 | element.addClass($translate.cloakClassName()); 3488 | }, 3489 | removeCloak = function (element) { 3490 | element.removeClass($translate.cloakClassName()); 3491 | }; 3492 | applyCloak(tElement); 3493 | 3494 | return function linkFn(scope, iElement, iAttr) { 3495 | //Create bound functions that incorporate the active DOM element. 3496 | var iRemoveCloak = removeCloak.bind(this, iElement), iApplyCloak = applyCloak.bind(this, iElement); 3497 | if (iAttr.translateCloak && iAttr.translateCloak.length) { 3498 | // Register a watcher for the defined translation allowing a fine tuned cloak 3499 | iAttr.$observe('translateCloak', function (translationId) { 3500 | $translate(translationId).then(iRemoveCloak, iApplyCloak); 3501 | }); 3502 | $rootScope.$on('$translateChangeSuccess', function () { 3503 | $translate(iAttr.translateCloak).then(iRemoveCloak, iApplyCloak); 3504 | }); 3505 | } else { 3506 | $translate.onReady(iRemoveCloak); 3507 | } 3508 | }; 3509 | } 3510 | }; 3511 | } 3512 | 3513 | translateCloakDirective.displayName = 'translateCloakDirective'; 3514 | 3515 | angular.module('pascalprecht.translate') 3516 | /** 3517 | * @ngdoc directive 3518 | * @name pascalprecht.translate.directive:translateNamespace 3519 | * @restrict A 3520 | * 3521 | * @description 3522 | * Translates given translation id either through attribute or DOM content. 3523 | * Internally it uses `translate` filter to translate translation id. It is possible to 3524 | * pass an optional `translate-values` object literal as string into translation id. 3525 | * 3526 | * @param {string=} translate namespace name which could be either string or interpolated string. 3527 | * 3528 | * @example 3529 | 3530 | 3531 |
3532 | 3533 |
3534 |

.HEADERS.TITLE

3535 |

.HEADERS.WELCOME

3536 |
3537 | 3538 |
3539 |

.TITLE

3540 |

.WELCOME

3541 |
3542 | 3543 |
3544 |
3545 | 3546 | angular.module('ngView', ['pascalprecht.translate']) 3547 | 3548 | .config(function ($translateProvider) { 3549 | 3550 | $translateProvider.translations('en',{ 3551 | 'TRANSLATION_ID': 'Hello there!', 3552 | 'CONTENT': { 3553 | 'HEADERS': { 3554 | TITLE: 'Title' 3555 | } 3556 | }, 3557 | 'CONTENT.HEADERS.WELCOME': 'Welcome' 3558 | }).preferredLanguage('en'); 3559 | 3560 | }); 3561 | 3562 | 3563 |
3564 | */ 3565 | .directive('translateNamespace', translateNamespaceDirective); 3566 | 3567 | function translateNamespaceDirective() { 3568 | 3569 | 'use strict'; 3570 | 3571 | return { 3572 | restrict: 'A', 3573 | scope: true, 3574 | compile: function () { 3575 | return { 3576 | pre: function (scope, iElement, iAttrs) { 3577 | scope.translateNamespace = _getTranslateNamespace(scope); 3578 | 3579 | if (scope.translateNamespace && iAttrs.translateNamespace.charAt(0) === '.') { 3580 | scope.translateNamespace += iAttrs.translateNamespace; 3581 | } else { 3582 | scope.translateNamespace = iAttrs.translateNamespace; 3583 | } 3584 | } 3585 | }; 3586 | } 3587 | }; 3588 | } 3589 | 3590 | /** 3591 | * Returns the scope's namespace. 3592 | * @private 3593 | * @param scope 3594 | * @returns {string} 3595 | */ 3596 | function _getTranslateNamespace(scope) { 3597 | 'use strict'; 3598 | if (scope.translateNamespace) { 3599 | return scope.translateNamespace; 3600 | } 3601 | if (scope.$parent) { 3602 | return _getTranslateNamespace(scope.$parent); 3603 | } 3604 | } 3605 | 3606 | translateNamespaceDirective.displayName = 'translateNamespaceDirective'; 3607 | 3608 | angular.module('pascalprecht.translate') 3609 | /** 3610 | * @ngdoc directive 3611 | * @name pascalprecht.translate.directive:translateLanguage 3612 | * @restrict A 3613 | * 3614 | * @description 3615 | * Forces the language to the directives in the underlying scope. 3616 | * 3617 | * @param {string=} translate language that will be negotiated. 3618 | * 3619 | * @example 3620 | 3621 | 3622 |
3623 | 3624 |
3625 |

HELLO

3626 |
3627 | 3628 |
3629 |

HELLO

3630 |
3631 | 3632 |
3633 |
3634 | 3635 | angular.module('ngView', ['pascalprecht.translate']) 3636 | 3637 | .config(function ($translateProvider) { 3638 | 3639 | $translateProvider 3640 | .translations('en',{ 3641 | 'HELLO': 'Hello world!' 3642 | }) 3643 | .translations('de',{ 3644 | 'HELLO': 'Hallo Welt!' 3645 | }) 3646 | .preferredLanguage('en'); 3647 | 3648 | }); 3649 | 3650 | 3651 |
3652 | */ 3653 | .directive('translateLanguage', translateLanguageDirective); 3654 | 3655 | function translateLanguageDirective() { 3656 | 3657 | 'use strict'; 3658 | 3659 | return { 3660 | restrict: 'A', 3661 | scope: true, 3662 | compile: function () { 3663 | return function linkFn(scope, iElement, iAttrs) { 3664 | 3665 | iAttrs.$observe('translateLanguage', function (newTranslateLanguage) { 3666 | scope.translateLanguage = newTranslateLanguage; 3667 | }); 3668 | 3669 | scope.$watch('translateLanguage', function(){ 3670 | scope.$broadcast('translateLanguageChanged'); 3671 | }); 3672 | }; 3673 | } 3674 | }; 3675 | } 3676 | 3677 | translateLanguageDirective.displayName = 'translateLanguageDirective'; 3678 | 3679 | angular.module('pascalprecht.translate') 3680 | /** 3681 | * @ngdoc filter 3682 | * @name pascalprecht.translate.filter:translate 3683 | * @requires $parse 3684 | * @requires pascalprecht.translate.$translate 3685 | * @function 3686 | * 3687 | * @description 3688 | * Uses `$translate` service to translate contents. Accepts interpolate parameters 3689 | * to pass dynamized values though translation. 3690 | * 3691 | * @param {string} translationId A translation id to be translated. 3692 | * @param {*=} interpolateParams Optional object literal (as hash or string) to pass values into translation. 3693 | * 3694 | * @returns {string} Translated text. 3695 | * 3696 | * @example 3697 | 3698 | 3699 |
3700 | 3701 |
{{ 'TRANSLATION_ID' | translate }}
3702 |
{{ translationId | translate }}
3703 |
{{ 'WITH_VALUES' | translate:'{value: 5}' }}
3704 |
{{ 'WITH_VALUES' | translate:values }}
3705 | 3706 |
3707 |
3708 | 3709 | angular.module('ngView', ['pascalprecht.translate']) 3710 | 3711 | .config(function ($translateProvider) { 3712 | 3713 | $translateProvider.translations('en', { 3714 | 'TRANSLATION_ID': 'Hello there!', 3715 | 'WITH_VALUES': 'The following value is dynamic: {{value}}' 3716 | }); 3717 | $translateProvider.preferredLanguage('en'); 3718 | 3719 | }); 3720 | 3721 | angular.module('ngView').controller('TranslateCtrl', function ($scope) { 3722 | $scope.translationId = 'TRANSLATION_ID'; 3723 | 3724 | $scope.values = { 3725 | value: 78 3726 | }; 3727 | }); 3728 | 3729 |
3730 | */ 3731 | .filter('translate', translateFilterFactory); 3732 | 3733 | function translateFilterFactory($parse, $translate) { 3734 | 3735 | 'use strict'; 3736 | 3737 | var translateFilter = function (translationId, interpolateParams, interpolation, forceLanguage) { 3738 | if (!angular.isObject(interpolateParams)) { 3739 | var ctx = this || { 3740 | '__SCOPE_IS_NOT_AVAILABLE': 'More info at https://github.com/angular/angular.js/commit/8863b9d04c722b278fa93c5d66ad1e578ad6eb1f' 3741 | }; 3742 | interpolateParams = $parse(interpolateParams)(ctx); 3743 | } 3744 | 3745 | return $translate.instant(translationId, interpolateParams, interpolation, forceLanguage); 3746 | }; 3747 | 3748 | if ($translate.statefulFilter()) { 3749 | translateFilter.$stateful = true; 3750 | } 3751 | 3752 | return translateFilter; 3753 | } 3754 | 3755 | translateFilterFactory.displayName = 'translateFilterFactory'; 3756 | 3757 | angular.module('pascalprecht.translate') 3758 | 3759 | /** 3760 | * @ngdoc object 3761 | * @name pascalprecht.translate.$translationCache 3762 | * @requires $cacheFactory 3763 | * 3764 | * @description 3765 | * The first time a translation table is used, it is loaded in the translation cache for quick retrieval. You 3766 | * can load translation tables directly into the cache by consuming the 3767 | * `$translationCache` service directly. 3768 | * 3769 | * @return {object} $cacheFactory object. 3770 | */ 3771 | .factory('$translationCache', $translationCache); 3772 | 3773 | function $translationCache($cacheFactory) { 3774 | 3775 | 'use strict'; 3776 | 3777 | return $cacheFactory('translations'); 3778 | } 3779 | 3780 | $translationCache.displayName = '$translationCache'; 3781 | return 'pascalprecht.translate'; 3782 | 3783 | })); 3784 | --------------------------------------------------------------------------------