test(ivy): i18n - add compile time translation to integration test (#32881)
PR Close #32881
This commit is contained in:
		
							parent
							
								
									90855f331f
								
							
						
					
					
						commit
						009cab8dce
					
				
							
								
								
									
										1
									
								
								integration/.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								integration/.gitignore
									
									
									
									
										vendored
									
									
								
							| @ -1,6 +1,7 @@ | ||||
| built/ | ||||
| dist/ | ||||
| vendor/ | ||||
| tmp/ | ||||
| */src/*.d.ts | ||||
| */src/*.js | ||||
| !karma.conf.js | ||||
|  | ||||
| @ -16,6 +16,7 @@ | ||||
|         "build": { | ||||
|           "builder": "@angular-devkit/build-angular:browser", | ||||
|           "options": { | ||||
|             "progress": false, | ||||
|             "outputPath": "dist", | ||||
|             "index": "src/index.html", | ||||
|             "main": "src/main.ts", | ||||
| @ -31,6 +32,9 @@ | ||||
|             "scripts": [] | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "view-engine": { | ||||
|               "tsConfig": "src/tsconfig.view-engine.json" | ||||
|             }, | ||||
|             "production": { | ||||
|               "fileReplacements": [ | ||||
|                 { | ||||
| @ -55,9 +59,8 @@ | ||||
|                 } | ||||
|               ] | ||||
|             }, | ||||
|             "legacy-id-mode": { | ||||
|               "tsConfig": "src/tsconfig.legacy-id-mode.json", | ||||
|               "polyfills": "src/polyfills.legacy-id-mode.ts", | ||||
|             "translated-legacy": { | ||||
|               "tsConfig": "src/tsconfig.legacy.json", | ||||
|               "optimization": true, | ||||
|               "outputHashing": "all", | ||||
|               "sourceMap": false, | ||||
| @ -66,7 +69,7 @@ | ||||
|               "aot": true, | ||||
|               "extractLicenses": true, | ||||
|               "vendorChunk": false, | ||||
|               "buildOptimizer": true, | ||||
|               "buildOptimizer": true | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
| @ -85,17 +88,15 @@ | ||||
|             "ci-production": { | ||||
|               "browserTarget": "cli-hello-world-ivy-compat:build:production", | ||||
|               "progress": false | ||||
|             }, | ||||
|             "legacy-id-mode": { | ||||
|               "browserTarget": "cli-hello-world-ivy-compat:build:legacy-id-mode", | ||||
|               "progress": false | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|         "extract-i18n": { | ||||
|           "builder": "@angular-devkit/build-angular:extract-i18n", | ||||
|           "options": { | ||||
|             "browserTarget": "cli-hello-world-ivy-compat:build" | ||||
|             "browserTarget": "cli-hello-world-ivy-compat:build:view-engine", | ||||
|             "outputPath": "../tmp/legacy-locales/", | ||||
|             "outFile": "messages.legacy.xlf" | ||||
|           } | ||||
|         }, | ||||
|         "test": { | ||||
| @ -137,8 +138,9 @@ | ||||
|         "e2e": { | ||||
|           "builder": "@angular-devkit/build-angular:protractor", | ||||
|           "options": { | ||||
|             "protractorConfig": "e2e/protractor.conf.js", | ||||
|             "devServerTarget": "cli-hello-world-ivy-compat:serve" | ||||
|             "protractorConfig": "e2e/runtime/protractor.conf.js", | ||||
|             "devServerTarget": "cli-hello-world-ivy-compat:serve", | ||||
|             "webdriverUpdate": true | ||||
|           }, | ||||
|           "configurations": { | ||||
|             "production": { | ||||
| @ -150,8 +152,17 @@ | ||||
|             "ci-production": { | ||||
|               "devServerTarget": "cli-hello-world-ivy-compat:serve:ci-production" | ||||
|             }, | ||||
|             "legacy-id-mode": { | ||||
|               "devServerTarget": "cli-hello-world-ivy-compat:serve:legacy-id-mode" | ||||
|             "translated-legacy": { | ||||
|               "devServerTarget": "", | ||||
|               "protractorConfig": "e2e/legacy/protractor.conf.js" | ||||
|             }, | ||||
|             "translated-fr": { | ||||
|               "devServerTarget": "", | ||||
|               "protractorConfig": "e2e/fr/protractor.conf.js" | ||||
|             }, | ||||
|             "translated-de": { | ||||
|               "devServerTarget": "", | ||||
|               "protractorConfig": "e2e/de/protractor.conf.js" | ||||
|             } | ||||
|           } | ||||
|         }, | ||||
|  | ||||
							
								
								
									
										38
									
								
								integration/cli-hello-world-ivy-i18n/e2e/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								integration/cli-hello-world-ivy-i18n/e2e/README.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,38 @@ | ||||
| ## E2E tests | ||||
| 
 | ||||
| There are four different sets of e2e tests in this folder. They are all testing different | ||||
| translation scenarios, but they are all built with IVY enabled. | ||||
| 
 | ||||
| ### runtime | ||||
| 
 | ||||
| Translations are provided at runtime by calling `loadTranslations()` in the polyfill.ts | ||||
| 
 | ||||
| ### de and fr | ||||
| 
 | ||||
| The application is built (into the `dist` folder) and then two sets of translations | ||||
| (`src/locales/messages.(de|fr).json`) are used to generate two copies of the app, which have | ||||
| been translated (compile-time inlined). | ||||
| 
 | ||||
| These translated apps are stored in `tmp/translations/(de|fr)`. | ||||
| 
 | ||||
| ### legacy | ||||
| 
 | ||||
| The legacy `ng xi18n` tool extracts the messages from the Angular templates, into the XLIFF 1.2 | ||||
| format with legacy message ids (`tmp/legacy-locales/messages.legacy.xlf`). | ||||
| 
 | ||||
| The translation file is modified to apply a simple translation. | ||||
| 
 | ||||
| The app must be compiled using the `i18nLegacyMessageIdFormat` option set to ensure that the correct | ||||
| message ids are used to match those in the translation files. | ||||
| 
 | ||||
| The app is translated using the compile-time inlining tool to generate a copy that has the | ||||
| translated message in it. | ||||
| 
 | ||||
| ## Hosting | ||||
| 
 | ||||
| Since the CLI hosts from and in-memory file-system the compile-time inliner is not able to | ||||
| translate the output files. So the `de`, `fr` and `legacy` apps must be statically built to | ||||
| disk and translated there. | ||||
| 
 | ||||
| Since the translated app is now on disk, we cannot use the CLI to serve it. Instead we use | ||||
| a simple static HTTP server instead. | ||||
							
								
								
									
										17
									
								
								integration/cli-hello-world-ivy-i18n/e2e/de/app.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								integration/cli-hello-world-ivy-i18n/e2e/de/app.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,17 @@ | ||||
| import {AppPage} from '../app.po'; | ||||
| 
 | ||||
| describe('cli-hello-world-ivy App', () => { | ||||
|   let page: AppPage; | ||||
|   beforeEach(() => { | ||||
|     page = new AppPage(); | ||||
|     page.navigateTo(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should display title', () => { | ||||
|     expect(page.getHeading()).toEqual('Guten Tag, cli-hello-world-ivy-compat! (inline)'); | ||||
|   }); | ||||
| 
 | ||||
|   it('should display welcome message', () => { | ||||
|     expect(page.getParagraph('message')).toEqual('Willkommen in der i18n App. (inline)'); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,5 @@ | ||||
| const {config} = require('../protractor.conf'); | ||||
| exports.config = { | ||||
|   ...config, | ||||
|   specs: ['./app.e2e-spec.ts'], | ||||
| }; | ||||
							
								
								
									
										16
									
								
								integration/cli-hello-world-ivy-i18n/e2e/fr/app.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								integration/cli-hello-world-ivy-i18n/e2e/fr/app.e2e-spec.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,16 @@ | ||||
| import {AppPage} from '../app.po'; | ||||
| 
 | ||||
| describe('cli-hello-world-ivy App', () => { | ||||
|   let page: AppPage; | ||||
|   beforeEach(() => { | ||||
|     page = new AppPage(); | ||||
|     page.navigateTo(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should display title', | ||||
|      () => { expect(page.getHeading()).toEqual('Bonjour, cli-hello-world-ivy-compat! (inline)'); }); | ||||
| 
 | ||||
|   it('should display welcome message', () => { | ||||
|     expect(page.getParagraph('message')).toEqual('Bienvenue sur l\'application i18n. (inline)'); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,5 @@ | ||||
| const {config} = require('../protractor.conf'); | ||||
| exports.config = { | ||||
|   ...config, | ||||
|   specs: ['./app.e2e-spec.ts'], | ||||
| }; | ||||
| @ -0,0 +1,18 @@ | ||||
| import {AppPage} from '../app.po'; | ||||
| 
 | ||||
| describe('cli-hello-world-ivy App', () => { | ||||
|   let page: AppPage; | ||||
|   beforeEach(() => { | ||||
|     page = new AppPage(); | ||||
|     page.navigateTo(); | ||||
|   }); | ||||
| 
 | ||||
|   it('should display translated title', | ||||
|      () => { expect(page.getHeading()).toEqual('Bonjour cli-hello-world-ivy-compat!'); }); | ||||
| 
 | ||||
|   it('should display untranslated welcome message', () => { | ||||
|     // This message does not get translated because we did not provide a translation for it
 | ||||
|     // See "translated:legacy:extract-and-update" in package.json.
 | ||||
|     expect(page.getParagraph('message')).toEqual('Welcome to the i18n app.'); | ||||
|   }); | ||||
| }); | ||||
| @ -0,0 +1,5 @@ | ||||
| const {config} = require('../protractor.conf'); | ||||
| exports.config = { | ||||
|   ...config, | ||||
|   specs: ['./app.e2e-spec.ts'], | ||||
| }; | ||||
| @ -1,4 +1,4 @@ | ||||
| import {AppPage} from './app.po'; | ||||
| import {AppPage} from '../app.po'; | ||||
| 
 | ||||
| describe('cli-hello-world-ivy App', () => { | ||||
|   let page: AppPage; | ||||
| @ -0,0 +1,5 @@ | ||||
| const {config} = require('../protractor.conf'); | ||||
| exports.config = { | ||||
|   ...config, | ||||
|   specs: ['./app.e2e-spec.ts'], | ||||
| }; | ||||
| @ -10,7 +10,22 @@ | ||||
|     "postinstall": "webdriver-manager update --gecko=false --standalone=false $CI_CHROMEDRIVER_VERSION_ARG", | ||||
|     "start": "ng serve", | ||||
|     "pretest": "ng version", | ||||
|     "test": "ng test --progress=false --watch=false && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production && yarn e2e --configuration=legacy-id-mode" | ||||
|     "test": "ng test --progress=false --watch=false && yarn e2e --configuration=ci && yarn e2e --configuration=ci-production && yarn translated:test && yarn translated:legacy:test", | ||||
|     "translate": "localize-translate -r \"dist/\" -s \"**/*\" -t \"src/locales/messages.*\" -o \"tmp/translations/{{LOCALE}}\"", | ||||
|      | ||||
|     "translated:test": "yarn build && yarn translate && yarn translated:fr:e2e && yarn translated:de:e2e", | ||||
|      | ||||
|     "translated:fr:serve": "serve tmp/translations/fr --listen 4200", | ||||
|     "translated:fr:e2e": "npm-run-all -p -r translated:fr:serve \"ng e2e --configuration=translated-fr --webdriver-update=false\"", | ||||
|      | ||||
|     "translated:de:serve": "serve tmp/translations/de --listen 4200", | ||||
|     "translated:de:e2e": "npm-run-all -p -r translated:de:serve \"ng e2e --configuration=translated-de --webdriver-update=false\"", | ||||
|      | ||||
|     "translated:legacy:test": "yarn translated:legacy:extract-and-update && ng build --configuration=translated-legacy && yarn translated:legacy:translate && yarn translated:legacy:e2e", | ||||
|     "translated:legacy:extract-and-update": "ng xi18n && sed -i.bak -e 's/source>/target>'/ -e 's/Hello/Bonjour/' -e 's/source-language=\"en\"/source-language=\"en\" target-language=\"legacy\"/' tmp/legacy-locales/messages.legacy.xlf", | ||||
|     "translated:legacy:translate": "localize-translate -r \"dist/\" -s \"**/*\" -t \"tmp/legacy-locales/messages.legacy.xlf\" -o \"tmp/translations/{{LOCALE}}\"", | ||||
|     "translated:legacy:serve": "serve tmp/translations/legacy --listen 4200", | ||||
|     "translated:legacy:e2e": "npm-run-all -p -r translated:legacy:serve \"ng e2e --configuration=translated-legacy --webdriver-update=false\"" | ||||
|   }, | ||||
|   "private": true, | ||||
|   "dependencies": { | ||||
| @ -24,13 +39,15 @@ | ||||
|     "@angular/platform-browser-dynamic": "file:../../dist/packages-dist/platform-browser-dynamic", | ||||
|     "@angular/router": "file:../../dist/packages-dist/router", | ||||
|     "core-js": "file:../../node_modules/core-js", | ||||
|     "npm-run-all": "^4.1.5", | ||||
|     "rxjs": "file:../../node_modules/rxjs", | ||||
|     "serve": "^11.2.0", | ||||
|     "tslib": "^1.9.3", | ||||
|     "zone.js": "file:../../node_modules/zone.js" | ||||
|   }, | ||||
|   "devDependencies": { | ||||
|     "@angular-devkit/build-angular": "^0.803.0-next.1", | ||||
|     "@angular/cli": "^8.3.0-next.1", | ||||
|     "@angular-devkit/build-angular": "^0.900.0-next.8", | ||||
|     "@angular/cli": "9.0.0-next.8", | ||||
|     "@angular/compiler-cli": "file:../../dist/packages-dist/compiler-cli", | ||||
|     "@angular/language-service": "file:../../dist/packages-dist/language-service", | ||||
|     "@types/jasmine": "~2.8.8", | ||||
|  | ||||
| @ -0,0 +1,7 @@ | ||||
| { | ||||
|   "locale": "de", | ||||
|   "translations": { | ||||
|     "1638894134994447485": " Guten Tag, {$INTERPOLATION}! (inline)", | ||||
|     "6762263703087737643": "Willkommen in der i18n App. (inline)" | ||||
|   } | ||||
| } | ||||
| @ -0,0 +1,7 @@ | ||||
| { | ||||
|   "locale": "fr", | ||||
|   "translations": { | ||||
|     "1638894134994447485": " Bonjour, {$INTERPOLATION}! (inline)", | ||||
|     "6762263703087737643": "Bienvenue sur l'application i18n. (inline)" | ||||
|   } | ||||
| } | ||||
| @ -1,104 +0,0 @@ | ||||
| /** | ||||
|  * This file includes polyfills needed by Angular and is loaded before the app. | ||||
|  * You can add your own extra polyfills to this file. | ||||
|  * | ||||
|  * This file is divided into 2 sections: | ||||
|  *   1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. | ||||
|  *   2. Application imports. Files imported after ZoneJS that should be loaded before your main | ||||
|  *      file. | ||||
|  * | ||||
|  * The current setup is for so-called "evergreen" browsers; the last versions of browsers that | ||||
|  * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), | ||||
|  * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. | ||||
|  * | ||||
|  * Learn more in https://angular.io/guide/browser-support
 | ||||
|  */ | ||||
| 
 | ||||
| /*************************************************************************************************** | ||||
|  * BROWSER POLYFILLS | ||||
|  */ | ||||
| 
 | ||||
| /** IE9, IE10, IE11, and Chrome <55 requires all of the following polyfills. | ||||
|  *  This also includes Android Emulators with older versions of Chrome and Google Search/Googlebot | ||||
|  */ | ||||
| 
 | ||||
| // import 'core-js/es6/symbol';
 | ||||
| // import 'core-js/es6/object';
 | ||||
| // import 'core-js/es6/function';
 | ||||
| // import 'core-js/es6/parse-int';
 | ||||
| // import 'core-js/es6/parse-float';
 | ||||
| // import 'core-js/es6/number';
 | ||||
| // import 'core-js/es6/math';
 | ||||
| // import 'core-js/es6/string';
 | ||||
| // import 'core-js/es6/date';
 | ||||
| // import 'core-js/es6/array';
 | ||||
| // import 'core-js/es6/regexp';
 | ||||
| // import 'core-js/es6/map';
 | ||||
| // import 'core-js/es6/weak-map';
 | ||||
| // import 'core-js/es6/set';
 | ||||
| 
 | ||||
| /** IE10 and IE11 requires the following for NgClass support on SVG elements */ | ||||
| // import 'classlist.js';  // Run `npm install --save classlist.js`.
 | ||||
| 
 | ||||
| /** IE10 and IE11 requires the following for the Reflect API. */ | ||||
| // import 'core-js/es6/reflect';
 | ||||
| 
 | ||||
| /** | ||||
|  * Web Animations `@angular/platform-browser/animations` | ||||
|  * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. | ||||
|  * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). | ||||
|  */ | ||||
| // import 'web-animations-js';  // Run `npm install --save web-animations-js`.
 | ||||
| 
 | ||||
| /** | ||||
|  * By default, zone.js will patch all possible macroTask and DomEvents | ||||
|  * user can disable parts of macroTask/DomEvents patch by setting following flags | ||||
|  * because those flags need to be set before `zone.js` being loaded, and webpack | ||||
|  * will put import in the top of bundle, so user need to create a separate file | ||||
|  * in this directory (for example: zone-flags.ts), and put the following flags | ||||
|  * into that file, and then add the following code before importing zone.js. | ||||
|  * import './zone-flags.ts'; | ||||
|  * | ||||
|  * The flags allowed in zone-flags.ts are listed here. | ||||
|  * | ||||
|  * The following flags will work for all browsers. | ||||
|  * | ||||
|  * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch
 | ||||
|  * requestAnimationFrame | ||||
|  * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
 | ||||
|  * (window as any).__zone_symbol__BLACK_LISTED_EVENTS = ['scroll', 'mousemove']; // disable patch
 | ||||
|  * specified eventNames | ||||
|  * | ||||
|  *  in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js | ||||
|  *  with the following flag, it will bypass `zone.js` patch for IE/Edge | ||||
|  * | ||||
|  *  (window as any).__Zone_enable_cross_context_check = true; | ||||
|  * | ||||
|  */ | ||||
| 
 | ||||
| /*************************************************************************************************** | ||||
|  * Zone JS is required by default for Angular itself. | ||||
|  */ | ||||
| import 'zone.js/dist/zone'; // Included with Angular CLI.
 | ||||
| 
 | ||||
| /*************************************************************************************************** | ||||
|  * Load `$localize` onto the global scope - used if i18n tags appear in Angular templates. | ||||
|  */ | ||||
| import '@angular/localize/init'; | ||||
| 
 | ||||
| // Note that `computeMsgId` is a private API at this stage. It will probably be exported directly
 | ||||
| // from `@angular/localize` at some point.
 | ||||
| import {computeMsgId} from '@angular/compiler'; | ||||
| import {loadTranslations} from '@angular/localize'; | ||||
| 
 | ||||
| // Load some runtime translations
 | ||||
| loadTranslations({ | ||||
|   // This message is in a template so it uses the legacy message id
 | ||||
|   ['2f8d6ae7ef7b0a53392bc23d0968d074ae02a318']: 'Bonjour {$INTERPOLATION}!', | ||||
|   // This message is in application code so it uses the normal message id
 | ||||
|   [computeMsgId('Welcome to the i18n app.')]: 'Bienvenue sur l\'application i18n.', | ||||
| }); | ||||
| 
 | ||||
| /*************************************************************************************************** | ||||
|  * APPLICATION IMPORTS | ||||
|  */ | ||||
| @ -0,0 +1,6 @@ | ||||
| { | ||||
|   "extends": "./tsconfig.app.json", | ||||
|   "angularCompilerOptions": { | ||||
|     "enableIvy": false | ||||
|   } | ||||
| } | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user