VirtualBox

source: vbox/trunk/src/VBox/Frontends/VirtualBox/src/globals/UITranslator.cpp@ 102493

Last change on this file since 102493 was 101571, checked in by vboxsync, 11 months ago

FE/Qt: bugref:10450: Get rid of Qt5 stuff; This one is about replacing obsolete stuff.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 30.1 KB
Line 
1/* $Id: UITranslator.cpp 101571 2023-10-24 00:48:20Z vboxsync $ */
2/** @file
3 * VBox Qt GUI - UITranslator class implementation.
4 */
5
6/*
7 * Copyright (C) 2006-2023 Oracle and/or its affiliates.
8 *
9 * This file is part of VirtualBox base platform packages, as
10 * available from https://www.virtualbox.org.
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation, in version 3 of the
15 * License.
16 *
17 * This program is distributed in the hope that it will be useful, but
18 * WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * General Public License for more details.
21 *
22 * You should have received a copy of the GNU General Public License
23 * along with this program; if not, see <https://www.gnu.org/licenses>.
24 *
25 * SPDX-License-Identifier: GPL-3.0-only
26 */
27
28/* Qt includes: */
29#include <QApplication>
30#include <QDir>
31#include <QKeySequence>
32#ifdef Q_OS_UNIX
33# include <QLibraryInfo>
34#endif
35#include <QRegularExpression>
36#include <QRegExp>
37
38/* GUI includes: */
39#include "UIConverter.h"
40#include "UIMessageCenter.h"
41#include "UITranslator.h"
42#ifdef VBOX_WS_MAC
43# include "VBoxUtils-darwin.h"
44#endif
45
46/* Other VBox includes: */
47#include <iprt/assert.h>
48#include <iprt/path.h>
49#ifdef Q_OS_UNIX
50# include <iprt/env.h>
51#endif
52
53/* External includes: */
54#include <math.h>
55
56
57/** Port config cache. */
58struct PortConfig
59{
60 const char *name;
61 const ulong IRQ;
62 const ulong IOBase;
63};
64
65/** Known port config COM ports. */
66static const PortConfig kComKnownPorts[] =
67{
68 { "COM1", 4, 0x3F8 },
69 { "COM2", 3, 0x2F8 },
70 { "COM3", 4, 0x3E8 },
71 { "COM4", 3, 0x2E8 },
72 /* Must not contain an element with IRQ=0 and IOBase=0 used to cause
73 * toCOMPortName() to return the "User-defined" string for these values. */
74};
75
76
77/* static */
78UITranslator *UITranslator::s_pTranslator = 0;
79bool UITranslator::s_fTranslationInProgress = false;
80QString UITranslator::s_strLoadedLanguageId = UITranslator::vboxBuiltInLanguageName();
81
82/* static */
83void UITranslator::loadLanguage(const QString &strLangId /* = QString() */)
84{
85 QString strEffectiveLangId = strLangId.isEmpty()
86 ? systemLanguageId()
87 : strLangId;
88 QString strLanguageFileName;
89 QString strSelectedLangId = vboxBuiltInLanguageName();
90
91 /* If C is selected we change it temporary to en. This makes sure any extra
92 * "en" translation file will be loaded. This is necessary for loading the
93 * plural forms of some of our translations. */
94 bool fResetToC = false;
95 if (strEffectiveLangId == "C")
96 {
97 strEffectiveLangId = "en";
98 fResetToC = true;
99 }
100
101 char szNlsPath[RTPATH_MAX];
102 int rc;
103
104 rc = RTPathAppPrivateNoArch(szNlsPath, sizeof(szNlsPath));
105 AssertRC(rc);
106
107 QString strNlsPath = QString(szNlsPath) + vboxLanguageSubDirectory();
108 QDir nlsDir(strNlsPath);
109
110 Assert(!strEffectiveLangId.isEmpty());
111 if (!strEffectiveLangId.isEmpty() && strEffectiveLangId != vboxBuiltInLanguageName())
112 {
113 QRegExp regExp(vboxLanguageIdRegExp());
114 int iPos = regExp.indexIn(strEffectiveLangId);
115 /* The language ID should match the regexp completely: */
116 AssertReturnVoid(iPos == 0);
117
118 QString strStrippedLangId = regExp.cap(2);
119
120 if (nlsDir.exists(vboxLanguageFileBase() + strEffectiveLangId + vboxLanguageFileExtension()))
121 {
122 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
123 strEffectiveLangId +
124 vboxLanguageFileExtension());
125 strSelectedLangId = strEffectiveLangId;
126 }
127 else if (nlsDir.exists(vboxLanguageFileBase() + strStrippedLangId + vboxLanguageFileExtension()))
128 {
129 strLanguageFileName = nlsDir.absoluteFilePath(vboxLanguageFileBase() +
130 strStrippedLangId +
131 vboxLanguageFileExtension());
132 strSelectedLangId = strStrippedLangId;
133 }
134 else
135 {
136 /* Never complain when the default language is requested. In any
137 * case, if no explicit language file exists, we will simply
138 * fall-back to English (built-in). */
139 if (!strLangId.isNull() && strEffectiveLangId != "en")
140 msgCenter().cannotFindLanguage(strEffectiveLangId, strNlsPath);
141 /* strSelectedLangId remains built-in here: */
142 AssertReturnVoid(strSelectedLangId == vboxBuiltInLanguageName());
143 }
144 }
145
146 /* Lock listener: */
147 s_fTranslationInProgress = true;
148 /* A list of translators to install: */
149 QList<QTranslator*> translators;
150
151 /* Delete the old translator if there is one: */
152 if (s_pTranslator)
153 {
154 /* QTranslator destructor will call qApp->removeTranslator() for
155 * us. It will also delete all its child translations we attach to it
156 * below, so we don't have to care about them specially. */
157 delete s_pTranslator;
158 }
159
160 /* Load new language files: */
161 s_pTranslator = new UITranslator(qApp);
162 Assert(s_pTranslator);
163 bool fLoadOk = true;
164 if (s_pTranslator)
165 {
166 if (strSelectedLangId != vboxBuiltInLanguageName())
167 {
168 Assert(!strLanguageFileName.isNull());
169 fLoadOk = s_pTranslator->loadFile(strLanguageFileName);
170 }
171 /* We install the translator in any case: on failure, this will
172 * activate an empty translator that will give us English (built-in): */
173 translators << s_pTranslator;
174 }
175 else
176 fLoadOk = false;
177
178 if (fLoadOk)
179 s_strLoadedLanguageId = strSelectedLangId;
180 else
181 {
182 msgCenter().cannotLoadLanguage(strLanguageFileName);
183 s_strLoadedLanguageId = vboxBuiltInLanguageName();
184 }
185
186 /* Try to load the corresponding Qt translation: */
187 if (languageId() != vboxBuiltInLanguageName() && languageId() != "en")
188 {
189#ifdef Q_OS_UNIX
190 // We use system installations of Qt on Linux systems, so first, try
191 // to load the Qt translation from the system location.
192 strLanguageFileName = QLibraryInfo::path(QLibraryInfo::TranslationsPath) + "/qt_" +
193 languageId() + vboxLanguageFileExtension();
194 QTranslator *pQtSysTr = new QTranslator(s_pTranslator);
195 Assert(pQtSysTr);
196 if (pQtSysTr && pQtSysTr->load(strLanguageFileName))
197 translators << pQtSysTr;
198 // Note that the Qt translation supplied by Oracle is always loaded
199 // afterwards to make sure it will take precedence over the system
200 // translation (it may contain more decent variants of translation
201 // that better correspond to VirtualBox UI). We need to load both
202 // because a newer version of Qt may be installed on the user computer
203 // and the Oracle version may not fully support it. We don't do it on
204 // Win32 because we supply a Qt library there and therefore the
205 // Oracle translation is always the best one. */
206#endif
207 strLanguageFileName = nlsDir.absoluteFilePath(QString("qt_") +
208 languageId() +
209 vboxLanguageFileExtension());
210 QTranslator *pQtTr = new QTranslator(s_pTranslator);
211 Assert(pQtTr);
212 if (pQtTr && (fLoadOk = pQtTr->load(strLanguageFileName)))
213 translators << pQtTr;
214 /* The below message doesn't fit 100% (because it's an additional
215 * language and the main one won't be reset to built-in on failure)
216 * but the load failure is so rare here that it's not worth a separate
217 * message (but still, having something is better than having none) */
218 if (!fLoadOk && !strLangId.isNull())
219 msgCenter().cannotLoadLanguage(strLanguageFileName);
220 }
221 if (fResetToC)
222 s_strLoadedLanguageId = vboxBuiltInLanguageName();
223#ifdef VBOX_WS_MAC
224 // Qt doesn't translate the items in the Application menu initially.
225 // Manually trigger an update.
226 ::darwinRetranslateAppMenu();
227#endif
228
229 /* Iterate through all the translators: */
230 for (int i = 0; i < translators.size(); ++i)
231 {
232 /* Unlock listener before the last one translator: */
233 if (i == translators.size() - 1)
234 {
235 QCoreApplication::sendPostedEvents(0, QEvent::LanguageChange);
236 s_fTranslationInProgress = false;
237 }
238
239 /* Install current one: */
240 qApp->installTranslator(translators.at(i));
241 }
242
243 /* Unlock listener in case if it's still locked: */
244 s_fTranslationInProgress = false;
245}
246
247/* static */
248QString UITranslator::vboxLanguageSubDirectory()
249{
250 return "/nls";
251}
252
253/* static */
254QString UITranslator::vboxLanguageFileBase()
255{
256 return "VirtualBox_";
257}
258
259/* static */
260QString UITranslator::vboxLanguageFileExtension()
261{
262 return ".qm";
263}
264
265/* static */
266QString UITranslator::vboxLanguageIdRegExp()
267{
268 return "(([a-z]{2})(?:_([A-Z]{2}))?)|(C)";
269}
270
271/* static */
272QString UITranslator::vboxBuiltInLanguageName()
273{
274 return "C";
275}
276
277/* static */
278QString UITranslator::languageId()
279{
280 /* Note that it may not match with UIExtraDataManager::languageId() if the specified language cannot be loaded.
281 *
282 * If the built-in language is active, this method returns "C". "C" is treated as the built-in language for
283 * simplicity -- the C locale is used in unix environments as a fallback when the requested locale is invalid.
284 * This way we don't need to process both the "built_in" language and the "C" language (which is a valid
285 * environment setting) separately. */
286
287 return s_strLoadedLanguageId;
288}
289
290/* static */
291QString UITranslator::yearsToString(uint32_t cVal)
292{
293 return QApplication::translate("UITranslator", "%n year(s)", "", cVal);
294}
295
296/* static */
297QString UITranslator::monthsToString(uint32_t cVal)
298{
299 return QApplication::translate("UITranslator", "%n month(s)", "", cVal);
300}
301
302/* static */
303QString UITranslator::daysToString(uint32_t cVal)
304{
305 return QApplication::translate("UITranslator", "%n day(s)", "", cVal);
306}
307
308/* static */
309QString UITranslator::hoursToString(uint32_t cVal)
310{
311 return QApplication::translate("UITranslator", "%n hour(s)", "", cVal);
312}
313
314/* static */
315QString UITranslator::minutesToString(uint32_t cVal)
316{
317 return QApplication::translate("UITranslator", "%n minute(s)", "", cVal);
318}
319
320/* static */
321QString UITranslator::secondsToString(uint32_t cVal)
322{
323 return QApplication::translate("UITranslator", "%n second(s)", "", cVal);
324}
325
326/* static */
327QString UITranslator::yearsToStringAgo(uint32_t cVal)
328{
329 return QApplication::translate("UITranslator", "%n year(s) ago", "", cVal);
330}
331
332/* static */
333QString UITranslator::monthsToStringAgo(uint32_t cVal)
334{
335 return QApplication::translate("UITranslator", "%n month(s) ago", "", cVal);
336}
337
338/* static */
339QString UITranslator::daysToStringAgo(uint32_t cVal)
340{
341 return QApplication::translate("UITranslator", "%n day(s) ago", "", cVal);
342}
343
344/* static */
345QString UITranslator::hoursToStringAgo(uint32_t cVal)
346{
347 return QApplication::translate("UITranslator", "%n hour(s) ago", "", cVal);
348}
349
350/* static */
351QString UITranslator::minutesToStringAgo(uint32_t cVal)
352{
353 return QApplication::translate("UITranslator", "%n minute(s) ago", "", cVal);
354}
355
356/* static */
357QString UITranslator::secondsToStringAgo(uint32_t cVal)
358{
359 return QApplication::translate("UITranslator", "%n second(s) ago", "", cVal);
360}
361
362/* static */
363QString UITranslator::decimalSep()
364{
365 return QString(QLocale::system().decimalPoint());
366}
367
368/* static */
369QString UITranslator::sizeRegexp()
370{
371 /* This regexp will capture 5 groups of text:
372 * - cap(1): integer number in case when no decimal point is present
373 * (if empty, it means that decimal point is present)
374 * - cap(2): size suffix in case when no decimal point is present (may be empty)
375 * - cap(3): integer number in case when decimal point is present (may be empty)
376 * - cap(4): fraction number (hundredth) in case when decimal point is present
377 * - cap(5): size suffix in case when decimal point is present (note that
378 * B cannot appear there). */
379
380 const QString strRegexp =
381 QString("^(?:(?:(\\d+)(?:\\s?(%2|%3|%4|%5|%6|%7))?)|(?:(\\d*)%1(\\d{1,2})(?:\\s?(%3|%4|%5|%6|%7))))$")
382 .arg(decimalSep())
383 .arg(tr("B", "size suffix Bytes"))
384 .arg(tr("KB", "size suffix KBytes=1024 Bytes"))
385 .arg(tr("MB", "size suffix MBytes=1024 KBytes"))
386 .arg(tr("GB", "size suffix GBytes=1024 MBytes"))
387 .arg(tr("TB", "size suffix TBytes=1024 GBytes"))
388 .arg(tr("PB", "size suffix PBytes=1024 TBytes"));
389 return strRegexp;
390}
391
392/* static */
393quint64 UITranslator::parseSize(const QString &strText)
394{
395 /* Text should be in form of B|KB|MB|GB|TB|PB. */
396 QRegExp regexp(sizeRegexp());
397 int iPos = regexp.indexIn(strText);
398 if (iPos != -1)
399 {
400 QString strInteger = regexp.cap(1);
401 QString strHundred;
402 QString strSuff = regexp.cap(2);
403 if (strInteger.isEmpty())
404 {
405 strInteger = regexp.cap(3);
406 strHundred = regexp.cap(4);
407 strSuff = regexp.cap(5);
408 }
409
410 quint64 uDenominator = 0;
411 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
412 uDenominator = 1;
413 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
414 uDenominator = _1K;
415 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
416 uDenominator = _1M;
417 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
418 uDenominator = _1G;
419 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
420 uDenominator = _1T;
421 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
422 uDenominator = _1P;
423
424 quint64 iInteger = strInteger.toULongLong();
425 if (uDenominator == 1)
426 return iInteger;
427
428 quint64 iHundred = strHundred.leftJustified(2, '0').toULongLong();
429 iHundred = iHundred * uDenominator / 100;
430 iInteger = iInteger * uDenominator + iHundred;
431 return iInteger;
432 }
433 else
434 return 0;
435}
436
437/* static */
438SizeSuffix UITranslator::parseSizeSuffix(const QString &strText)
439{
440 /* Text should be in form of B|KB|MB|GB|TB|PB. */
441 QRegExp regexp(sizeRegexp());
442 int iPos = regexp.indexIn(strText);
443 if (iPos != -1)
444 {
445 QString strInteger = regexp.cap(1);
446 QString strSuff = regexp.cap(2);
447 if (strInteger.isEmpty())
448 {
449 strInteger = regexp.cap(3);
450 strSuff = regexp.cap(5);
451 }
452
453 SizeSuffix enmSizeSuffix = SizeSuffix_Byte;
454
455 if (strSuff.isEmpty() || strSuff == tr("B", "size suffix Bytes"))
456 enmSizeSuffix = SizeSuffix_Byte;
457 else if (strSuff == tr("KB", "size suffix KBytes=1024 Bytes"))
458 enmSizeSuffix = SizeSuffix_KiloByte;
459 else if (strSuff == tr("MB", "size suffix MBytes=1024 KBytes"))
460 enmSizeSuffix = SizeSuffix_MegaByte;
461 else if (strSuff == tr("GB", "size suffix GBytes=1024 MBytes"))
462 enmSizeSuffix = SizeSuffix_GigaByte;
463 else if (strSuff == tr("TB", "size suffix TBytes=1024 GBytes"))
464 enmSizeSuffix = SizeSuffix_TeraByte;
465 else if (strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
466 enmSizeSuffix = SizeSuffix_PetaByte;
467 return enmSizeSuffix;
468 }
469 else
470 return SizeSuffix_Byte;
471}
472
473/* static */
474bool UITranslator::hasSizeSuffix(const QString &strText)
475{
476 /* Text should be in form of B|KB|MB|GB|TB|PB. */
477 QRegExp regexp(sizeRegexp());
478 int iPos = regexp.indexIn(strText);
479 if (iPos != -1)
480 {
481 QString strInteger = regexp.cap(1);
482 QString strSuff = regexp.cap(2);
483 if (strInteger.isEmpty())
484 {
485 strInteger = regexp.cap(3);
486 strSuff = regexp.cap(5);
487 }
488
489 if (strSuff.isEmpty())
490 return false;
491 if (strSuff == tr("B", "size suffix Bytes") ||
492 strSuff == tr("KB", "size suffix KBytes=1024 Bytes") ||
493 strSuff == tr("MB", "size suffix MBytes=1024 KBytes") ||
494 strSuff == tr("GB", "size suffix GBytes=1024 MBytes") ||
495 strSuff == tr("TB", "size suffix TBytes=1024 GBytes") ||
496 strSuff == tr("PB", "size suffix PBytes=1024 TBytes"))
497 return true;
498 return false;
499 }
500 else
501 return false;
502}
503
504/* static */
505QString UITranslator::formatSize(quint64 uSize, uint cDecimal /* = 2 */,
506 FormatSize enmMode /* = FormatSize_Round */)
507{
508 /* Text will be in form of B|KB|MB|GB|TB|PB.
509 *
510 * When enmMode is FormatSize_Round, the result is rounded to the
511 * closest number containing @a aDecimal decimal digits.
512 * When enmMode is FormatSize_RoundDown, the result is rounded to the
513 * largest number with @a aDecimal decimal digits that is not greater than
514 * the result. This guarantees that converting the resulting string back to
515 * the integer value in bytes will not produce a value greater that the
516 * initial size parameter.
517 * When enmMode is FormatSize_RoundUp, the result is rounded to the
518 * smallest number with @a aDecimal decimal digits that is not less than the
519 * result. This guarantees that converting the resulting string back to the
520 * integer value in bytes will not produce a value less that the initial
521 * size parameter. */
522
523 quint64 uDenominator = 0;
524 int iSuffix = 0;
525
526 if (uSize < _1K)
527 {
528 uDenominator = 1;
529 iSuffix = 0;
530 }
531 else if (uSize < _1M)
532 {
533 uDenominator = _1K;
534 iSuffix = 1;
535 }
536 else if (uSize < _1G)
537 {
538 uDenominator = _1M;
539 iSuffix = 2;
540 }
541 else if (uSize < _1T)
542 {
543 uDenominator = _1G;
544 iSuffix = 3;
545 }
546 else if (uSize < _1P)
547 {
548 uDenominator = _1T;
549 iSuffix = 4;
550 }
551 else
552 {
553 uDenominator = _1P;
554 iSuffix = 5;
555 }
556
557 quint64 uInteger = uSize / uDenominator;
558 quint64 uDecimal = uSize % uDenominator;
559 quint64 uMult = 1;
560 for (uint i = 0; i < cDecimal; ++i)
561 uMult *= 10;
562
563 QString strNumber;
564 if (uDenominator > 1)
565 {
566 if (uDecimal)
567 {
568 uDecimal *= uMult;
569 /* Not greater: */
570 if (enmMode == FormatSize_RoundDown)
571 uDecimal = uDecimal / uDenominator;
572 /* Not less: */
573 else if (enmMode == FormatSize_RoundUp)
574 uDecimal = (uDecimal + uDenominator - 1) / uDenominator;
575 /* Nearest: */
576 else
577 uDecimal = (uDecimal + uDenominator / 2) / uDenominator;
578 }
579 /* Check for the fractional part overflow due to rounding: */
580 if (uDecimal == uMult)
581 {
582 uDecimal = 0;
583 ++uInteger;
584 /* Check if we've got 1024 XB after rounding and scale down if so: */
585 if (uInteger == 1024 && iSuffix + 1 < (int)SizeSuffix_Max)
586 {
587 uInteger /= 1024;
588 ++iSuffix;
589 }
590 }
591 strNumber = QString::number(uInteger);
592 if (cDecimal)
593 strNumber += QString("%1%2").arg(decimalSep())
594 .arg(QString::number(uDecimal).rightJustified(cDecimal, '0'));
595 }
596 else
597 {
598 strNumber = QString::number(uInteger);
599 }
600
601 return QString("%1 %2").arg(strNumber).arg(gpConverter->toString(static_cast<SizeSuffix>(iSuffix)));
602}
603
604/* static */
605QString UITranslator::addMetricSuffixToNumber(quint64 uNumber)
606{
607 if (uNumber <= 0)
608 return QString();
609 /* See https://en.wikipedia.org/wiki/Metric_prefix for metric suffixes:*/
610 char suffixes[] = {'k', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y'};
611 int zeroCount = (int)log10((long double)uNumber);
612 if (zeroCount < 3)
613 return QString::number(uNumber);
614 int h = 3 * (zeroCount / 3);
615 char result[128];
616 sprintf(result, "%.2f", uNumber / (float)pow((double)10, h));
617 return QString("%1%2").arg(result).arg(suffixes[h / 3 - 1]);
618}
619
620/* static */
621QStringList UITranslator::COMPortNames()
622{
623 QStringList list;
624 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
625 list << kComKnownPorts[i].name;
626
627 return list;
628}
629
630/* static */
631QString UITranslator::toCOMPortName(ulong uIRQ, ulong uIOBase)
632{
633 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
634 if (kComKnownPorts[i].IRQ == uIRQ &&
635 kComKnownPorts[i].IOBase == uIOBase)
636 return kComKnownPorts[i].name;
637
638 return tr("User-defined", "serial port");;
639}
640
641/* static */
642bool UITranslator::toCOMPortNumbers(const QString &strName, ulong &uIRQ, ulong &uIOBase)
643{
644 for (size_t i = 0; i < RT_ELEMENTS(kComKnownPorts); ++i)
645 if (strcmp(kComKnownPorts[i].name, strName.toUtf8().data()) == 0)
646 {
647 uIRQ = kComKnownPorts[i].IRQ;
648 uIOBase = kComKnownPorts[i].IOBase;
649 return true;
650 }
651
652 return false;
653}
654
655/* Regular expressions used by both highlight and emphasize. They use the
656 same prefix and suffix expression. Unfortunately, QRegularExpression isn't
657 thread safe, so we only store the string contstants here. */
658/** @todo qt6: Both these had bogus suffix sets '[:.-!);]', I've changed them to '[-:.!);]', hope that's correct. */
659static char const g_szRxSingleQuotes[] = "((?:^|\\s)[(]?)"
660 "'([^']*)'"
661 "(?=[-:.!);]?(?:\\s|$))";
662static const char g_szRxUuid[] = "((?:^|\\s)[(]?)"
663 "(\\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\\})"
664 "(?=[-:.!);]?(?:\\s|$))";
665
666/* static */
667QString UITranslator::highlight(QString strText, bool fToolTip /* = false */)
668{
669 /* We should reformat the input strText so that:
670 * - strings in single quotes will be put inside <nobr> and marked
671 * with blue color;
672 * - UUIDs be put inside <nobr> and marked
673 * with green color;
674 * - replaces new line chars with </p><p> constructs to form paragraphs
675 * (note that <p\> and </p> are not appended to the beginning and to the
676 * end of the string respectively, to allow the result be appended
677 * or prepended to the existing paragraph).
678 *
679 * If @a fToolTip is true, colouring is not applied, only the <nobr> tag
680 * is added. Also, new line chars are replaced with <br> instead of <p>. */
681
682 QString strFont;
683 QString uuidFont;
684 QString endFont;
685 if (!fToolTip)
686 {
687 strFont = "<font color=#0000CC>";
688 uuidFont = "<font color=#008000>";
689 endFont = "</font>";
690 }
691
692 /* Replace special entities, '&' -- first! */
693 strText.replace('&', "&amp;");
694 strText.replace('<', "&lt;");
695 strText.replace('>', "&gt;");
696 strText.replace('\"', "&quot;");
697
698 /* Mark strings in single quotes with color: */
699 strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strFont).arg(endFont));
700
701 /* Mark UUIDs with color: */
702 strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidFont).arg(endFont));
703
704 /* Split to paragraphs at \n chars: */
705 if (!fToolTip)
706 strText.replace('\n', "</p><p>");
707 else
708 strText.replace('\n', "<br>");
709
710 return strText;
711}
712
713/* static */
714QString UITranslator::emphasize(QString strText)
715{
716 /* We should reformat the input string @a strText so that:
717 * - strings in single quotes will be put inside \<nobr\> and marked
718 * with bold style;
719 * - UUIDs be put inside \<nobr\> and marked
720 * with italic style;
721 * - replaces new line chars with \</p\>\<p\> constructs to form paragraphs
722 * (note that \<p\> and \</p\> are not appended to the beginning and to the
723 * end of the string respectively, to allow the result be appended
724 * or prepended to the existing paragraph). */
725
726 QString strEmphStart("<b>");
727 QString strEmphEnd("</b>");
728 QString uuidEmphStart("<i>");
729 QString uuidEmphEnd("</i>");
730
731 /* Replace special entities, '&' -- first! */
732 strText.replace('&', "&amp;");
733 strText.replace('<', "&lt;");
734 strText.replace('>', "&gt;");
735 strText.replace('\"', "&quot;");
736
737 /* Mark strings in single quotes with bold style: */
738 strText.replace(QRegularExpression(g_szRxSingleQuotes), QString("\\1%1<nobr>'\\2'</nobr>%2").arg(strEmphStart).arg(strEmphEnd));
739
740 /* Mark UUIDs with italic style: */
741 strText.replace(QRegularExpression(g_szRxUuid), QString("\\1%1<nobr>\\2</nobr>%2").arg(uuidEmphStart).arg(uuidEmphEnd));
742
743 /* Split to paragraphs at \n chars: */
744 strText.replace('\n', "</p><p>");
745
746 return strText;
747}
748
749/* static */
750QString UITranslator::removeAccelMark(QString strText)
751{
752 /* In order to support accelerators used in non-alphabet languages
753 * (e.g. Japanese) that has a form of "(&<L>)" (where <L> is a latin letter),
754 * this method first searches for this pattern and, if found, removes it as a
755 * whole. If such a pattern is not found, then the '&' character is simply
756 * removed from the string. */
757
758 QRegExp accel("\\(&[a-zA-Z]\\)");
759 int iPos = accel.indexIn(strText);
760 if (iPos >= 0)
761 strText.remove(iPos, accel.cap().length());
762 else
763 {
764 iPos = strText.indexOf('&');
765 if (iPos >= 0)
766 strText.remove(iPos, 1);
767 }
768
769 return strText;
770}
771
772/* static */
773QString UITranslator::insertKeyToActionText(const QString &strText, const QString &strKey)
774{
775#ifdef VBOX_WS_MAC
776 QString strPattern("%1 (Host+%2)");
777#else
778 QString strPattern("%1 \tHost+%2");
779#endif
780 if ( strKey.isEmpty()
781 || strKey.compare("None", Qt::CaseInsensitive) == 0)
782 return strText;
783 else
784 return strPattern.arg(strText).arg(QKeySequence(strKey).toString(QKeySequence::NativeText));
785}
786
787/* static */
788bool UITranslator::isTranslationInProgress()
789{
790 return s_fTranslationInProgress;
791}
792
793/* static */
794QString UITranslator::byteStringToMegaByteString(const QString &strByteString)
795{
796 if (strByteString.isEmpty())
797 return QString();
798 bool fConversionSuccess = false;
799 qulonglong uByte = strByteString.toULongLong(&fConversionSuccess);
800 AssertReturn(fConversionSuccess, QString());
801 return QString::number(uByte / _1M);
802}
803
804/* static */
805QString UITranslator::megabyteStringToByteString(const QString &strMegaByteString)
806{
807 if (strMegaByteString.isEmpty())
808 return QString();
809 bool fConversionSuccess = false;
810 qulonglong uMegaByte = strMegaByteString.toULongLong(&fConversionSuccess);
811 AssertReturn(fConversionSuccess, QString());
812 return QString::number(uMegaByte * _1M);
813}
814
815UITranslator::UITranslator(QObject *pParent /* = 0 */)
816 : QTranslator(pParent)
817{
818}
819
820bool UITranslator::loadFile(const QString &strFileName)
821{
822 QFile file(strFileName);
823 if (!file.open(QIODevice::ReadOnly))
824 return false;
825 m_data = file.readAll();
826 return load((uchar*)m_data.data(), m_data.size());
827}
828
829/* static */
830QString UITranslator::languageName()
831{
832 /* Returns "English" if no translation is installed
833 * or if the translation file is invalid. */
834 return QApplication::translate("@@@", "English",
835 "Native language name");
836}
837
838/* static */
839QString UITranslator::languageCountry()
840{
841 /* Returns "--" if no translation is installed or if the translation file
842 * is invalid, or if the language is independent on the country. */
843 return QApplication::translate("@@@", "--",
844 "Native language country name "
845 "(empty if this language is for all countries)");
846}
847
848/* static */
849QString UITranslator::languageNameEnglish()
850{
851 /* Returns "English" if no translation is installed
852 * or if the translation file is invalid. */
853 return QApplication::translate("@@@", "English",
854 "Language name, in English");
855}
856
857/* static */
858QString UITranslator::languageCountryEnglish()
859{
860 /* Returns "--" if no translation is installed or if the translation file
861 * is invalid, or if the language is independent on the country. */
862 return QApplication::translate("@@@", "--",
863 "Language country name, in English "
864 "(empty if native country name is empty)");
865}
866
867/* static */
868QString UITranslator::languageTranslators()
869{
870 /* Returns "Oracle Corporation" if no translation is installed or if the translation file
871 * is invalid, or if the translation is supplied by Oracle Corporation. */
872 return QApplication::translate("@@@", "Oracle Corporation",
873 "Comma-separated list of translators");
874}
875
876/* static */
877QString UITranslator::systemLanguageId()
878{
879 /* This does exactly the same as QLocale::system().name() but corrects its wrong behavior on Linux systems
880 * (LC_NUMERIC for some strange reason takes precedence over any other locale setting in the QLocale::system()
881 * implementation). This implementation first looks at LC_ALL (as defined by SUS), then looks at LC_MESSAGES
882 * which is designed to define a language for program messages in case if it differs from the language for
883 * other locale categories. Then it looks for LANG and finally falls back to QLocale::system().name().
884 *
885 * The order of precedence is well defined here:
886 * http://opengroup.org/onlinepubs/007908799/xbd/envvar.html
887 *
888 * This method will return "C" when the requested locale is invalid or when the "C" locale is set explicitly. */
889
890#if defined(VBOX_WS_MAC)
891 // QLocale return the right id only if the user select the format
892 // of the language also. So we use our own implementation */
893 return ::darwinSystemLanguage();
894#elif defined(Q_OS_UNIX)
895 const char *pszValue = RTEnvGet("LC_ALL");
896 if (pszValue == 0)
897 pszValue = RTEnvGet("LC_MESSAGES");
898 if (pszValue == 0)
899 pszValue = RTEnvGet("LANG");
900 if (pszValue != 0)
901 return QLocale(pszValue).name();
902#endif
903 return QLocale::system().name();
904}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette