| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * @license | 
					
						
							| 
									
										
										
										
											2020-05-19 12:08:49 -07:00
										 |  |  |  * Copyright Google LLC All Rights Reserved. | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |  * | 
					
						
							|  |  |  |  * Use of this source code is governed by an MIT-style license that can be | 
					
						
							|  |  |  |  * found in the LICENSE file at https://angular.io/license
 | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  | import {MissingTranslationStrategy} from '@angular/core'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  | import * as i18n from '../../src/i18n/i18n_ast'; | 
					
						
							|  |  |  | import {TranslationBundle} from '../../src/i18n/translation_bundle'; | 
					
						
							| 
									
										
										
										
											2018-03-28 22:10:08 -07:00
										 |  |  | import * as html from '../../src/ml_parser/ast'; | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  | import {ParseLocation, ParseSourceFile, ParseSourceSpan} from '../../src/parse_util'; | 
					
						
							| 
									
										
										
										
											2018-11-27 12:32:14 -08:00
										 |  |  | import {serializeNodes} from '../ml_parser/util/util'; | 
					
						
							| 
									
										
										
										
											2018-03-28 22:10:08 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  | import {_extractMessages} from './i18n_parser_spec'; | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-12-16 14:42:55 -08:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |   describe('TranslationBundle', () => { | 
					
						
							|  |  |  |     const file = new ParseSourceFile('content', 'url'); | 
					
						
							| 
									
										
										
										
											2017-04-14 18:06:25 +02:00
										 |  |  |     const startLocation = new ParseLocation(file, 0, 0, 0); | 
					
						
							|  |  |  |     const endLocation = new ParseLocation(file, 0, 0, 7); | 
					
						
							|  |  |  |     const span = new ParseSourceSpan(startLocation, endLocation); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |     const srcNode = new i18n.Text('src', span); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 22:10:08 -07:00
										 |  |  |     it('should translate a plain text', () => { | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       const msgMap = {foo: [new i18n.Text('bar', null!)]}; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |       const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |       const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       expect(serializeNodes(tb.get(msg))).toEqual(['bar']); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-03-28 22:10:08 -07:00
										 |  |  |     it('should translate html-like plain text', () => { | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       const msgMap = {foo: [new i18n.Text('<p>bar</p>', null!)]}; | 
					
						
							| 
									
										
										
										
											2018-03-28 22:10:08 -07:00
										 |  |  |       const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); | 
					
						
							|  |  |  |       const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							|  |  |  |       const nodes = tb.get(msg); | 
					
						
							|  |  |  |       expect(nodes.length).toEqual(1); | 
					
						
							|  |  |  |       const textNode: html.Text = nodes[0] as any; | 
					
						
							|  |  |  |       expect(textNode instanceof html.Text).toEqual(true); | 
					
						
							|  |  |  |       expect(textNode.value).toBe('<p>bar</p>'); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |     it('should translate a message with placeholder', () => { | 
					
						
							|  |  |  |       const msgMap = { | 
					
						
							|  |  |  |         foo: [ | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           new i18n.Text('bar', null!), | 
					
						
							|  |  |  |           new i18n.Placeholder('', 'ph1', null!), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         ] | 
					
						
							|  |  |  |       }; | 
					
						
							|  |  |  |       const phMap = { | 
					
						
							| 
									
										
										
										
											2020-10-01 00:17:21 +02:00
										 |  |  |         ph1: createPlaceholder('*phContent*'), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       }; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |       const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |       const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       expect(serializeNodes(tb.get(msg))).toEqual(['bar*phContent*']); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     it('should translate a message with placeholder referencing messages', () => { | 
					
						
							|  |  |  |       const msgMap = { | 
					
						
							|  |  |  |         foo: [ | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           new i18n.Text('--', null!), | 
					
						
							|  |  |  |           new i18n.Placeholder('', 'ph1', null!), | 
					
						
							|  |  |  |           new i18n.Text('++', null!), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         ], | 
					
						
							|  |  |  |         ref: [ | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           new i18n.Text('*refMsg*', null!), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         ], | 
					
						
							|  |  |  |       }; | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |       const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							|  |  |  |       const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       let count = 0; | 
					
						
							|  |  |  |       const digest = (_: any) => count++ ? 'ref' : 'foo'; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |       const tb = new TranslationBundle(msgMap, null, digest); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |       expect(serializeNodes(tb.get(msg))).toEqual(['--*refMsg*++']); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |     it('should use the original message or throw when a translation is not found', () => { | 
					
						
							|  |  |  |       const src = | 
					
						
							|  |  |  |           `<some-tag>some text{{ some_expression }}</some-tag>{count, plural, =0 {no} few {a <b>few</b>}}`; | 
					
						
							|  |  |  |       const messages = _extractMessages(`<div i18n>${src}</div>`); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       const digest = (_: any) => `no matching id`; | 
					
						
							|  |  |  |       // Empty message map -> use source messages in Ignore mode
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       let tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Ignore); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |       expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src); | 
					
						
							|  |  |  |       // Empty message map -> use source messages in Warning mode
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Warning); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |       expect(serializeNodes(tb.get(messages[0])).join('')).toEqual(src); | 
					
						
							|  |  |  |       // Empty message map -> throw in Error mode
 | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |       tb = new TranslationBundle({}, null, digest, null!, MissingTranslationStrategy.Error); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |       expect(() => serializeNodes(tb.get(messages[0])).join('')).toThrow(); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     describe('errors reporting', () => { | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       it('should report unknown placeholders', () => { | 
					
						
							|  |  |  |         const msgMap = { | 
					
						
							|  |  |  |           foo: [ | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |             new i18n.Text('bar', null!), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |             new i18n.Placeholder('', 'ph1', span), | 
					
						
							|  |  |  |           ] | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |         const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |         const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         expect(() => tb.get(msg)).toThrowError(/Unknown placeholder/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should report missing translation', () => { | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |         const tb = | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |             new TranslationBundle({}, null, (_) => 'foo', null!, MissingTranslationStrategy.Error); | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |         const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |         expect(() => tb.get(msg)).toThrowError(/Missing translation for message "foo"/); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  |       it('should report missing translation with MissingTranslationStrategy.Warning', () => { | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |         const log: string[] = []; | 
					
						
							|  |  |  |         const console = { | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |           log: (msg: string) => { | 
					
						
							|  |  |  |             throw `unexpected`; | 
					
						
							|  |  |  |           }, | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |           warn: (msg: string) => log.push(msg), | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  |         const tb = new TranslationBundle( | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |             {}, 'en', (_) => 'foo', null!, MissingTranslationStrategy.Warning, console); | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  |         const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         expect(() => tb.get(msg)).not.toThrowError(); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |         expect(log.length).toEqual(1); | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |         expect(log[0]).toMatch(/Missing translation for message "foo" for locale "en"/); | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should not report missing translation with MissingTranslationStrategy.Ignore', () => { | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |         const tb = | 
					
						
							|  |  |  |             new TranslationBundle({}, null, (_) => 'foo', null!, MissingTranslationStrategy.Ignore); | 
					
						
							| 
									
										
										
										
											2017-01-10 14:14:41 +01:00
										 |  |  |         const msg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							|  |  |  |         expect(() => tb.get(msg)).not.toThrowError(); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       it('should report missing referenced message', () => { | 
					
						
							|  |  |  |         const msgMap = { | 
					
						
							|  |  |  |           foo: [new i18n.Placeholder('', 'ph1', span)], | 
					
						
							|  |  |  |         }; | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |         const refMsg = new i18n.Message([srcNode], {}, {}, 'm', 'd', 'i'); | 
					
						
							|  |  |  |         const msg = new i18n.Message([srcNode], {}, {ph1: refMsg}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         let count = 0; | 
					
						
							|  |  |  |         const digest = (_: any) => count++ ? 'ref' : 'foo'; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |         const tb = | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |             new TranslationBundle(msgMap, null, digest, null!, MissingTranslationStrategy.Error); | 
					
						
							| 
									
										
										
										
											2017-01-25 23:26:49 -08:00
										 |  |  |         expect(() => tb.get(msg)).toThrowError(/Missing translation for message "ref"/); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |       }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |       it('should report invalid translated html', () => { | 
					
						
							|  |  |  |         const msgMap = { | 
					
						
							|  |  |  |           foo: [ | 
					
						
							| 
									
										
										
										
											2020-04-08 10:14:18 -07:00
										 |  |  |             new i18n.Text('text', null!), | 
					
						
							|  |  |  |             new i18n.Placeholder('', 'ph1', null!), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |           ] | 
					
						
							|  |  |  |         }; | 
					
						
							|  |  |  |         const phMap = { | 
					
						
							| 
									
										
										
										
											2020-10-01 00:17:21 +02:00
										 |  |  |           ph1: createPlaceholder('</b>'), | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         }; | 
					
						
							| 
									
										
										
										
											2017-02-03 14:29:28 -08:00
										 |  |  |         const tb = new TranslationBundle(msgMap, null, (_) => 'foo'); | 
					
						
							| 
									
										
										
										
											2016-12-06 15:04:59 +01:00
										 |  |  |         const msg = new i18n.Message([srcNode], phMap, {}, 'm', 'd', 'i'); | 
					
						
							| 
									
										
										
										
											2016-11-02 17:40:15 -07:00
										 |  |  |         expect(() => tb.get(msg)).toThrowError(/Unexpected closing tag "b"/); | 
					
						
							|  |  |  |       }); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  |   }); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2020-10-01 00:17:21 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | function createPlaceholder(text: string): i18n.MessagePlaceholder { | 
					
						
							|  |  |  |   const file = new ParseSourceFile(text, 'file://test'); | 
					
						
							|  |  |  |   const start = new ParseLocation(file, 0, 0, 0); | 
					
						
							|  |  |  |   const end = new ParseLocation(file, text.length, 0, text.length); | 
					
						
							|  |  |  |   return { | 
					
						
							|  |  |  |     text, | 
					
						
							|  |  |  |     sourceSpan: new ParseSourceSpan(start, end), | 
					
						
							|  |  |  |   }; | 
					
						
							|  |  |  | } |