VirtualBox

source: vbox/trunk/src/VBox/Main/src-all/VirtualBoxTranslator.cpp@ 91312

Last change on this file since 91312 was 91312, checked in by vboxsync, 4 years ago

Main: bugref:1909: Prepared the API translation engine to using in ExtPacks and VBoxManage. Added using API translation engine in ExtPacks. Allowed VBox compilation with NLS enabled and GUI disabled. Allowed ExtPacks only compilation with NLS translation enabled.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 15.3 KB
Line 
1/* $Id: VirtualBoxTranslator.cpp 91312 2021-09-20 11:06:57Z vboxsync $ */
2/** @file
3 * VirtualBox Translator class.
4 */
5
6/*
7 * Copyright (C) 2006-2020 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18#include <iprt/asm.h>
19#include <iprt/ctype.h>
20#include <iprt/err.h>
21#include <iprt/locale.h>
22#include <iprt/once.h>
23#include <iprt/path.h>
24#include <iprt/string.h>
25#include <iprt/thread.h>
26#include <iprt/strcache.h>
27
28#include "Global.h"
29#include "VirtualBoxBase.h"
30#include "QMTranslator.h"
31#include "VirtualBoxTranslator.h"
32
33#define TRANSLATOR_CACHE_SIZE 32
34
35/** Init once for the critical section. */
36static RTONCE g_Once = RTONCE_INITIALIZER;
37RTCRITSECTRW VirtualBoxTranslator::s_instanceRwLock;
38VirtualBoxTranslator *VirtualBoxTranslator::s_pInstance = NULL;
39static RTTLS g_idxTls = NIL_RTTLS;
40
41typedef std::pair<const char *, const char *> LastTranslation;
42
43/**
44 * @callback_method_impl{FNRTONCE}
45 */
46static DECLCALLBACK(int32_t) initLock(void *pvUser)
47{
48 RT_NOREF(pvUser);
49 return VirtualBoxTranslator::initCritSect();
50}
51
52
53/**
54 * @callback_method_impl{FNRTTLSDTOR, Destroys the cache during thread termination.}
55 */
56static DECLCALLBACK(void) freeThreadCache(void *pvValue) RT_NOTHROW_DEF
57{
58 if (pvValue != NULL)
59 {
60 LastTranslation *pCache = (LastTranslation *)pvValue;
61 delete pCache;
62 }
63}
64
65
66VirtualBoxTranslator::VirtualBoxTranslator()
67 : util::RWLockHandle(VBoxLockingClass::LOCKCLASS_TRANSLATOR)
68 , m_cInstanceRefs(0)
69 , m_pDefaultComponent(NULL)
70 , m_strLanguage("C")
71 , m_hStrCache(NIL_RTSTRCACHE)
72{
73 RTTlsAllocEx(&g_idxTls, &freeThreadCache);
74 int rc = RTStrCacheCreate(&m_hStrCache, "API Translation");
75 m_rcCache = rc;
76 if (RT_FAILURE(rc))
77 m_hStrCache = NIL_RTSTRCACHE; /* (loadLanguage will fail) */
78}
79
80
81VirtualBoxTranslator::~VirtualBoxTranslator()
82{
83 if (g_idxTls != NIL_RTTLS)
84 {
85 void *pvTlsValue = NULL;
86 int rc = RTTlsGetEx(g_idxTls, &pvTlsValue);
87 if (RT_SUCCESS(rc))
88 {
89 freeThreadCache(pvTlsValue);
90 RTTlsSet(g_idxTls, NULL);
91 }
92 RTTlsFree(g_idxTls);
93 g_idxTls = NIL_RTTLS;
94 }
95
96 m_pDefaultComponent = NULL;
97
98 for (TranslatorList::iterator it = m_lTranslators.begin();
99 it != m_lTranslators.end();
100 ++it)
101 {
102 if (it->pTranslator != NULL)
103 delete it->pTranslator;
104 it->pTranslator = NULL;
105 }
106 if (m_hStrCache != NIL_RTSTRCACHE)
107 {
108 RTStrCacheDestroy(m_hStrCache);
109 m_hStrCache = NIL_RTSTRCACHE;
110 m_rcCache = VERR_WRONG_ORDER;
111 }
112}
113
114
115/**
116 * Get or create a translator instance (singelton), referenced.
117 *
118 * The main reference is held by the main VBox singelton objects (VirtualBox,
119 * VirtualBoxClient) tying it's lifetime to theirs.
120 */
121/* static */
122VirtualBoxTranslator *VirtualBoxTranslator::instance()
123{
124 int rc = RTOnce(&g_Once, initLock, NULL);
125 if (RT_SUCCESS(rc))
126 {
127 RTCritSectRwEnterShared(&s_instanceRwLock);
128 VirtualBoxTranslator *pInstance = s_pInstance;
129 if (RT_LIKELY(pInstance != NULL))
130 {
131 uint32_t cRefs = ASMAtomicIncU32(&pInstance->m_cInstanceRefs);
132 Assert(cRefs > 1); Assert(cRefs < _8K); RT_NOREF(cRefs);
133 RTCritSectRwLeaveShared(&s_instanceRwLock);
134 return pInstance;
135 }
136
137 /* Maybe create the instance: */
138 RTCritSectRwLeaveShared(&s_instanceRwLock);
139 RTCritSectRwEnterExcl(&s_instanceRwLock);
140 pInstance = s_pInstance;
141 if (pInstance == NULL)
142 s_pInstance = pInstance = new VirtualBoxTranslator();
143 ASMAtomicIncU32(&pInstance->m_cInstanceRefs);
144 RTCritSectRwLeaveExcl(&s_instanceRwLock);
145 return pInstance;
146 }
147 return NULL;
148}
149
150
151/* static */
152VirtualBoxTranslator *VirtualBoxTranslator::tryInstance()
153{
154 int rc = RTOnce(&g_Once, initLock, NULL);
155 if (RT_SUCCESS(rc))
156 {
157 RTCritSectRwEnterShared(&s_instanceRwLock);
158 VirtualBoxTranslator *pInstance = s_pInstance;
159 if (RT_LIKELY(pInstance != NULL))
160 {
161 uint32_t cRefs = ASMAtomicIncU32(&pInstance->m_cInstanceRefs);
162 Assert(cRefs > 1); Assert(cRefs < _8K); RT_NOREF(cRefs);
163 }
164 RTCritSectRwLeaveShared(&s_instanceRwLock);
165 return pInstance;
166 }
167 return NULL;
168}
169
170
171/**
172 * Release translator reference previous obtained via instance() or
173 * tryinstance().
174 */
175void VirtualBoxTranslator::release()
176{
177 RTCritSectRwEnterShared(&s_instanceRwLock);
178 uint32_t cRefs = ASMAtomicDecU32(&m_cInstanceRefs);
179 Assert(cRefs < _8K);
180 if (RT_LIKELY(cRefs > 0))
181 RTCritSectRwLeaveShared(&s_instanceRwLock);
182 else
183 {
184 /* Looks like we've got the last reference. Must switch to exclusive
185 mode for safe cleanup. */
186 ASMAtomicIncU32(&m_cInstanceRefs);
187 RTCritSectRwLeaveShared(&s_instanceRwLock);
188 RTCritSectRwEnterExcl(&s_instanceRwLock);
189 cRefs = ASMAtomicDecU32(&m_cInstanceRefs);
190 Assert(cRefs < _8K);
191 if (cRefs == 0)
192 {
193 s_pInstance = NULL;
194 delete this;
195 }
196 RTCritSectRwLeaveExcl(&s_instanceRwLock);
197 }
198}
199
200
201HRESULT VirtualBoxTranslator::loadLanguage(ComPtr<IVirtualBox> aVirtualBox)
202{
203 AssertReturn(aVirtualBox, E_INVALIDARG);
204
205 ComPtr<ISystemProperties> pSystemProperties;
206 HRESULT hrc = aVirtualBox->COMGETTER(SystemProperties)(pSystemProperties.asOutParam());
207 if (SUCCEEDED(hrc))
208 {
209 com::Bstr bstrLocale;
210 hrc = pSystemProperties->COMGETTER(LanguageId)(bstrLocale.asOutParam());
211 if (SUCCEEDED(hrc))
212 {
213 int vrc = i_loadLanguage(com::Utf8Str(bstrLocale).c_str());
214 if (RT_FAILURE(vrc))
215 hrc = Global::vboxStatusCodeToCOM(vrc);
216 }
217 }
218 return hrc;
219}
220
221
222int VirtualBoxTranslator::i_loadLanguage(const char *pszLang)
223{
224 int rc = VINF_SUCCESS;
225 char szLocale[256];
226 if (pszLang == NULL || *pszLang == '\0')
227 {
228 rc = RTLocaleQueryNormalizedBaseLocaleName(szLocale, sizeof(szLocale));
229 if (RT_SUCCESS(rc))
230 pszLang = szLocale;
231 }
232 else
233 {
234 /* check the pszLang looks like language code, i.e. {ll} or {ll}_{CC} */
235 size_t cbLang = strlen(pszLang);
236 if ( !(cbLang == 1 && pszLang[0] == 'C')
237 && !(cbLang == 2 && RT_C_IS_LOWER(pszLang[0]) && RT_C_IS_LOWER(pszLang[1]))
238 && !(cbLang == 5 && RTLOCALE_IS_LANGUAGE2_UNDERSCORE_COUNTRY2(pszLang)))
239 rc = VERR_INVALID_PARAMETER;
240 }
241 if (RT_SUCCESS(rc))
242 {
243 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
244
245 m_strLanguage = pszLang;
246
247 for (TranslatorList::iterator it = m_lTranslators.begin();
248 it != m_lTranslators.end();
249 ++it)
250 {
251 /* ignore errors from particular translator allowing the use of others */
252 i_loadLanguageForComponent(&(*it), pszLang);
253 }
254 }
255 return rc;
256}
257
258
259int VirtualBoxTranslator::i_loadLanguageForComponent(TranslatorComponent *aComponent, const char *aLang)
260{
261 AssertReturn(aComponent, VERR_INVALID_PARAMETER);
262 int rc;
263 if (strcmp(aLang, "C") != 0)
264 {
265 /* Construct the base filename for the translations: */
266 char szNlsPath[RTPATH_MAX];
267 /* Try load language file on form 'VirtualBoxAPI_ll_CC.qm' if it exists
268 where 'll_CC' could for example be 'en_US' or 'de_CH': */
269 ssize_t cchOkay = RTStrPrintf2(szNlsPath, sizeof(szNlsPath), "%s_%s.qm",
270 aComponent->strPath.c_str(), aLang);
271 if (cchOkay > 0)
272 rc = i_setLanguageFile(aComponent, szNlsPath);
273 else
274 rc = VERR_FILENAME_TOO_LONG;
275 if (RT_FAILURE(rc))
276 {
277 /* No luck, drop the country part, i.e. 'VirtualBoxAPI_de.qm' or 'VirtualBoxAPI_en.qm': */
278 const char *pszDash = strchr(aLang, '_');
279 if (pszDash && pszDash != aLang)
280 {
281 cchOkay = RTStrPrintf2(szNlsPath, sizeof(szNlsPath), "%s_%.*s.qm",
282 aComponent->strPath.c_str(), pszDash - aLang, aLang);
283 if (cchOkay > 0)
284 rc = i_setLanguageFile(aComponent, szNlsPath);
285 }
286 }
287 }
288 else
289 {
290 /* No translator needed for 'C' */
291 delete aComponent->pTranslator;
292 aComponent->pTranslator = NULL;
293 rc = VINF_SUCCESS;
294 }
295 return rc;
296}
297
298
299int VirtualBoxTranslator::i_setLanguageFile(TranslatorComponent *aComponent, const char *aFileName)
300{
301 AssertReturn(aComponent, VERR_INVALID_PARAMETER);
302
303 int rc = m_rcCache;
304 if (m_hStrCache != NIL_RTSTRCACHE)
305 {
306 QMTranslator *pNewTranslator;
307 try { pNewTranslator = new QMTranslator(); }
308 catch (std::bad_alloc &) { pNewTranslator = NULL; }
309 if (pNewTranslator)
310 {
311 rc = pNewTranslator->load(aFileName, m_hStrCache);
312 if (RT_SUCCESS(rc))
313 {
314 if (aComponent->pTranslator)
315 delete aComponent->pTranslator;
316 aComponent->pTranslator = pNewTranslator;
317 }
318 else
319 delete pNewTranslator;
320 }
321 else
322 rc = VERR_NO_MEMORY;
323 }
324 else
325 Assert(RT_FAILURE_NP(rc));
326 return rc;
327}
328
329
330int VirtualBoxTranslator::registerTranslation(const char *aTranslationPath,
331 bool aDefault,
332 TRCOMPONENT *aComponent)
333{
334 VirtualBoxTranslator *pCurrInstance = VirtualBoxTranslator::tryInstance();
335 int rc = VERR_GENERAL_FAILURE;
336 if (pCurrInstance != NULL)
337 {
338 rc = pCurrInstance->i_registerTranslation(aTranslationPath, aDefault, aComponent);
339 pCurrInstance->release();
340 }
341 return rc;
342}
343
344
345int VirtualBoxTranslator::i_registerTranslation(const char *aTranslationPath,
346 bool aDefault,
347 TRCOMPONENT *aComponent)
348{
349 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
350 TranslatorComponent *pComponent;
351 for (TranslatorList::iterator it = m_lTranslators.begin();
352 it != m_lTranslators.end();
353 ++it)
354 {
355 if (it->strPath == aTranslationPath)
356 {
357 pComponent = &(*it);
358 if (aDefault)
359 m_pDefaultComponent = pComponent;
360 *aComponent = (TRCOMPONENT)pComponent;
361 return VINF_SUCCESS;
362 }
363 }
364
365 try
366 {
367 m_lTranslators.push_back(TranslatorComponent());
368 pComponent = &m_lTranslators.back();
369 }
370 catch(std::bad_alloc &)
371 {
372 return VERR_NO_MEMORY;
373 }
374
375 pComponent->strPath = aTranslationPath;
376 if (aDefault)
377 m_pDefaultComponent = pComponent;
378 *aComponent = (TRCOMPONENT)pComponent;
379 /* ignore the error during loading because path
380 * could contain no translation for current language */
381 i_loadLanguageForComponent(pComponent, m_strLanguage.c_str());
382 return VINF_SUCCESS;
383}
384
385
386int VirtualBoxTranslator::unregisterTranslation(TRCOMPONENT aComponent)
387{
388 VirtualBoxTranslator *pCurrInstance = VirtualBoxTranslator::tryInstance();
389 int rc = VERR_GENERAL_FAILURE;
390 if (pCurrInstance != NULL)
391 {
392 rc = pCurrInstance->i_unregisterTranslation(aComponent);
393 pCurrInstance->release();
394 }
395 return rc;
396}
397
398
399int VirtualBoxTranslator::i_unregisterTranslation(TRCOMPONENT aComponent)
400{
401 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
402
403 TranslatorComponent *pComponent = (TranslatorComponent *)aComponent;
404
405 if (pComponent == m_pDefaultComponent)
406 m_pDefaultComponent = NULL;
407
408 for (TranslatorList::iterator it = m_lTranslators.begin();
409 it != m_lTranslators.end();
410 ++it)
411 {
412 if (&(*it) == pComponent)
413 {
414 delete pComponent->pTranslator;
415 m_lTranslators.erase(it);
416 return VINF_SUCCESS;
417 }
418 }
419
420 return VERR_NOT_FOUND;
421}
422
423
424const char *VirtualBoxTranslator::translate(TRCOMPONENT aComponent,
425 const char *aContext,
426 const char *aSourceText,
427 const char *aComment,
428 const int aNum)
429{
430 VirtualBoxTranslator *pCurrInstance = VirtualBoxTranslator::tryInstance();
431 const char *pszTranslation = aSourceText;
432 if (pCurrInstance != NULL)
433 {
434 pszTranslation = pCurrInstance->i_translate(aComponent, aContext,
435 aSourceText, aComment, aNum);
436 pCurrInstance->release();
437 }
438 return pszTranslation;
439}
440
441
442static LastTranslation *getTlsEntry() RT_NOEXCEPT
443{
444 if (RT_LIKELY(g_idxTls != NIL_RTTLS))
445 {
446 LastTranslation *pEntry = (LastTranslation *)RTTlsGet(g_idxTls);
447 if (pEntry != NULL)
448 return pEntry; /* likely */
449
450 /* Create a new entry. */
451 try
452 {
453 pEntry = new LastTranslation();
454 pEntry->first = pEntry->second = "";
455 int rc = RTTlsSet(g_idxTls, pEntry);
456 if (RT_SUCCESS(rc))
457 return pEntry;
458 delete pEntry;
459 }
460 catch (std::bad_alloc &)
461 {
462 }
463 }
464 return NULL;
465}
466
467
468const char *VirtualBoxTranslator::i_translate(TRCOMPONENT aComponent,
469 const char *aContext,
470 const char *aSourceText,
471 const char *aComment,
472 const int aNum)
473{
474 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
475
476 TranslatorComponent *pComponent = (TranslatorComponent *)aComponent;
477 if (pComponent == NULL)
478 pComponent = m_pDefaultComponent;
479
480 if ( pComponent == NULL
481 || pComponent->pTranslator == NULL)
482 return aSourceText;
483
484 const char *pszTranslation = pComponent->pTranslator->translate(aContext, aSourceText, aComment, aNum);
485
486 LastTranslation *pEntry = getTlsEntry();
487 if (pEntry)
488 {
489 pEntry->first = pszTranslation;
490 pEntry->second = m_hStrCache != NIL_RTSTRCACHE ?
491 RTStrCacheEnter(m_hStrCache, aSourceText) :
492 aSourceText;
493 }
494
495 return pszTranslation;
496}
497
498
499const char *VirtualBoxTranslator::trSource(const char *aTranslation)
500{
501 const char *pszSource = aTranslation;
502 VirtualBoxTranslator *pCurInstance = VirtualBoxTranslator::tryInstance(); /* paranoia */
503 if (pCurInstance != NULL)
504 {
505 LastTranslation *pEntry = getTlsEntry();
506 if ( pEntry != NULL
507 && ( pEntry->first == aTranslation
508 || strcmp(pEntry->first, aTranslation) == 0) )
509 pszSource = pEntry->second;
510 pCurInstance->release();
511 }
512 return pszSource;
513}
514
515
516int32_t VirtualBoxTranslator::initCritSect()
517{
518 return RTCritSectRwInit(&s_instanceRwLock);
519}
520/* vi: set tabstop=4 shiftwidth=4 expandtab: */
Note: See TracBrowser for help on using the repository browser.

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