feat(compiler): add source files to xmb/xliff translations (#14705)
Fixes #14190
This commit is contained in:

committed by
Tobias Bosch

parent
09c4cb2540
commit
4054055d0d
@ -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{};
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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(
|
||||
|
@ -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());
|
||||
|
Reference in New Issue
Block a user