Compare commits
14 Commits
Author | SHA1 | Date | |
---|---|---|---|
7ed39ebaaf | |||
091f0a5aaa | |||
315606e02c | |||
5ea373d184 | |||
6e36bb7b20 | |||
3b2fb23805 | |||
bd2eecb4de | |||
3d351a4f5f | |||
5492fada21 | |||
fd4f9acbcf | |||
48528a86e1 | |||
80364def27 | |||
1803beb4d5 | |||
3bcba8a570 |
13
CHANGELOG.md
13
CHANGELOG.md
@ -1,3 +1,16 @@
|
||||
<a name="2.4.5"></a>
|
||||
## [2.4.5](https://github.com/angular/angular/compare/2.4.4...2.4.5) (2017-01-25)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **compiler:** [i18n] XMB/XTB placeholder names can contain only A-Z, 0-9, _n ([5492fad](https://github.com/angular/angular/commit/5492fad))
|
||||
* **compiler:** fix regexp to support firefox 31 ([#14082](https://github.com/angular/angular/issues/14082)) ([bd2eecb](https://github.com/angular/angular/commit/bd2eecb)), closes [#14029](https://github.com/angular/angular/issues/14029) [#13900](https://github.com/angular/angular/issues/13900)
|
||||
* **core:** export animation classes required for Renderer impl ([#14002](https://github.com/angular/angular/issues/14002)) ([fd4f9ac](https://github.com/angular/angular/commit/fd4f9ac)), closes [#14001](https://github.com/angular/angular/issues/14001)
|
||||
* **upgrade:** ensure upgraded injector is initialized early enough ([#14065](https://github.com/angular/angular/issues/14065)) ([3b2fb23](https://github.com/angular/angular/commit/3b2fb23)), closes [#13811](https://github.com/angular/angular/issues/13811)
|
||||
|
||||
|
||||
|
||||
<a name="2.4.4"></a>
|
||||
## [2.4.4](https://github.com/angular/angular/compare/2.4.3...2.4.4) (2017-01-19)
|
||||
|
||||
|
@ -1,10 +1,10 @@
|
||||
machine:
|
||||
node:
|
||||
version: 5.4.1
|
||||
version: 6.6.0
|
||||
|
||||
dependencies:
|
||||
pre:
|
||||
- npm install -g npm@3.6.0
|
||||
- npm install -g npm@3.5.3
|
||||
|
||||
test:
|
||||
override:
|
||||
|
@ -24,7 +24,7 @@ import {InvalidPipeArgumentError} from './invalid_pipe_argument_error';
|
||||
* Where:
|
||||
* - `expression` is a date object or a number (milliseconds since UTC epoch) or an ISO string
|
||||
* (https://www.w3.org/TR/NOTE-datetime).
|
||||
* - `format` indicates which date/time components to include. The format can be predifined as
|
||||
* - `format` indicates which date/time components to include. The format can be predefined as
|
||||
* shown below or custom as shown in the table.
|
||||
* - `'medium'`: equivalent to `'yMMMdjms'` (e.g. `Sep 3, 2010, 12:05:08 PM` for `en-US`)
|
||||
* - `'short'`: equivalent to `'yMdjm'` (e.g. `9/3/2010, 12:05 PM` for `en-US`)
|
||||
|
@ -52,7 +52,7 @@ export class Extractor {
|
||||
|
||||
extract(rootFiles: string[]): Promise<MessageBundle> {
|
||||
const programSymbols = extractProgramSymbols(this.staticSymbolResolver, rootFiles, this.host);
|
||||
const {ngModuleByPipeOrDirective, files, ngModules} =
|
||||
const {files, ngModules} =
|
||||
analyzeAndValidateNgModules(programSymbols, this.host, this.metadataResolver);
|
||||
return Promise
|
||||
.all(ngModules.map(
|
||||
|
@ -111,8 +111,14 @@ export class PlaceholderRegistry {
|
||||
private _hashClosingTag(tag: string): string { return this._hashTag(`/${tag}`, {}, false); }
|
||||
|
||||
private _generateUniqueName(base: string): string {
|
||||
const next = this._placeHolderNameCounts[base];
|
||||
this._placeHolderNameCounts[base] = next ? next + 1 : 1;
|
||||
return next ? `${base}_${next}` : base;
|
||||
const seen = this._placeHolderNameCounts.hasOwnProperty(base);
|
||||
if (!seen) {
|
||||
this._placeHolderNameCounts[base] = 1;
|
||||
return base;
|
||||
}
|
||||
|
||||
const id = this._placeHolderNameCounts[base];
|
||||
this._placeHolderNameCounts[base] = id + 1;
|
||||
return `${base}_${id}`;
|
||||
}
|
||||
}
|
||||
|
@ -8,10 +8,26 @@
|
||||
|
||||
import * as i18n from '../i18n_ast';
|
||||
|
||||
export interface Serializer {
|
||||
write(messages: i18n.Message[]): string;
|
||||
export abstract class Serializer {
|
||||
abstract write(messages: i18n.Message[]): string;
|
||||
|
||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
||||
abstract load(content: string, url: string): {[msgId: string]: i18n.Node[]};
|
||||
|
||||
digest(message: i18n.Message): string;
|
||||
abstract digest(message: i18n.Message): string;
|
||||
|
||||
// Creates a name mapper, see `PlaceholderMapper`
|
||||
// Returning `null` means that no name mapping is used.
|
||||
createNameMapper(message: i18n.Message): PlaceholderMapper { return null; }
|
||||
}
|
||||
|
||||
/**
|
||||
* A `PlaceholderMapper` converts placeholder names from internal to serialized representation and
|
||||
* back.
|
||||
*
|
||||
* It should be used for serialization format that put constraints on the placeholder names.
|
||||
*/
|
||||
export interface PlaceholderMapper {
|
||||
toPublicName(internalName: string): string;
|
||||
|
||||
toInternalName(publicName: string): string;
|
||||
}
|
@ -27,7 +27,7 @@ const _UNIT_TAG = 'trans-unit';
|
||||
|
||||
// http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html
|
||||
// http://docs.oasis-open.org/xliff/v1.2/xliff-profile-html/xliff-profile-html-1.2.html
|
||||
export class Xliff implements Serializer {
|
||||
export class Xliff extends Serializer {
|
||||
write(messages: i18n.Message[]): string {
|
||||
const visitor = new _WriteVisitor();
|
||||
const visited: {[id: string]: boolean} = {};
|
||||
|
@ -9,7 +9,7 @@
|
||||
import {decimalDigest} from '../digest';
|
||||
import * as i18n from '../i18n_ast';
|
||||
|
||||
import {Serializer} from './serializer';
|
||||
import {PlaceholderMapper, Serializer} from './serializer';
|
||||
import * as xml from './xml_helper';
|
||||
|
||||
const _MESSAGES_TAG = 'messagebundle';
|
||||
@ -37,7 +37,7 @@ const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
|
||||
|
||||
<!ELEMENT ex (#PCDATA)>`;
|
||||
|
||||
export class Xmb implements Serializer {
|
||||
export class Xmb extends Serializer {
|
||||
write(messages: i18n.Message[]): string {
|
||||
const exampleVisitor = new ExampleVisitor();
|
||||
const visitor = new _Visitor();
|
||||
@ -51,6 +51,8 @@ export class Xmb implements Serializer {
|
||||
if (visited[id]) return;
|
||||
visited[id] = true;
|
||||
|
||||
const mapper = this.createNameMapper(message);
|
||||
|
||||
const attrs: {[k: string]: string} = {id};
|
||||
|
||||
if (message.description) {
|
||||
@ -62,7 +64,8 @@ export class Xmb implements Serializer {
|
||||
}
|
||||
|
||||
rootNode.children.push(
|
||||
new xml.CR(2), new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes)));
|
||||
new xml.CR(2),
|
||||
new xml.Tag(_MESSAGE_TAG, attrs, visitor.serialize(message.nodes, {mapper})));
|
||||
});
|
||||
|
||||
rootNode.children.push(new xml.CR());
|
||||
@ -82,22 +85,29 @@ export class Xmb implements Serializer {
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
|
||||
|
||||
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||
return new XmbPlaceholderMapper(message);
|
||||
}
|
||||
}
|
||||
|
||||
class _Visitor implements i18n.Visitor {
|
||||
visitText(text: i18n.Text, context?: any): xml.Node[] { return [new xml.Text(text.value)]; }
|
||||
visitText(text: i18n.Text, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
return [new xml.Text(text.value)];
|
||||
}
|
||||
|
||||
visitContainer(container: i18n.Container, context?: any): xml.Node[] {
|
||||
visitContainer(container: i18n.Container, ctx: any): xml.Node[] {
|
||||
const nodes: xml.Node[] = [];
|
||||
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this)));
|
||||
container.children.forEach((node: i18n.Node) => nodes.push(...node.visit(this, ctx)));
|
||||
return nodes;
|
||||
}
|
||||
|
||||
visitIcu(icu: i18n.Icu, context?: any): xml.Node[] {
|
||||
visitIcu(icu: i18n.Icu, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
const nodes = [new xml.Text(`{${icu.expressionPlaceholder}, ${icu.type}, `)];
|
||||
|
||||
Object.keys(icu.cases).forEach((c: string) => {
|
||||
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this), new xml.Text(`} `));
|
||||
nodes.push(new xml.Text(`${c} {`), ...icu.cases[c].visit(this, ctx), new xml.Text(`} `));
|
||||
});
|
||||
|
||||
nodes.push(new xml.Text(`}`));
|
||||
@ -105,30 +115,34 @@ class _Visitor implements i18n.Visitor {
|
||||
return nodes;
|
||||
}
|
||||
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, context?: any): xml.Node[] {
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
const startEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`<${ph.tag}>`)]);
|
||||
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.startName}, [startEx]);
|
||||
let name = ctx.mapper.toPublicName(ph.startName);
|
||||
const startTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [startEx]);
|
||||
if (ph.isVoid) {
|
||||
// void tags have no children nor closing tags
|
||||
return [startTagPh];
|
||||
}
|
||||
|
||||
const closeEx = new xml.Tag(_EXEMPLE_TAG, {}, [new xml.Text(`</${ph.tag}>`)]);
|
||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name: ph.closeName}, [closeEx]);
|
||||
name = ctx.mapper.toPublicName(ph.closeName);
|
||||
const closeTagPh = new xml.Tag(_PLACEHOLDER_TAG, {name}, [closeEx]);
|
||||
|
||||
return [startTagPh, ...this.serialize(ph.children), closeTagPh];
|
||||
return [startTagPh, ...this.serialize(ph.children, ctx), closeTagPh];
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||
visitPlaceholder(ph: i18n.Placeholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
const name = ctx.mapper.toPublicName(ph.name);
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
||||
}
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): xml.Node[] {
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name: ph.name})];
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
const name = ctx.mapper.toPublicName(ph.name);
|
||||
return [new xml.Tag(_PLACEHOLDER_TAG, {name})];
|
||||
}
|
||||
|
||||
serialize(nodes: i18n.Node[]): xml.Node[] {
|
||||
return [].concat(...nodes.map(node => node.visit(this)));
|
||||
serialize(nodes: i18n.Node[], ctx: {mapper: PlaceholderMapper}): xml.Node[] {
|
||||
return [].concat(...nodes.map(node => node.visit(this, ctx)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -158,3 +172,69 @@ class ExampleVisitor implements xml.IVisitor {
|
||||
visitDeclaration(decl: xml.Declaration): void {}
|
||||
visitDoctype(doctype: xml.Doctype): void {}
|
||||
}
|
||||
|
||||
/**
|
||||
* XMB/XTB placeholders can only contain A-Z, 0-9 and _
|
||||
*
|
||||
* Because such restrictions do not exist on placeholder names generated locally, the
|
||||
* `PlaceholderMapper` is used to convert internal names to XMB names when the XMB file is
|
||||
* serialized and back from XTB to internal names when an XTB is loaded.
|
||||
*/
|
||||
export class XmbPlaceholderMapper implements PlaceholderMapper, i18n.Visitor {
|
||||
private internalToXmb: {[k: string]: string} = {};
|
||||
private xmbToNextId: {[k: string]: number} = {};
|
||||
private xmbToInternal: {[k: string]: string} = {};
|
||||
|
||||
// create a mapping from the message
|
||||
constructor(message: i18n.Message) { message.nodes.forEach(node => node.visit(this)); }
|
||||
|
||||
toPublicName(internalName: string): string {
|
||||
return this.internalToXmb.hasOwnProperty(internalName) ? this.internalToXmb[internalName] :
|
||||
null;
|
||||
}
|
||||
|
||||
toInternalName(publicName: string): string {
|
||||
return this.xmbToInternal.hasOwnProperty(publicName) ? this.xmbToInternal[publicName] : null;
|
||||
}
|
||||
|
||||
visitText(text: i18n.Text, ctx?: any): any { return null; }
|
||||
|
||||
visitContainer(container: i18n.Container, ctx?: any): any {
|
||||
container.children.forEach(child => child.visit(this));
|
||||
}
|
||||
|
||||
visitIcu(icu: i18n.Icu, ctx?: any): any {
|
||||
Object.keys(icu.cases).forEach(k => { icu.cases[k].visit(this); });
|
||||
}
|
||||
|
||||
visitTagPlaceholder(ph: i18n.TagPlaceholder, ctx?: any): any {
|
||||
this.addPlaceholder(ph.startName);
|
||||
ph.children.forEach(child => child.visit(this));
|
||||
this.addPlaceholder(ph.closeName);
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, ctx?: any): any { this.addPlaceholder(ph.name); }
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, ctx?: any): any { this.addPlaceholder(ph.name); }
|
||||
|
||||
// XMB placeholders could only contains A-Z, 0-9 and _
|
||||
private addPlaceholder(internalName: string): void {
|
||||
if (!internalName || this.internalToXmb.hasOwnProperty(internalName)) {
|
||||
return;
|
||||
}
|
||||
|
||||
let xmbName = internalName.toUpperCase().replace(/[^A-Z0-9_]/g, '_');
|
||||
|
||||
if (this.xmbToInternal.hasOwnProperty(xmbName)) {
|
||||
// Create a new XMB when it has already been used
|
||||
const nextId = this.xmbToNextId[xmbName];
|
||||
this.xmbToNextId[xmbName] = nextId + 1;
|
||||
xmbName = `${xmbName}_${nextId}`;
|
||||
} else {
|
||||
this.xmbToNextId[xmbName] = 1;
|
||||
}
|
||||
|
||||
this.internalToXmb[internalName] = xmbName;
|
||||
this.xmbToInternal[xmbName] = internalName;
|
||||
}
|
||||
}
|
||||
|
@ -11,14 +11,14 @@ import {XmlParser} from '../../ml_parser/xml_parser';
|
||||
import * as i18n from '../i18n_ast';
|
||||
import {I18nError} from '../parse_util';
|
||||
|
||||
import {Serializer} from './serializer';
|
||||
import {digest} from './xmb';
|
||||
import {PlaceholderMapper, Serializer} from './serializer';
|
||||
import {XmbPlaceholderMapper, digest} from './xmb';
|
||||
|
||||
const _TRANSLATIONS_TAG = 'translationbundle';
|
||||
const _TRANSLATION_TAG = 'translation';
|
||||
const _PLACEHOLDER_TAG = 'ph';
|
||||
|
||||
export class Xtb implements Serializer {
|
||||
export class Xtb extends Serializer {
|
||||
write(messages: i18n.Message[]): string { throw new Error('Unsupported'); }
|
||||
|
||||
load(content: string, url: string): {[msgId: string]: i18n.Node[]} {
|
||||
@ -43,6 +43,10 @@ export class Xtb implements Serializer {
|
||||
}
|
||||
|
||||
digest(message: i18n.Message): string { return digest(message); }
|
||||
|
||||
createNameMapper(message: i18n.Message): PlaceholderMapper {
|
||||
return new XmbPlaceholderMapper(message);
|
||||
}
|
||||
}
|
||||
|
||||
// Extract messages as xml nodes from the xtb file
|
||||
|
@ -11,7 +11,7 @@ import {HtmlParser} from '../ml_parser/html_parser';
|
||||
|
||||
import * as i18n from './i18n_ast';
|
||||
import {I18nError} from './parse_util';
|
||||
import {Serializer} from './serializers/serializer';
|
||||
import {PlaceholderMapper, Serializer} from './serializers/serializer';
|
||||
|
||||
/**
|
||||
* A container for translated messages
|
||||
@ -21,16 +21,20 @@ export class TranslationBundle {
|
||||
|
||||
constructor(
|
||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||
public digest: (m: i18n.Message) => string) {
|
||||
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest);
|
||||
public digest: (m: i18n.Message) => string,
|
||||
public mapperFactory?: (m: i18n.Message) => PlaceholderMapper) {
|
||||
this._i18nToHtml = new I18nToHtmlVisitor(_i18nNodesByMsgId, digest, mapperFactory);
|
||||
}
|
||||
|
||||
// Creates a `TranslationBundle` by parsing the given `content` with the `serializer`.
|
||||
static load(content: string, url: string, serializer: Serializer): TranslationBundle {
|
||||
const i18nNodesByMsgId = serializer.load(content, url);
|
||||
const digestFn = (m: i18n.Message) => serializer.digest(m);
|
||||
return new TranslationBundle(i18nNodesByMsgId, digestFn);
|
||||
const mapperFactory = (m: i18n.Message) => serializer.createNameMapper(m);
|
||||
return new TranslationBundle(i18nNodesByMsgId, digestFn, mapperFactory);
|
||||
}
|
||||
|
||||
// Returns the translation as HTML nodes from the given source message.
|
||||
get(srcMsg: i18n.Message): html.Node[] {
|
||||
const html = this._i18nToHtml.convert(srcMsg);
|
||||
|
||||
@ -46,15 +50,17 @@ export class TranslationBundle {
|
||||
|
||||
class I18nToHtmlVisitor implements i18n.Visitor {
|
||||
private _srcMsg: i18n.Message;
|
||||
private _srcMsgStack: i18n.Message[] = [];
|
||||
private _contextStack: {msg: i18n.Message, mapper: (name: string) => string}[] = [];
|
||||
private _errors: I18nError[] = [];
|
||||
private _mapper: (name: string) => string;
|
||||
|
||||
constructor(
|
||||
private _i18nNodesByMsgId: {[msgId: string]: i18n.Node[]} = {},
|
||||
private _digest: (m: i18n.Message) => string) {}
|
||||
private _digest: (m: i18n.Message) => string,
|
||||
private _mapperFactory: (m: i18n.Message) => PlaceholderMapper) {}
|
||||
|
||||
convert(srcMsg: i18n.Message): {nodes: html.Node[], errors: I18nError[]} {
|
||||
this._srcMsgStack.length = 0;
|
||||
this._contextStack.length = 0;
|
||||
this._errors.length = 0;
|
||||
// i18n to text
|
||||
const text = this._convertToText(srcMsg);
|
||||
@ -88,7 +94,7 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
||||
}
|
||||
|
||||
visitPlaceholder(ph: i18n.Placeholder, context?: any): string {
|
||||
const phName = ph.name;
|
||||
const phName = this._mapper(ph.name);
|
||||
if (this._srcMsg.placeholders.hasOwnProperty(phName)) {
|
||||
return this._srcMsg.placeholders[phName];
|
||||
}
|
||||
@ -105,14 +111,26 @@ class I18nToHtmlVisitor implements i18n.Visitor {
|
||||
|
||||
visitIcuPlaceholder(ph: i18n.IcuPlaceholder, context?: any): any { throw 'unreachable code'; }
|
||||
|
||||
/**
|
||||
* Convert a source message to a translated text string:
|
||||
* - text nodes are replaced with their translation,
|
||||
* - placeholders are replaced with their content,
|
||||
* - ICU nodes are converted to ICU expressions.
|
||||
*/
|
||||
private _convertToText(srcMsg: i18n.Message): string {
|
||||
const digest = this._digest(srcMsg);
|
||||
const mapper = this._mapperFactory ? this._mapperFactory(srcMsg) : null;
|
||||
|
||||
if (this._i18nNodesByMsgId.hasOwnProperty(digest)) {
|
||||
this._srcMsgStack.push(this._srcMsg);
|
||||
this._contextStack.push({msg: this._srcMsg, mapper: this._mapper});
|
||||
this._srcMsg = srcMsg;
|
||||
this._mapper = (name: string) => mapper ? mapper.toInternalName(name) : name;
|
||||
|
||||
const nodes = this._i18nNodesByMsgId[digest];
|
||||
const text = nodes.map(node => node.visit(this)).join('');
|
||||
this._srcMsg = this._srcMsgStack.pop();
|
||||
const context = this._contextStack.pop();
|
||||
this._srcMsg = context.msg;
|
||||
this._mapper = context.mapper;
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@ -9,10 +9,11 @@
|
||||
import {getHtmlTagDefinition} from './ml_parser/html_tags';
|
||||
|
||||
const _SELECTOR_REGEXP = new RegExp(
|
||||
'(\\:not\\()|' + //":not("
|
||||
'([-\\w]+)|' + // "tag"
|
||||
'(?:\\.([-\\w]+))|' + // ".class"
|
||||
'(?:\\[([.-\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
|
||||
'(\\:not\\()|' + //":not("
|
||||
'([-\\w]+)|' + // "tag"
|
||||
'(?:\\.([-\\w]+))|' + // ".class"
|
||||
// "-" should appear first in the regexp below as FF31 parses "[.-\w]" as a range
|
||||
'(?:\\[([-.\\w*]+)(?:=([^\\]]*))?\\])|' + // "[name]", "[name=value]"
|
||||
'(\\))|' + // ")"
|
||||
'(\\s*,\\s*)', // ","
|
||||
'g');
|
||||
|
@ -182,6 +182,12 @@ const XTB = `
|
||||
<ph name="START_TAG_DIV_1"/><ph name="ICU"/><ph name="CLOSE_TAG_DIV"></ph>
|
||||
</translation>
|
||||
<translation id="1491627405349178954">ca <ph name="START_BOLD_TEXT"/>devrait<ph name="CLOSE_BOLD_TEXT"/> marcher</translation>
|
||||
<translation id="i18n16">avec un ID explicite</translation>
|
||||
<translation id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {un} =2 {deux} other {<ph
|
||||
name="START_BOLD_TEXT"><ex><b></ex></ph>beaucoup<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</translation>
|
||||
<translation id="4085484936881858615">{VAR_PLURAL, plural, =0 {Pas de réponse} =1 {une réponse} other {<ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> réponse} }</translation>
|
||||
<translation id="4035252431381981115">FOO<ph name="START_LINK"><ex><a></ex></ph>BAR<ph name="CLOSE_LINK"><ex></a></ex></ph></translation>
|
||||
<translation id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></translation>
|
||||
</translationbundle>`;
|
||||
|
||||
// unused, for reference only
|
||||
@ -210,6 +216,11 @@ const XMB = `
|
||||
<ph name="START_TAG_DIV_1"><ex><div></ex></ph><ph name="ICU"/><ph name="CLOSE_TAG_DIV"><ex></div></ex></ph>
|
||||
</msg>
|
||||
<msg id="1491627405349178954">it <ph name="START_BOLD_TEXT"><ex><b></ex></ph>should<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph> work</msg>
|
||||
<msg id="i18n16">with an explicit ID</msg>
|
||||
<msg id="i18n17">{VAR_PLURAL, plural, =0 {zero} =1 {one} =2 {two} other {<ph name="START_BOLD_TEXT"><ex><b></ex></ph>many<ph name="CLOSE_BOLD_TEXT"><ex></b></ex></ph>} }</msg>
|
||||
<msg id="4085484936881858615" desc="desc">{VAR_PLURAL, plural, =0 {Found no results} =1 {Found one result} other {Found <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> results} }</msg>
|
||||
<msg id="4035252431381981115">foo<ph name="START_LINK"><ex><a></ex></ph>bar<ph name="CLOSE_LINK"><ex></a></ex></ph></msg>
|
||||
<msg id="5339604010413301604"><ph name="MAP_NAME"><ex>MAP_NAME</ex></ph></msg>;
|
||||
</messagebundle>`;
|
||||
|
||||
const HTML = `
|
||||
@ -262,4 +273,6 @@ const HTML = `
|
||||
}</div>
|
||||
|
||||
<div i18n id="i18n-18">foo<a i18n-title title="in a translatable section">bar</a></div>
|
||||
|
||||
<div i18n>{{ 'test' //i18n(ph="map name") }}</div>
|
||||
`;
|
||||
|
@ -42,7 +42,7 @@ export function main(): void {
|
||||
});
|
||||
}
|
||||
|
||||
class _TestSerializer implements Serializer {
|
||||
class _TestSerializer extends Serializer {
|
||||
write(messages: i18n.Message[]): string {
|
||||
return messages.map(msg => `${serializeNodes(msg.nodes)} (${msg.meaning}|${msg.description})`)
|
||||
.join('//');
|
||||
|
@ -8,6 +8,16 @@
|
||||
|
||||
import {AnimationStyles} from './animation_styles';
|
||||
|
||||
/**
|
||||
* `AnimationKeyframe` consists of a series of styles (contained within {@link AnimationStyles
|
||||
* `AnimationStyles`})
|
||||
* and an offset value indicating when those styles are applied within the `duration/delay/easing`
|
||||
* timings.
|
||||
* `AnimationKeyframe` is mostly an internal class which is designed to be used alongside {@link
|
||||
* Renderer#animate-anchor `Renderer.animate`}.
|
||||
*
|
||||
* @experimental Animation support is experimental
|
||||
*/
|
||||
export class AnimationKeyframe {
|
||||
constructor(public offset: number, public styles: AnimationStyles) {}
|
||||
}
|
||||
|
@ -6,6 +6,20 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
// having an import prevents dgeni from truncating out
|
||||
// the class description in the docs. DO NOT REMOVE.
|
||||
import {isPresent} from '../facade/lang';
|
||||
|
||||
/**
|
||||
* `AnimationStyles` consists of a collection of key/value maps containing CSS-based style data
|
||||
* that can either be used as initial styling data or apart of a series of keyframes within an
|
||||
* animation.
|
||||
* This class is mostly internal, and it is designed to be used alongside
|
||||
* {@link AnimationKeyframe `AnimationKeyframe`} and {@link Renderer#animate-anchor
|
||||
* `Renderer.animate`}.
|
||||
*
|
||||
* @experimental Animation support is experimental
|
||||
*/
|
||||
export class AnimationStyles {
|
||||
constructor(public styles: {[key: string]: string | number}[]) {}
|
||||
}
|
||||
|
@ -35,4 +35,6 @@ export * from './core_private_export';
|
||||
export * from './animation/metadata';
|
||||
export {AnimationTransitionEvent} from './animation/animation_transition_event';
|
||||
export {AnimationPlayer} from './animation/animation_player';
|
||||
export {AnimationStyles} from './animation/animation_styles';
|
||||
export {AnimationKeyframe} from './animation/animation_keyframe';
|
||||
export {Sanitizer, SecurityContext} from './security';
|
||||
|
@ -6,6 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Determine if the argument is shaped like a Promise
|
||||
*/
|
||||
export function isPromise(obj: any): obj is Promise<any> {
|
||||
// allow any Promise/A+ compliant thenable.
|
||||
// It's up to the caller to ensure that obj.then conforms to the spec
|
||||
|
@ -6,6 +6,9 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
/**
|
||||
* Convenience to throw an Error with 'unimplemented' as the message.
|
||||
*/
|
||||
export function unimplemented(): any {
|
||||
throw new Error('unimplemented');
|
||||
}
|
||||
|
@ -216,7 +216,7 @@ export interface CanActivateChild {
|
||||
* {
|
||||
* path: 'team/:id',
|
||||
* component: TeamCmp,
|
||||
* canActivate: ['canDeactivateTeam']
|
||||
* canDeactivate: ['canDeactivateTeam']
|
||||
* }
|
||||
* ])
|
||||
* ],
|
||||
|
@ -150,10 +150,12 @@ export class UpgradeModule {
|
||||
*/
|
||||
bootstrap(
|
||||
element: Element, modules: string[] = [], config?: any /*angular.IAngularBootstrapConfig*/) {
|
||||
const INIT_MODULE_NAME = UPGRADE_MODULE_NAME + '.init';
|
||||
|
||||
// Create an ng1 module to bootstrap
|
||||
const upgradeModule =
|
||||
const initModule =
|
||||
angular
|
||||
.module(UPGRADE_MODULE_NAME, modules)
|
||||
.module(INIT_MODULE_NAME, [])
|
||||
|
||||
.value(INJECTOR_KEY, this.injector)
|
||||
|
||||
@ -205,6 +207,8 @@ export class UpgradeModule {
|
||||
}
|
||||
]);
|
||||
|
||||
const upgradeModule = angular.module(UPGRADE_MODULE_NAME, [INIT_MODULE_NAME].concat(modules));
|
||||
|
||||
// Make sure resumeBootstrap() only exists if the current bootstrap is deferred
|
||||
const windowAngular = (window as any /** TODO #???? */)['angular'];
|
||||
windowAngular.resumeBootstrap = undefined;
|
||||
|
@ -6,11 +6,12 @@
|
||||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
|
||||
import {NgModule, OpaqueToken, destroyPlatform} from '@angular/core';
|
||||
import {Injector, NgModule, OpaqueToken, destroyPlatform} from '@angular/core';
|
||||
import {async} from '@angular/core/testing';
|
||||
import {BrowserModule} from '@angular/platform-browser';
|
||||
import {platformBrowserDynamic} from '@angular/platform-browser-dynamic';
|
||||
import * as angular from '@angular/upgrade/src/angular_js';
|
||||
import {$INJECTOR, INJECTOR_KEY} from '@angular/upgrade/src/aot/constants';
|
||||
import {UpgradeModule, downgradeInjectable} from '@angular/upgrade/static';
|
||||
|
||||
import {bootstrap, html} from '../test_helpers';
|
||||
@ -76,5 +77,27 @@ export function main() {
|
||||
expect(ng2Injector.get(Ng1Service)).toBe('ng1 service value');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should initialize the upgraded injector before application run blocks are executed',
|
||||
async(() => {
|
||||
let runBlockTriggered = false;
|
||||
|
||||
const ng1Module = angular.module('ng1Module', []).run([
|
||||
INJECTOR_KEY,
|
||||
function(injector: Injector) {
|
||||
runBlockTriggered = true;
|
||||
expect(injector.get($INJECTOR)).toBeDefined();
|
||||
}
|
||||
]);
|
||||
|
||||
@NgModule({imports: [BrowserModule, UpgradeModule]})
|
||||
class Ng2Module {
|
||||
ngDoBootstrap() {}
|
||||
}
|
||||
|
||||
bootstrap(platformBrowserDynamic(), Ng2Module, html('<div>'), ng1Module).then(() => {
|
||||
expect(runBlockTriggered).toBeTruthy();
|
||||
});
|
||||
}));
|
||||
});
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "angular-srcs",
|
||||
"version": "2.4.4",
|
||||
"version": "2.4.5",
|
||||
"private": true,
|
||||
"branchPattern": "2.0.*",
|
||||
"description": "Angular 2 - a web framework for modern web apps",
|
||||
|
17
tools/public_api_guard/core/index.d.ts
vendored
17
tools/public_api_guard/core/index.d.ts
vendored
@ -44,6 +44,13 @@ export declare class AnimationGroupMetadata extends AnimationWithStepsMetadata {
|
||||
constructor(_steps: AnimationMetadata[]);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationKeyframe {
|
||||
offset: number;
|
||||
styles: AnimationStyles;
|
||||
constructor(offset: number, styles: AnimationStyles);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationKeyframesSequenceMetadata extends AnimationMetadata {
|
||||
steps: AnimationStyleMetadata[];
|
||||
@ -106,6 +113,16 @@ export declare class AnimationStyleMetadata extends AnimationMetadata {
|
||||
}>, offset?: number);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationStyles {
|
||||
styles: {
|
||||
[key: string]: string | number;
|
||||
}[];
|
||||
constructor(styles: {
|
||||
[key: string]: string | number;
|
||||
}[]);
|
||||
}
|
||||
|
||||
/** @experimental */
|
||||
export declare class AnimationTransitionEvent {
|
||||
fromState: string;
|
||||
|
Reference in New Issue
Block a user