feat(compiler): add source files to xmb/xliff translations (#14705)

Fixes #14190
This commit is contained in:
Olivier Combe
2017-04-14 18:06:25 +02:00
committed by Tobias Bosch
parent 09c4cb2540
commit 4054055d0d
15 changed files with 311 additions and 54 deletions

View File

@ -9,6 +9,8 @@
import {ParseSourceSpan} from '../parse_util';
export class Message {
sources: MessageSpan[];
/**
* @param nodes message AST
* @param placeholders maps placeholder names to static content
@ -20,7 +22,28 @@ export class Message {
constructor(
public nodes: Node[], public placeholders: {[phName: string]: string},
public placeholderToMessage: {[phName: string]: Message}, public meaning: string,
public description: string, public id: string) {}
public description: string, public id: string) {
if (nodes.length) {
this.sources = [{
filePath: nodes[0].sourceSpan.start.file.url,
startLine: nodes[0].sourceSpan.start.line + 1,
startCol: nodes[0].sourceSpan.start.col + 1,
endLine: nodes[nodes.length - 1].sourceSpan.end.line + 1,
endCol: nodes[0].sourceSpan.start.col + 1
}];
} else {
this.sources = [];
}
}
}
// line and columns indexes are 1 based
export interface MessageSpan {
filePath: string;
startLine: number;
startCol: number;
endLine: number;
endCol: number;
}
export interface Node {
@ -131,4 +154,4 @@ export class RecurseVisitor implements Visitor {
visitPlaceholder(ph: Placeholder, context?: any): any{};
visitIcuPlaceholder(ph: IcuPlaceholder, context?: any): any{};
}
}

View File

@ -48,7 +48,7 @@ export class MessageBundle {
// The public (serialized) format might be different, see the `write` method.
getMessages(): i18n.Message[] { return this._messages; }
write(serializer: Serializer): string {
write(serializer: Serializer, filterSources?: (path: string) => string): string {
const messages: {[id: string]: i18n.Message} = {};
const mapperVisitor = new MapPlaceholderNames();
@ -57,6 +57,8 @@ export class MessageBundle {
const id = serializer.digest(message);
if (!messages.hasOwnProperty(id)) {
messages[id] = message;
} else {
messages[id].sources.push(...message.sources);
}
});
@ -65,7 +67,13 @@ export class MessageBundle {
const mapper = serializer.createNameMapper(messages[id]);
const src = messages[id];
const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes;
return new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
let transformedMessage = new i18n.Message(nodes, {}, {}, src.meaning, src.description, id);
transformedMessage.sources = src.sources;
if (filterSources) {
transformedMessage.sources.forEach(
(source: i18n.MessageSpan) => source.filePath = filterSources(source.filePath));
}
return transformedMessage;
});
return serializer.write(msgList, this._locale);

View File

@ -25,6 +25,8 @@ const _FILE_TAG = 'file';
const _SOURCE_TAG = 'source';
const _TARGET_TAG = 'target';
const _UNIT_TAG = 'trans-unit';
const _CONTEXT_GROUP_TAG = 'context-group';
const _CONTEXT_TAG = 'context';
// 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
@ -34,10 +36,24 @@ export class Xliff extends Serializer {
const transUnits: xml.Node[] = [];
messages.forEach(message => {
let contextTags: xml.Node[] = [];
message.sources.forEach((source: i18n.MessageSpan) => {
let contextGroupTag = new xml.Tag(_CONTEXT_GROUP_TAG, {purpose: 'location'});
contextGroupTag.children.push(
new xml.CR(10),
new xml.Tag(
_CONTEXT_TAG, {'context-type': 'sourcefile'}, [new xml.Text(source.filePath)]),
new xml.CR(10), new xml.Tag(
_CONTEXT_TAG, {'context-type': 'linenumber'},
[new xml.Text(`${source.startLine}`)]),
new xml.CR(8));
contextTags.push(new xml.CR(8), contextGroupTag);
});
const transUnit = new xml.Tag(_UNIT_TAG, {id: message.id, datatype: 'html'});
transUnit.children.push(
new xml.CR(8), new xml.Tag(_SOURCE_TAG, {}, visitor.serialize(message.nodes)),
new xml.CR(8), new xml.Tag(_TARGET_TAG));
new xml.CR(8), new xml.Tag(_TARGET_TAG), ...contextTags);
if (message.description) {
transUnit.children.push(

View File

@ -16,6 +16,7 @@ const _MESSAGES_TAG = 'messagebundle';
const _MESSAGE_TAG = 'msg';
const _PLACEHOLDER_TAG = 'ph';
const _EXEMPLE_TAG = 'ex';
const _SOURCE_TAG = 'source';
const _DOCTYPE = `<!ELEMENT messagebundle (msg)*>
<!ATTLIST messagebundle class CDATA #IMPLIED>
@ -54,8 +55,17 @@ export class Xmb extends Serializer {
attrs['meaning'] = message.meaning;
}
let sourceTags: xml.Tag[] = [];
message.sources.forEach((source: i18n.MessageSpan) => {
sourceTags.push(new xml.Tag(_SOURCE_TAG, {}, [
new xml.Text(
`${source.filePath}:${source.startLine}${source.endLine !== source.startLine ? ',' + source.endLine : ''}`)
]));
});
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, [...sourceTags, ...visitor.serialize(message.nodes)]));
});
rootNode.children.push(new xml.CR());