Skip to content

Commit

Permalink
BUG: Enable language translation in MRML classes
Browse files Browse the repository at this point in the history
vtkMRMLI18N singleton class is responsible for internationalization related features in MRML.
It currently only stores a language translator.

A translator that uses Qt for translation is specified in qSlicerCoreApplication.

Enables fixing of these issues:
Slicer/SlicerLanguagePacks#12
Slicer#6647
Slicer#6177

Co-authored-by: Mouhamed DIOP <mouhamed.diop@esp.sn>
  • Loading branch information
mhdiop authored and lassoan committed Sep 2, 2023
1 parent ff5700d commit 477ba0e
Show file tree
Hide file tree
Showing 7 changed files with 391 additions and 0 deletions.
30 changes: 30 additions & 0 deletions Base/QTCore/qSlicerCoreApplication.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,9 @@
#ifdef Slicer_BUILD_CLI_SUPPORT
# include <vtkMRMLCommandLineModuleNode.h>
#endif
#include <vtkMRMLI18N.h>
#include <vtkMRMLScene.h>
#include <vtkMRMLTranslator.h>

// CTK includes
#include <ctkUtils.h>
Expand Down Expand Up @@ -153,6 +155,30 @@
#include <ctkDICOMDatabase.h>
#endif

//-----------------------------------------------------------------------------
// Adapter class for translation in MRML classes using Qt translation infrastructure

class vtkQtTranslator: public vtkMRMLTranslator
{
public:
static vtkQtTranslator* New();
vtkTypeMacro(vtkQtTranslator, vtkMRMLTranslator);

/// Translation function for logic classes
std::string Translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1) override
{
return QCoreApplication::translate(context, sourceText, disambiguation, n).toStdString();
}

protected:
vtkQtTranslator() = default;
~vtkQtTranslator() override = default;
vtkQtTranslator(const vtkQtTranslator&) = delete;
void operator=(const vtkQtTranslator&) = delete;
};

vtkStandardNewMacro(vtkQtTranslator);

//-----------------------------------------------------------------------------
// Helper function

Expand Down Expand Up @@ -353,6 +379,10 @@ void qSlicerCoreApplicationPrivate::init()
vtkEventBroker::GetInstance()->SetRequestModifiedCallback(modifiedRequestCallback);
}

// Set up translation in MRML classes using Qt translator
vtkNew<vtkQtTranslator> mrmlTranslator;
vtkMRMLI18N::GetInstance()->SetTranslator(mrmlTranslator);

