1/*
2 SPDX-FileCopyrightText: 2013 Marco Martin <mart@kde.org>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7// Undefine this because we don't want our i18n*() method names to be turned into i18nd*()
8#undef TRANSLATION_DOMAIN
9
10#include "klocalizedqmlcontext.h"
11
12#include <klocalizedstring.h>
13
14#include <QCoreApplication>
15#include <QQmlContext>
16#include <QQmlEngine>
17
18#include "ki18n_qml_logging.h"
19
20class KLocalizedQmlContextPrivate
21{
22public:
23 void markCurrentFunctionAsTranslationBinding(const KLocalizedQmlContext *q) const;
24
25 QString m_translationDomain;
26};
27
28void KLocalizedQmlContextPrivate::markCurrentFunctionAsTranslationBinding(const KLocalizedQmlContext *q) const
29{
30 if (auto engine = qmlEngine(q); engine) {
31 engine->markCurrentFunctionAsTranslationBinding();
32 } else {
33 qCDebug(KI18N) << "No QML engine available, KLocalizedQmlContext not properly set up?";
34 }
35}
36
37KLocalizedQmlContext::KLocalizedQmlContext(QObject *parent)
38 : QObject(parent)
39 , d(new KLocalizedQmlContextPrivate)
40{
41 QCoreApplication::instance()->installEventFilter(filterObj: this);
42}
43
44KLocalizedQmlContext::~KLocalizedQmlContext() = default;
45
46QString KLocalizedQmlContext::translationDomain() const
47{
48 return d->m_translationDomain;
49}
50
51void KLocalizedQmlContext::setTranslationDomain(const QString &domain)
52{
53 if (domain != d->m_translationDomain) {
54 d->m_translationDomain = domain;
55 Q_EMIT translationDomainChanged(translationDomain: domain);
56 }
57}
58
59static void subsVariant(KLocalizedString &trMessage, const QVariant &value)
60{
61 switch (value.userType()) {
62 case QMetaType::QString:
63 trMessage = trMessage.subs(a: value.toString());
64 break;
65 case QMetaType::Int:
66 trMessage = trMessage.subs(a: value.toInt());
67 break;
68 case QMetaType::Double:
69 trMessage = trMessage.subs(a: value.toDouble());
70 break;
71 case QMetaType::Char:
72 trMessage = trMessage.subs(a: value.toChar());
73 break;
74 default:
75 if (value.canConvert<QString>()) {
76 trMessage = trMessage.subs(a: value.toString());
77 } else {
78 trMessage = trMessage.subs(QStringLiteral("???"));
79 qCWarning(KI18N) << "couldn't convert" << value << "to translate";
80 }
81 }
82}
83
84static void resolveMessage(KLocalizedString &trMessage,
85 const QVariant &param1,
86 const QVariant &param2,
87 const QVariant &param3,
88 const QVariant &param4,
89 const QVariant &param5,
90 const QVariant &param6,
91 const QVariant &param7,
92 const QVariant &param8,
93 const QVariant &param9,
94 const QVariant &param10 = QVariant())
95{
96 if (param1.isValid()) {
97 subsVariant(trMessage, value: param1);
98 }
99 if (param2.isValid()) {
100 subsVariant(trMessage, value: param2);
101 }
102 if (param3.isValid()) {
103 subsVariant(trMessage, value: param3);
104 }
105 if (param4.isValid()) {
106 subsVariant(trMessage, value: param4);
107 }
108 if (param5.isValid()) {
109 subsVariant(trMessage, value: param5);
110 }
111 if (param6.isValid()) {
112 subsVariant(trMessage, value: param6);
113 }
114 if (param7.isValid()) {
115 subsVariant(trMessage, value: param7);
116 }
117 if (param8.isValid()) {
118 subsVariant(trMessage, value: param8);
119 }
120 if (param9.isValid()) {
121 subsVariant(trMessage, value: param9);
122 }
123 if (param10.isValid()) {
124 subsVariant(trMessage, value: param10);
125 }
126}
127
128static void resolvePlural(KLocalizedString &trMessage, const QVariant &param)
129{
130 trMessage = trMessage.subs(a: param.toInt());
131}
132
133QString KLocalizedQmlContext::i18n(const QString &message,
134 const QVariant &param1,
135 const QVariant &param2,
136 const QVariant &param3,
137 const QVariant &param4,
138 const QVariant &param5,
139 const QVariant &param6,
140 const QVariant &param7,
141 const QVariant &param8,
142 const QVariant &param9,
143 const QVariant &param10) const
144{
145 if (message.isEmpty()) {
146 qCWarning(KI18N) << "i18n() needs at least one parameter";
147 return QString();
148 }
149
150 KLocalizedString trMessage;
151 if (!d->m_translationDomain.isEmpty()) {
152 trMessage = ki18nd(domain: d->m_translationDomain.toUtf8().constData(), text: message.toUtf8().constData());
153 } else {
154 trMessage = ki18n(text: message.toUtf8().constData());
155 }
156
157 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
158
159 d->markCurrentFunctionAsTranslationBinding(q: this);
160 return trMessage.toString();
161}
162
163QString KLocalizedQmlContext::i18nc(const QString &context,
164 const QString &message,
165 const QVariant &param1,
166 const QVariant &param2,
167 const QVariant &param3,
168 const QVariant &param4,
169 const QVariant &param5,
170 const QVariant &param6,
171 const QVariant &param7,
172 const QVariant &param8,
173 const QVariant &param9,
174 const QVariant &param10) const
175{
176 if (context.isEmpty() || message.isEmpty()) {
177 qCWarning(KI18N).noquote().nospace() << "i18nc(\"" << context << message << "\") needs at least two arguments";
178 return QString();
179 }
180
181 KLocalizedString trMessage;
182 if (!d->m_translationDomain.isEmpty()) {
183 trMessage = ki18ndc(domain: d->m_translationDomain.toUtf8().constData(), context: context.toUtf8().constData(), text: message.toUtf8().constData());
184 } else {
185 trMessage = ki18nc(context: context.toUtf8().constData(), text: message.toUtf8().constData());
186 }
187
188 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
189
190 d->markCurrentFunctionAsTranslationBinding(q: this);
191 return trMessage.toString();
192}
193
194QString KLocalizedQmlContext::i18np(const QString &singular,
195 const QString &plural,
196 const QVariant &param1,
197 const QVariant &param2,
198 const QVariant &param3,
199 const QVariant &param4,
200 const QVariant &param5,
201 const QVariant &param6,
202 const QVariant &param7,
203 const QVariant &param8,
204 const QVariant &param9,
205 const QVariant &param10) const
206{
207 if (singular.isEmpty() || plural.isEmpty()) {
208 qCWarning(KI18N).noquote().nospace() << "i18np(\"" << singular << plural << "\") needs at least two arguments";
209 return QString();
210 }
211
212 KLocalizedString trMessage;
213 if (!d->m_translationDomain.isEmpty()) {
214 trMessage = ki18ndp(domain: d->m_translationDomain.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
215 } else {
216 trMessage = ki18np(singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
217 }
218
219 resolvePlural(trMessage, param: param1);
220 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
221
222 d->markCurrentFunctionAsTranslationBinding(q: this);
223 return trMessage.toString();
224}
225
226QString KLocalizedQmlContext::i18ncp(const QString &context,
227 const QString &singular,
228 const QString &plural,
229 const QVariant &param1,
230 const QVariant &param2,
231 const QVariant &param3,
232 const QVariant &param4,
233 const QVariant &param5,
234 const QVariant &param6,
235 const QVariant &param7,
236 const QVariant &param8,
237 const QVariant &param9,
238 const QVariant &param10) const
239{
240 if (context.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
241 qCWarning(KI18N) << "i18ncp() needs at least three arguments";
242 return QString();
243 }
244
245 KLocalizedString trMessage;
246 if (!d->m_translationDomain.isEmpty()) {
247 trMessage =
248 ki18ndcp(domain: d->m_translationDomain.toUtf8().constData(), context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
249 } else {
250 trMessage = ki18ncp(context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
251 }
252
253 resolvePlural(trMessage, param: param1);
254 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
255
256 d->markCurrentFunctionAsTranslationBinding(q: this);
257 return trMessage.toString();
258}
259
260QString KLocalizedQmlContext::i18nd(const QString &domain,
261 const QString &message,
262 const QVariant &param1,
263 const QVariant &param2,
264 const QVariant &param3,
265 const QVariant &param4,
266 const QVariant &param5,
267 const QVariant &param6,
268 const QVariant &param7,
269 const QVariant &param8,
270 const QVariant &param9,
271 const QVariant &param10) const
272{
273 if (domain.isEmpty() || message.isEmpty()) {
274 qCWarning(KI18N).noquote().nospace() << "i18nd(\"" << domain << message << "\") needs at least two parameters";
275 return QString();
276 }
277
278 KLocalizedString trMessage = ki18nd(domain: domain.toUtf8().constData(), text: message.toUtf8().constData());
279
280 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
281
282 d->markCurrentFunctionAsTranslationBinding(q: this);
283 return trMessage.toString();
284}
285
286QString KLocalizedQmlContext::i18ndc(const QString &domain,
287 const QString &context,
288 const QString &message,
289 const QVariant &param1,
290 const QVariant &param2,
291 const QVariant &param3,
292 const QVariant &param4,
293 const QVariant &param5,
294 const QVariant &param6,
295 const QVariant &param7,
296 const QVariant &param8,
297 const QVariant &param9,
298 const QVariant &param10) const
299{
300 if (domain.isEmpty() || context.isEmpty() || message.isEmpty()) {
301 qCWarning(KI18N) << "i18ndc() needs at least three arguments";
302 return QString();
303 }
304
305 KLocalizedString trMessage = ki18ndc(domain: domain.toUtf8().constData(), context: context.toUtf8().constData(), text: message.toUtf8().constData());
306
307 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
308
309 d->markCurrentFunctionAsTranslationBinding(q: this);
310 return trMessage.toString();
311}
312
313QString KLocalizedQmlContext::i18ndp(const QString &domain,
314 const QString &singular,
315 const QString &plural,
316 const QVariant &param1,
317 const QVariant &param2,
318 const QVariant &param3,
319 const QVariant &param4,
320 const QVariant &param5,
321 const QVariant &param6,
322 const QVariant &param7,
323 const QVariant &param8,
324 const QVariant &param9,
325 const QVariant &param10) const
326{
327 if (domain.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
328 qCWarning(KI18N) << "i18ndp() needs at least three arguments";
329 return QString();
330 }
331
332 KLocalizedString trMessage = ki18ndp(domain: domain.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
333
334 resolvePlural(trMessage, param: param1);
335 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
336
337 d->markCurrentFunctionAsTranslationBinding(q: this);
338 return trMessage.toString();
339}
340
341QString KLocalizedQmlContext::i18ndcp(const QString &domain,
342 const QString &context,
343 const QString &singular,
344 const QString &plural,
345 const QVariant &param1,
346 const QVariant &param2,
347 const QVariant &param3,
348 const QVariant &param4,
349 const QVariant &param5,
350 const QVariant &param6,
351 const QVariant &param7,
352 const QVariant &param8,
353 const QVariant &param9,
354 const QVariant &param10) const
355{
356 if (domain.isEmpty() || context.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
357 qCWarning(KI18N) << "i18ndcp() needs at least four arguments";
358 return QString();
359 }
360
361 KLocalizedString trMessage =
362 ki18ndcp(domain: domain.toUtf8().constData(), context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
363
364 resolvePlural(trMessage, param: param1);
365 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
366
367 d->markCurrentFunctionAsTranslationBinding(q: this);
368 return trMessage.toString();
369}
370
371/////////////////////////
372
373QString KLocalizedQmlContext::xi18n(const QString &message,
374 const QVariant &param1,
375 const QVariant &param2,
376 const QVariant &param3,
377 const QVariant &param4,
378 const QVariant &param5,
379 const QVariant &param6,
380 const QVariant &param7,
381 const QVariant &param8,
382 const QVariant &param9,
383 const QVariant &param10) const
384{
385 if (message.isEmpty()) {
386 qCWarning(KI18N) << "xi18n() needs at least one parameter";
387 return QString();
388 }
389
390 KLocalizedString trMessage;
391 if (!d->m_translationDomain.isEmpty()) {
392 trMessage = kxi18nd(domain: d->m_translationDomain.toUtf8().constData(), text: message.toUtf8().constData());
393 } else {
394 trMessage = kxi18n(text: message.toUtf8().constData());
395 }
396
397 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
398
399 d->markCurrentFunctionAsTranslationBinding(q: this);
400 return trMessage.toString();
401}
402
403QString KLocalizedQmlContext::xi18nc(const QString &context,
404 const QString &message,
405 const QVariant &param1,
406 const QVariant &param2,
407 const QVariant &param3,
408 const QVariant &param4,
409 const QVariant &param5,
410 const QVariant &param6,
411 const QVariant &param7,
412 const QVariant &param8,
413 const QVariant &param9,
414 const QVariant &param10) const
415{
416 if (context.isEmpty() || message.isEmpty()) {
417 qCWarning(KI18N).noquote().nospace() << "xi18nc(\"" << context << message << "\") needs at least two arguments";
418 return QString();
419 }
420
421 KLocalizedString trMessage;
422 if (!d->m_translationDomain.isEmpty()) {
423 trMessage = kxi18ndc(domain: d->m_translationDomain.toUtf8().constData(), context: context.toUtf8().constData(), text: message.toUtf8().constData());
424 } else {
425 trMessage = kxi18nc(context: context.toUtf8().constData(), text: message.toUtf8().constData());
426 }
427
428 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
429
430 d->markCurrentFunctionAsTranslationBinding(q: this);
431 return trMessage.toString();
432}
433
434QString KLocalizedQmlContext::xi18np(const QString &singular,
435 const QString &plural,
436 const QVariant &param1,
437 const QVariant &param2,
438 const QVariant &param3,
439 const QVariant &param4,
440 const QVariant &param5,
441 const QVariant &param6,
442 const QVariant &param7,
443 const QVariant &param8,
444 const QVariant &param9,
445 const QVariant &param10) const
446{
447 if (singular.isEmpty() || plural.isEmpty()) {
448 qCWarning(KI18N).noquote().nospace() << "xi18np(\"" << singular << plural << "\") needs at least two arguments";
449 return QString();
450 }
451
452 KLocalizedString trMessage;
453 if (!d->m_translationDomain.isEmpty()) {
454 trMessage = kxi18ndp(domain: d->m_translationDomain.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
455 } else {
456 trMessage = kxi18np(singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
457 }
458
459 resolvePlural(trMessage, param: param1);
460 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
461
462 d->markCurrentFunctionAsTranslationBinding(q: this);
463 return trMessage.toString();
464}
465
466QString KLocalizedQmlContext::xi18ncp(const QString &context,
467 const QString &singular,
468 const QString &plural,
469 const QVariant &param1,
470 const QVariant &param2,
471 const QVariant &param3,
472 const QVariant &param4,
473 const QVariant &param5,
474 const QVariant &param6,
475 const QVariant &param7,
476 const QVariant &param8,
477 const QVariant &param9,
478 const QVariant &param10) const
479{
480 if (context.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
481 qCWarning(KI18N) << "xi18ncp() needs at least three arguments";
482 return QString();
483 }
484
485 KLocalizedString trMessage;
486 if (!d->m_translationDomain.isEmpty()) {
487 trMessage =
488 kxi18ndcp(domain: d->m_translationDomain.toUtf8().constData(), context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
489 } else {
490 trMessage = kxi18ncp(context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
491 }
492
493 resolvePlural(trMessage, param: param1);
494 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
495
496 d->markCurrentFunctionAsTranslationBinding(q: this);
497 return trMessage.toString();
498}
499
500QString KLocalizedQmlContext::xi18nd(const QString &domain,
501 const QString &message,
502 const QVariant &param1,
503 const QVariant &param2,
504 const QVariant &param3,
505 const QVariant &param4,
506 const QVariant &param5,
507 const QVariant &param6,
508 const QVariant &param7,
509 const QVariant &param8,
510 const QVariant &param9,
511 const QVariant &param10) const
512{
513 if (domain.isEmpty() || message.isEmpty()) {
514 qCWarning(KI18N).noquote().nospace() << "xi18nd(\"" << domain << message << "\") needs at least two parameters";
515 return QString();
516 }
517
518 KLocalizedString trMessage = kxi18nd(domain: domain.toUtf8().constData(), text: message.toUtf8().constData());
519
520 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
521
522 d->markCurrentFunctionAsTranslationBinding(q: this);
523 return trMessage.toString();
524}
525
526QString KLocalizedQmlContext::xi18ndc(const QString &domain,
527 const QString &context,
528 const QString &message,
529 const QVariant &param1,
530 const QVariant &param2,
531 const QVariant &param3,
532 const QVariant &param4,
533 const QVariant &param5,
534 const QVariant &param6,
535 const QVariant &param7,
536 const QVariant &param8,
537 const QVariant &param9,
538 const QVariant &param10) const
539{
540 if (domain.isEmpty() || context.isEmpty() || message.isEmpty()) {
541 qCWarning(KI18N) << "x18ndc() needs at least three arguments";
542 return QString();
543 }
544
545 KLocalizedString trMessage = kxi18ndc(domain: domain.toUtf8().constData(), context: context.toUtf8().constData(), text: message.toUtf8().constData());
546
547 resolveMessage(trMessage, param1, param2, param3, param4, param5, param6, param7, param8, param9, param10);
548
549 d->markCurrentFunctionAsTranslationBinding(q: this);
550 return trMessage.toString();
551}
552
553QString KLocalizedQmlContext::xi18ndp(const QString &domain,
554 const QString &singular,
555 const QString &plural,
556 const QVariant &param1,
557 const QVariant &param2,
558 const QVariant &param3,
559 const QVariant &param4,
560 const QVariant &param5,
561 const QVariant &param6,
562 const QVariant &param7,
563 const QVariant &param8,
564 const QVariant &param9,
565 const QVariant &param10) const
566{
567 if (domain.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
568 qCWarning(KI18N) << "xi18ndp() needs at least three arguments";
569 return QString();
570 }
571
572 KLocalizedString trMessage = kxi18ndp(domain: domain.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
573
574 resolvePlural(trMessage, param: param1);
575 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
576
577 d->markCurrentFunctionAsTranslationBinding(q: this);
578 return trMessage.toString();
579}
580
581QString KLocalizedQmlContext::xi18ndcp(const QString &domain,
582 const QString &context,
583 const QString &singular,
584 const QString &plural,
585 const QVariant &param1,
586 const QVariant &param2,
587 const QVariant &param3,
588 const QVariant &param4,
589 const QVariant &param5,
590 const QVariant &param6,
591 const QVariant &param7,
592 const QVariant &param8,
593 const QVariant &param9,
594 const QVariant &param10) const
595{
596 if (domain.isEmpty() || context.isEmpty() || singular.isEmpty() || plural.isEmpty()) {
597 qCWarning(KI18N) << "xi18ndcp() needs at least four arguments";
598 return QString();
599 }
600
601 KLocalizedString trMessage =
602 kxi18ndcp(domain: domain.toUtf8().constData(), context: context.toUtf8().constData(), singular: singular.toUtf8().constData(), plural: plural.toUtf8().constData());
603
604 resolvePlural(trMessage, param: param1);
605 resolveMessage(trMessage, param1: param2, param2: param3, param3: param4, param4: param5, param5: param6, param6: param7, param7: param8, param8: param9, param9: param10);
606
607 d->markCurrentFunctionAsTranslationBinding(q: this);
608 return trMessage.toString();
609}
610
611bool KLocalizedQmlContext::eventFilter(QObject *watched, QEvent *event)
612{
613 if (event->type() == QEvent::LanguageChange && watched == QCoreApplication::instance()) {
614 qCDebug(KI18N) << "triggering binding reevaluation";
615 // run this deferred so we can be sure other things have reacted, such as KLocalizedString
616 // having updated its internal caches
617 if (auto engine = qmlEngine(this); engine) {
618 QMetaObject::invokeMethod(object: engine, function: &QQmlEngine::retranslate, type: Qt::QueuedConnection);
619 }
620 }
621 return QObject::eventFilter(watched, event);
622}
623
624KLocalizedQmlContext *KLocalization::Internal::createLocalizedContext(QQmlEngine *engine)
625{
626 auto ctx = new KLocalizedQmlContext(engine);
627 engine->rootContext()->setContextObject(ctx);
628 QQmlEngine::setContextForObject(ctx, engine->rootContext());
629 return ctx;
630}
631
632#include "moc_klocalizedqmlcontext.cpp"
633

source code of ki18n/src/i18n-qml/klocalizedqmlcontext.cpp