1 | var fs = require('fs');
|
2 | var glob = require('glob');
|
3 | var mkpath = require('mkpath');
|
4 | var path = require('path');
|
5 | var XmlReader = require('xml-reader-datatest');
|
6 |
|
7 | function writeFile(filepath, content) {
|
8 | mkpath.sync(path.dirname(filepath));
|
9 | fs.writeFileSync(filepath, content);
|
10 | }
|
11 |
|
12 | function gatherXlfPackage(location) {
|
13 | var package = [];
|
14 |
|
15 | var xlfPaths = glob.sync(location + '/*/*/*.xlf');
|
16 | xlfPaths.forEach((path, i) => {
|
17 | var langLoc = path.split('/');
|
18 | langLoc = langLoc.slice(langLoc.length - 3, langLoc.length - 1).join('/');
|
19 | package.push({
|
20 | langLoc: langLoc,
|
21 | path: path,
|
22 | strings: parseXLF(fs.readFileSync(path, 'utf8'))
|
23 | });
|
24 | });
|
25 | return package;
|
26 | }
|
27 |
|
28 | function parseXLF(xlf, cb) {
|
29 | var res = XmlReader.parseSync(xlf, {
|
30 | parentNodes: false,
|
31 | dataEmitTest: (data) => {
|
32 | return true;
|
33 | }
|
34 | });
|
35 | var tunits = res.children.filter(el => { return el.type === 'element'; })[0].children.filter(el => { return el.type === 'element'; })[0].children.filter(el => { return el.name === 'trans-unit'; });
|
36 | var simpler = tunits.map(tunit => {
|
37 | var targetEl = tunit.children.find(el => { return el.name === 'target'; });
|
38 | var descEl = tunit.children.find(el => {return el.name ==='note' && el.attributes.from === 'description'; });
|
39 | var meaningEl = tunit.children.find(el => {return el.name ==='note' && el.attributes.from === 'meaning'; });
|
40 | var sourceEl = tunit.children.find(el => { return el.name === 'source'; });
|
41 | return {
|
42 | id: tunit.attributes.id,
|
43 | source: sourceEl.children.length ? resolveChildrenToString(sourceEl) : null,
|
44 | target: targetEl.children.length ? resolveChildrenToString(targetEl) : null,
|
45 | state: targetEl.attributes ? targetEl.attributes.state : '',
|
46 | note: descEl && descEl.children.length ? descEl.children[0].value : null,
|
47 | meaning: meaningEl && meaningEl.children.length ? meaningEl.children[0].value : null
|
48 | };
|
49 | });
|
50 | return simpler;
|
51 | }
|
52 |
|
53 | function resolveChildrenToString(el) {
|
54 | return el.children.reduce((v, el) => {
|
55 | if (el.type === 'text') {
|
56 | return v + el.value;
|
57 | }
|
58 | v += `<${el.name}`;
|
59 | for (var k in el.attributes) {
|
60 | v += ` ${k}="${el.attributes[k]}"`;
|
61 | }
|
62 | return v + '/>';
|
63 | }, '');
|
64 | }
|
65 |
|
66 | module.exports = pathToPackage => {
|
67 |
|
68 | |
69 |
|
70 |
|
71 |
|
72 |
|
73 |
|
74 |
|
75 |
|
76 |
|
77 |
|
78 |
|
79 |
|
80 |
|
81 |
|
82 |
|
83 |
|
84 |
|
85 |
|
86 | var package = gatherXlfPackage(pathToPackage);
|
87 |
|
88 | return {
|
89 | getPackage: () => {
|
90 | return package;
|
91 | },
|
92 |
|
93 |
|
94 |
|
95 |
|
96 |
|
97 |
|
98 |
|
99 |
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | getStrings: onlyTranslated => {
|
105 | var strs = [];
|
106 | package.forEach(xlf => {
|
107 | xlf.strings.forEach(s => {
|
108 | var existing = strs.find(es => {
|
109 | return es.id === s.id;
|
110 | });
|
111 | if (!existing) {
|
112 | existing = {
|
113 | id: s.id,
|
114 | langs: {}
|
115 | };
|
116 | strs.push(existing);
|
117 | }
|
118 | if (xlf.langLoc === 'en/common') {
|
119 |
|
120 | existing.source = s.source;
|
121 | }
|
122 | existing.langs[xlf.langLoc] = s.target || '';
|
123 | });
|
124 | });
|
125 | strs.forEach(s => {
|
126 | s.allTranslated = true;
|
127 | for (var k in s.langs) {
|
128 | if (!s.langs[k] && k !== 'en/common') {
|
129 | s.allTranslated = false;
|
130 | }
|
131 | }
|
132 | });
|
133 | if (onlyTranslated) {
|
134 | strs = strs.filter(s => { return s.allTranslated; });
|
135 | }
|
136 | return strs;
|
137 | },
|
138 | updateString: (id, s) => {
|
139 | for (var k in s) {
|
140 | var xlf = package.find(xlf => {
|
141 | return xlf.langLoc === k;
|
142 | });
|
143 | var so = xlf.strings.find(so => {
|
144 | return so.id === id;
|
145 | });
|
146 | if (so) {
|
147 | so.target = s[k];
|
148 | so.state = !!so.target;
|
149 | }
|
150 | else {
|
151 | console.log(`String with ID ${id} not found!`);
|
152 | }
|
153 | }
|
154 | },
|
155 | write: basePath => {
|
156 | basePath = basePath || pathToPackage;
|
157 | if (basePath[basePath.length-1] !== '/') {
|
158 | basePath += '/';
|
159 | }
|
160 |
|
161 | package.forEach(xlf => {
|
162 | var pathParts = xlf.path.split('/');
|
163 | var path = basePath + xlf.langLoc + '/' + pathParts[pathParts.length-1];
|
164 |
|
165 | var xml =
|
166 | `<?xml version="1.0" encoding="utf-8"?><xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
167 | <file datatype="x-soy-msg-bundle" original="SoyMsgBundle" source-language="en" target-language="${xlf.langLoc}" xml:space="preserve">
|
168 | <body>`;
|
169 |
|
170 | xlf.strings.forEach(s => {
|
171 |
|
172 | xml += `
|
173 | <trans-unit datatype="html" id="${s.id}">
|
174 | <source>${s.source}</source>
|
175 | <target` +
|
176 | (s.target ? ` state="${s.state ? 'translated' : ''}">${s.target ? s.target : ''}</target>\n` : '/>\n') +
|
177 | (s.note ? ` <note from="description">${s.note}</note>\n` : '') +
|
178 | (s.meaning ? ` <note from="meaning">${s.meaning}</note>\n` : '') +
|
179 | ` </trans-unit>`;
|
180 |
|
181 | });
|
182 |
|
183 | xml += `
|
184 | </body>
|
185 | </file>
|
186 | </xliff>
|
187 | `;
|
188 | writeFile(path, xml);
|
189 | });
|
190 | }
|
191 | };
|
192 | }; |
\ | No newline at end of file |