// Ensure that temporary folder is writable
{
// QTemporaryFile is deleted automatically when leaving this scope
Expand Down
3 changes: 3 additions & 0 deletions Libs/MRML/Core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ set(MRMLCore_SRCS
vtkCodedEntry.cxx
vtkEventBroker.cxx
vtkDataFileFormatHelper.cxx
vtkMRMLI18N.cxx
vtkMRMLI18N.h
vtkMRMLMeasurement.cxx
vtkMRMLStaticMeasurement.cxx
vtkMRMLLogic.cxx
Expand Down Expand Up @@ -221,6 +223,7 @@ set(MRMLCore_SRCS
vtkMRMLTransformStorageNode.cxx
vtkMRMLTransformDisplayNode.cxx
vtkMRMLTransformableNode.cxx
vtkMRMLTranslator.h
vtkMRMLUnitNode.cxx
vtkMRMLVectorVolumeDisplayNode.cxx
vtkMRMLViewNode.cxx
Expand Down
1 change: 1 addition & 0 deletions Libs/MRML/Core/Testing/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ create_test_sourcelist(Tests ${KIT}CxxTests.cxx
vtkMRMLGridTransformNodeTest1.cxx
vtkMRMLHierarchyNodeTest1.cxx
vtkMRMLHierarchyNodeTest3.cxx
vtkMRMLI18NTest1.cxx
vtkMRMLInteractionNodeTest1.cxx
vtkMRMLLabelMapVolumeDisplayNodeTest1.cxx
vtkMRMLLayoutNodeTest1.cxx
Expand Down
61 changes: 61 additions & 0 deletions Libs/MRML/Core/Testing/vtkMRMLI18NTest1.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*=auto=========================================================================
Portions (c) Copyright 2005 Brigham and Women's Hospital (BWH)
All Rights Reserved.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Program: 3D Slicer
=========================================================================auto=*/

// MRML includes
#include "vtkMRMLCoreTestingMacros.h"
#include "vtkMRMLScene.h"

// VTK includes
#include <vtkMRMLI18N.h>
#include <vtkMRMLTranslator.h>

namespace
{

class vtkTestTranslator : public vtkMRMLTranslator
{
public:
static vtkTestTranslator * New();
vtkTypeMacro(vtkTestTranslator, vtkMRMLTranslator);

/// Translation method for testing that returns "translated-(context)(sourceText)" as translation
std::string Translate(const char* context, const char* sourceText, const char* disambiguation = nullptr, int n = -1) override
{
return std::string("translated-") + context + sourceText;
}

protected:
vtkTestTranslator () = default;
~vtkTestTranslator () override = default;
vtkTestTranslator (const vtkTestTranslator &) = delete;
void operator=(const vtkTestTranslator &) = delete;
};

vtkStandardNewMacro(vtkTestTranslator );
}

int vtkMRMLI18NTest1(int, char*[])
{
// Check default translation (simply sourcetext is returned)
CHECK_STD_STRING(vtkMRMLI18N::Translate("SomeContext", "SomeMessage"), "SomeMessage");

// Set custom translator
vtkNew<vtkTestTranslator> translator;
vtkMRMLI18N::GetInstance()->SetTranslator(translator);

// Check translation with custom translator
CHECK_STD_STRING(vtkMRMLI18N::Translate("SomeContext", "SomeMessage"), "translated-SomeContextSomeMessage");
// Use translation convenience function
CHECK_STD_STRING(vtkMRMLTr("SomeContext", "SomeMessage"), "translated-SomeContextSomeMessage");

return EXIT_SUCCESS;
}
149 changes: 149 additions & 0 deletions Libs/MRML/Core/vtkMRMLI18N.cxx
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/*==============================================================================
Program: 3D Slicer
Copyright(c) Kitware Inc.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

// MRML includes
#include "vtkMRMLI18N.h"
#include "vtkMRMLTranslator.h"

// VTK includes
#include <vtkObjectFactory.h>

vtkCxxSetObjectMacro(vtkMRMLI18N, Translator, vtkMRMLTranslator);

//----------------------------------------------------------------------------
// The i18n manager singleton.
// This MUST be default initialized to zero by the compiler and is
// therefore not initialized here. The ClassInitialize and
// ClassFinalize methods handle this instance.
static vtkMRMLI18N* vtkMRMLI18NInstance;

//----------------------------------------------------------------------------
// Must NOT be initialized. Default initialization to zero is necessary.
unsigned int vtkMRMLI18NInitialize::Count;

//----------------------------------------------------------------------------
// Implementation of vtkMRMLI18NInitialize class.
//----------------------------------------------------------------------------
vtkMRMLI18NInitialize::vtkMRMLI18NInitialize()
{
if(++Self::Count == 1)
{
vtkMRMLI18N::classInitialize();
}
}

//----------------------------------------------------------------------------
vtkMRMLI18NInitialize::~vtkMRMLI18NInitialize()
{
if(--Self::Count == 0)
{
vtkMRMLI18N::classFinalize();
}
}

//----------------------------------------------------------------------------

//----------------------------------------------------------------------------
// Up the reference count so it behaves like New
vtkMRMLI18N* vtkMRMLI18N::New()
{
vtkMRMLI18N* ret = vtkMRMLI18N::GetInstance();
ret->Register(nullptr);
return ret;
}

//----------------------------------------------------------------------------
// Return the single instance of the vtkMRMLI18N
vtkMRMLI18N* vtkMRMLI18N::GetInstance()
{
if(!vtkMRMLI18NInstance)
{
// Try the factory first
vtkMRMLI18NInstance = (vtkMRMLI18N*)vtkObjectFactory::CreateInstance("vtkMRMLI18N");
// if the factory did not provide one, then create it here
if(!vtkMRMLI18NInstance)
{
vtkMRMLI18NInstance = new vtkMRMLI18N;
#ifdef VTK_HAS_INITIALIZE_OBJECT_BASE
vtkMRMLI18NInstance->InitializeObjectBase();
#endif
}
}
// return the instance
return vtkMRMLI18NInstance;
}

//----------------------------------------------------------------------------
vtkMRMLI18N::vtkMRMLI18N()
{
this->Translator = nullptr;
}

//----------------------------------------------------------------------------
vtkMRMLI18N::~vtkMRMLI18N()
{
if (this->Translator)
{
this->Translator->Delete();
}
this->Translator = nullptr;
}

//----------------------------------------------------------------------------
void vtkMRMLI18N::PrintSelf(ostream& os, vtkIndent indent)
{
this->vtkObject::PrintSelf(os, indent);

os << indent << "Translator:";
if (this->GetTranslator())
{
this->GetTranslator()->PrintSelf(os, indent.GetNextIndent());
}
else
{
os << " (none)" << "\n";
}
}

//----------------------------------------------------------------------------
void vtkMRMLI18N::classInitialize()
{
// Allocate the singleton
vtkMRMLI18NInstance = vtkMRMLI18N::GetInstance();
}

//----------------------------------------------------------------------------
void vtkMRMLI18N::classFinalize()
{
vtkMRMLI18NInstance->Delete();
vtkMRMLI18NInstance = nullptr;
}

//----------------------------------------------------------------------------
std::string vtkMRMLI18N::Translate(const char *context, const char *sourceText, const char *disambiguation/*=nullptr*/, int n/*=-1*/)
{
vtkMRMLI18N* i18n = vtkMRMLI18N::GetInstance();
vtkMRMLTranslator* translator = i18n ? i18n->GetTranslator() : nullptr;
if (translator)
{
return translator->Translate(context, sourceText, disambiguation, n);
}
else
{
return sourceText ? sourceText : "";
}
}
98 changes: 98 additions & 0 deletions Libs/MRML/Core/vtkMRMLI18N.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*==============================================================================
Program: 3D Slicer
Copyright(c) Kitware Inc.
See COPYRIGHT.txt
or http://www.slicer.org/copyright/copyright.txt for details.
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#ifndef __vtkMRMLI18N_h
#define __vtkMRMLI18N_h

// MRML includes
#include "vtkMRML.h"

// VTK includes
#include <vtkObject.h>

class vtkMRMLTranslator;

/// \brief Class that provide internationalization (i18n) features,
/// such as language translation or region-specific units and date formatting.
///
class VTK_MRML_EXPORT vtkMRMLI18N : public vtkObject
{
public:
vtkTypeMacro(vtkMRMLI18N, vtkObject);
void PrintSelf(ostream& os, vtkIndent indent) override;

///
/// Return the singleton instance with no reference counting.
static vtkMRMLI18N* GetInstance();

///
/// This is a singleton pattern New. There will only be ONE
/// reference to a vtkMRMLI18N object per process. Clients that
/// call this must call Delete on the object so that the reference
/// counting will work. The single instance will be unreferenced when
/// the program exits.
static vtkMRMLI18N* New();

/// Translate message with the current translator
static std::string Translate(const char *context, const char *sourceText, const char *disambiguation = nullptr, int n = -1);

/// Set translator object. This class takes ownership of the translator
/// and it releases it when the process quits.
void SetTranslator(vtkMRMLTranslator* translator);

/// Get translator object that can translate text that is displayed to the user
/// to the currently chosen language.
vtkGetObjectMacro (Translator, vtkMRMLTranslator);

protected:
vtkMRMLI18N();
~vtkMRMLI18N() override;
vtkMRMLI18N(const vtkMRMLI18N&);
void operator=(const vtkMRMLI18N&);

///
/// Singleton management functions.
static void classInitialize();
static void classFinalize();

friend class vtkMRMLI18NInitialize;
typedef vtkMRMLI18N Self;

vtkMRMLTranslator* Translator{ nullptr };
};

/// Utility class to make sure qSlicerModuleManager is initialized before it is used.
class VTK_MRML_EXPORT vtkMRMLI18NInitialize
{
public:
typedef vtkMRMLI18NInitialize Self;

vtkMRMLI18NInitialize();
~vtkMRMLI18NInitialize();
private:
static unsigned int Count;
};

/// This instance will show up in any translation unit that uses
/// vtkMRMLI18N. It will make sure vtkMRMLI18N is initialized
/// before it is used.
static vtkMRMLI18NInitialize vtkMRMLI18NInitializer;

/// Translation function used in MRML classes
#define vtkMRMLTr(context, sourceText) vtkMRMLI18N::Translate(context, sourceText)

#endif
Loading

0 comments on commit 477ba0e

Please sign in to comment.