63 #include <iostream> |
63 #include <iostream> |
64 #include <cstdlib> |
64 #include <cstdlib> |
65 |
65 |
66 QT_BEGIN_NAMESPACE |
66 QT_BEGIN_NAMESPACE |
67 |
67 |
|
68 class LU { |
|
69 Q_DECLARE_TR_FUNCTIONS(LUpdate) |
|
70 }; |
|
71 |
68 using namespace QDeclarativeJS; |
72 using namespace QDeclarativeJS; |
|
73 |
|
74 class Comment |
|
75 { |
|
76 public: |
|
77 Comment() : lastLine(-1) {} |
|
78 QString extracomment; |
|
79 QString msgid; |
|
80 TranslatorMessage::ExtraData extra; |
|
81 QString sourcetext; |
|
82 int lastLine; |
|
83 |
|
84 bool isValid() const |
|
85 { return !extracomment.isEmpty() || !msgid.isEmpty() || !sourcetext.isEmpty() || !extra.isEmpty(); } |
|
86 }; |
69 |
87 |
70 class FindTrCalls: protected AST::Visitor |
88 class FindTrCalls: protected AST::Visitor |
71 { |
89 { |
72 public: |
90 public: |
73 void operator()(Translator *translator, const QString &fileName, AST::Node *node) |
91 void operator()(Translator *translator, const QString &fileName, AST::Node *node) |
75 m_translator = translator; |
93 m_translator = translator; |
76 m_fileName = fileName; |
94 m_fileName = fileName; |
77 m_component = QFileInfo(fileName).baseName(); //matches qsTr usage in QScriptEngine |
95 m_component = QFileInfo(fileName).baseName(); //matches qsTr usage in QScriptEngine |
78 accept(node); |
96 accept(node); |
79 } |
97 } |
|
98 |
|
99 QList<Comment> comments; |
80 |
100 |
81 protected: |
101 protected: |
82 using AST::Visitor::visit; |
102 using AST::Visitor::visit; |
83 using AST::Visitor::endVisit; |
103 using AST::Visitor::endVisit; |
84 |
104 |
112 AST::ArgumentList *nNode = commentNode->next; |
132 AST::ArgumentList *nNode = commentNode->next; |
113 if (nNode) |
133 if (nNode) |
114 plural = true; |
134 plural = true; |
115 } |
135 } |
116 |
136 |
|
137 QString id; |
|
138 QString extracomment; |
|
139 TranslatorMessage::ExtraData extra; |
|
140 Comment scomment = findComment(node->firstSourceLocation().startLine); |
|
141 if (scomment.isValid()) { |
|
142 extracomment = scomment.extracomment; |
|
143 extra = scomment.extra; |
|
144 id = scomment.msgid; |
|
145 } |
|
146 |
117 TranslatorMessage msg(m_component, source, |
147 TranslatorMessage msg(m_component, source, |
118 comment, QString(), m_fileName, |
148 comment, QString(), m_fileName, |
119 node->firstSourceLocation().startLine, QStringList(), |
149 node->firstSourceLocation().startLine, QStringList(), |
120 TranslatorMessage::Unfinished, plural); |
150 TranslatorMessage::Unfinished, plural); |
|
151 msg.setExtraComment(extracomment.simplified()); |
|
152 msg.setId(id); |
|
153 msg.setExtras(extra); |
121 m_translator->extend(msg); |
154 m_translator->extend(msg); |
122 } |
155 } |
123 } else if (idExpr->name->asString() == QLatin1String("qsTranslate") || |
156 } else if (idExpr->name->asString() == QLatin1String("qsTranslate") || |
124 idExpr->name->asString() == QLatin1String("QT_TRANSLATE_NOOP")) { |
157 idExpr->name->asString() == QLatin1String("QT_TRANSLATE_NOOP")) { |
125 if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) { |
158 if (node->arguments && AST::cast<AST::StringLiteral *>(node->arguments->expression)) { |
138 if (!createString(binary)) |
171 if (!createString(binary)) |
139 m_bSource.clear(); |
172 m_bSource.clear(); |
140 } |
173 } |
141 if (!literal && m_bSource.isEmpty()) |
174 if (!literal && m_bSource.isEmpty()) |
142 return; |
175 return; |
|
176 |
|
177 QString id; |
|
178 QString extracomment; |
|
179 TranslatorMessage::ExtraData extra; |
|
180 Comment scomment = findComment(node->firstSourceLocation().startLine); |
|
181 if (scomment.isValid()) { |
|
182 extracomment = scomment.extracomment; |
|
183 extra = scomment.extra; |
|
184 id = scomment.msgid; |
|
185 } |
|
186 |
143 source = literal ? literal->value->asString() : m_bSource; |
187 source = literal ? literal->value->asString() : m_bSource; |
144 AST::ArgumentList *commentNode = sourceNode->next; |
188 AST::ArgumentList *commentNode = sourceNode->next; |
145 if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) { |
189 if (commentNode && AST::cast<AST::StringLiteral *>(commentNode->expression)) { |
146 literal = AST::cast<AST::StringLiteral *>(commentNode->expression); |
190 literal = AST::cast<AST::StringLiteral *>(commentNode->expression); |
147 comment = literal->value->asString(); |
191 comment = literal->value->asString(); |
153 |
197 |
154 TranslatorMessage msg(context, source, |
198 TranslatorMessage msg(context, source, |
155 comment, QString(), m_fileName, |
199 comment, QString(), m_fileName, |
156 node->firstSourceLocation().startLine, QStringList(), |
200 node->firstSourceLocation().startLine, QStringList(), |
157 TranslatorMessage::Unfinished, plural); |
201 TranslatorMessage::Unfinished, plural); |
|
202 msg.setExtraComment(extracomment.simplified()); |
|
203 msg.setId(id); |
|
204 msg.setExtras(extra); |
158 m_translator->extend(msg); |
205 m_translator->extend(msg); |
159 } |
206 } |
160 |
207 } else if (idExpr->name->asString() == QLatin1String("qsTrId") || |
|
208 idExpr->name->asString() == QLatin1String("QT_TRID_NOOP")) { |
|
209 if (!node->arguments) |
|
210 return; |
|
211 |
|
212 AST::StringLiteral *literal = AST::cast<AST::StringLiteral *>(node->arguments->expression); |
|
213 if (literal) { |
|
214 |
|
215 QString extracomment; |
|
216 QString sourcetext; |
|
217 TranslatorMessage::ExtraData extra; |
|
218 Comment comment = findComment(node->firstSourceLocation().startLine); |
|
219 if (comment.isValid()) { |
|
220 extracomment = comment.extracomment; |
|
221 sourcetext = comment.sourcetext; |
|
222 extra = comment.extra; |
|
223 } |
|
224 |
|
225 const QString id = literal->value->asString(); |
|
226 bool plural = node->arguments->next; |
|
227 |
|
228 TranslatorMessage msg(QString(), QString(), |
|
229 QString(), QString(), m_fileName, |
|
230 node->firstSourceLocation().startLine, QStringList(), |
|
231 TranslatorMessage::Unfinished, plural); |
|
232 msg.setExtraComment(extracomment.simplified()); |
|
233 msg.setId(id); |
|
234 msg.setExtras(extra); |
|
235 m_translator->extend(msg); |
|
236 } |
161 } |
237 } |
162 } |
238 } |
163 } |
239 } |
164 |
240 |
165 private: |
241 private: |
166 bool createString(AST::BinaryExpression *b) { |
242 bool createString(AST::BinaryExpression *b) |
|
243 { |
167 if (!b || b->op != 0) |
244 if (!b || b->op != 0) |
168 return false; |
245 return false; |
169 AST::BinaryExpression *l = AST::cast<AST::BinaryExpression *>(b->left); |
246 AST::BinaryExpression *l = AST::cast<AST::BinaryExpression *>(b->left); |
170 AST::BinaryExpression *r = AST::cast<AST::BinaryExpression *>(b->right); |
247 AST::BinaryExpression *r = AST::cast<AST::BinaryExpression *>(b->right); |
171 AST::StringLiteral *ls = AST::cast<AST::StringLiteral *>(b->left); |
248 AST::StringLiteral *ls = AST::cast<AST::StringLiteral *>(b->left); |
185 m_bSource.append(rs->value->asString()); |
262 m_bSource.append(rs->value->asString()); |
186 |
263 |
187 return true; |
264 return true; |
188 } |
265 } |
189 |
266 |
|
267 Comment findComment(int loc) |
|
268 { |
|
269 if (comments.isEmpty()) |
|
270 return Comment(); |
|
271 |
|
272 int i = 0; |
|
273 int commentLoc = comments.at(i).lastLine; |
|
274 while (commentLoc <= loc) { |
|
275 if (commentLoc == loc) |
|
276 return comments.at(i); |
|
277 if (i == comments.count()-1) |
|
278 break; |
|
279 commentLoc = comments.at(++i).lastLine; |
|
280 } |
|
281 return Comment(); |
|
282 } |
|
283 |
190 Translator *m_translator; |
284 Translator *m_translator; |
191 QString m_fileName; |
285 QString m_fileName; |
192 QString m_component; |
286 QString m_component; |
193 QString m_bSource; |
287 QString m_bSource; |
194 }; |
288 }; |
234 errorString += error; |
328 errorString += error; |
235 } |
329 } |
236 return errorString; |
330 return errorString; |
237 } |
331 } |
238 |
332 |
|
333 bool processComment(const QChar *chars, int length, Comment &comment) |
|
334 { |
|
335 // Try to match the logic of the QtScript parser. |
|
336 if (!length) |
|
337 return comment.isValid(); |
|
338 if (*chars == QLatin1Char(':') && chars[1].isSpace()) { |
|
339 comment.extracomment += QString(chars+1, length-1); |
|
340 } else if (*chars == QLatin1Char('=') && chars[1].isSpace()) { |
|
341 comment.msgid = QString(chars+2, length-2).simplified(); |
|
342 } else if (*chars == QLatin1Char('~') && chars[1].isSpace()) { |
|
343 QString text = QString(chars+2, length-2).trimmed(); |
|
344 int k = text.indexOf(QLatin1Char(' ')); |
|
345 if (k > -1) |
|
346 comment.extra.insert(text.left(k), text.mid(k + 1).trimmed()); |
|
347 } else if (*chars == QLatin1Char('%') && chars[1].isSpace()) { |
|
348 comment.sourcetext.reserve(comment.sourcetext.length() + length-2); |
|
349 ushort *ptr = (ushort *)comment.sourcetext.data() + comment.sourcetext.length(); |
|
350 int p = 2, c; |
|
351 forever { |
|
352 if (p >= length) |
|
353 break; |
|
354 c = chars[p++].unicode(); |
|
355 if (isspace(c)) |
|
356 continue; |
|
357 if (c != '"') |
|
358 break; |
|
359 forever { |
|
360 if (p >= length) |
|
361 break; |
|
362 c = chars[p++].unicode(); |
|
363 if (c == '"') |
|
364 break; |
|
365 if (c == '\\') { |
|
366 if (p >= length) |
|
367 break; |
|
368 c = chars[p++].unicode(); |
|
369 if (c == '\n') |
|
370 break; |
|
371 *ptr++ = '\\'; |
|
372 } |
|
373 *ptr++ = c; |
|
374 } |
|
375 } |
|
376 comment.sourcetext.resize(ptr - (ushort *)comment.sourcetext.data()); |
|
377 } |
|
378 return comment.isValid(); |
|
379 } |
|
380 |
239 bool loadQml(Translator &translator, const QString &filename, ConversionData &cd) |
381 bool loadQml(Translator &translator, const QString &filename, ConversionData &cd) |
240 { |
382 { |
241 cd.m_sourceFileName = filename; |
383 cd.m_sourceFileName = filename; |
242 QFile file(filename); |
384 QFile file(filename); |
243 if (!file.open(QIODevice::ReadOnly)) { |
385 if (!file.open(QIODevice::ReadOnly)) { |
244 cd.appendError(QString::fromLatin1("Cannot open %1: %2") |
386 cd.appendError(LU::tr("Cannot open %1: %2").arg(filename, file.errorString())); |
245 .arg(filename, file.errorString())); |
|
246 return false; |
387 return false; |
247 } |
388 } |
248 |
389 |
249 const QString code = QTextStream(&file).readAll(); |
390 const QString code = QTextStream(&file).readAll(); |
250 |
391 |
258 lexer.setCode(code, /*line = */ 1); |
399 lexer.setCode(code, /*line = */ 1); |
259 driver.setLexer(&lexer); |
400 driver.setLexer(&lexer); |
260 |
401 |
261 if (parser.parse()) { |
402 if (parser.parse()) { |
262 FindTrCalls trCalls; |
403 FindTrCalls trCalls; |
|
404 |
|
405 // build up a list of comments that contain translation information. |
|
406 for (int i = 0; i < driver.comments().size(); ++i) { |
|
407 AST::SourceLocation loc = driver.comments().at(i); |
|
408 QString commentStr = code.mid(loc.offset, loc.length); |
|
409 |
|
410 if (trCalls.comments.isEmpty() || trCalls.comments.last().lastLine != int(loc.startLine)) { |
|
411 Comment comment; |
|
412 comment.lastLine = loc.startLine+1; |
|
413 if (processComment(commentStr.constData(), commentStr.length(), comment)) |
|
414 trCalls.comments.append(comment); |
|
415 } else { |
|
416 Comment &lastComment = trCalls.comments.last(); |
|
417 lastComment.lastLine += 1; |
|
418 processComment(commentStr.constData(), commentStr.length(), lastComment); |
|
419 } |
|
420 } |
|
421 |
|
422 //find all tr calls in the code |
263 trCalls(&translator, filename, parser.ast()); |
423 trCalls(&translator, filename, parser.ast()); |
264 } else { |
424 } else { |
265 QString error = createErrorString(filename, code, parser); |
425 QString error = createErrorString(filename, code, parser); |
266 cd.appendError(error); |
426 cd.appendError(error); |
267 return false; |
427 return false; |