Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plenty chinese i18n ts files and code modification #898

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1,035 changes: 1,035 additions & 0 deletions Applications/SlicerApp/Resources/Translations/qSlicerApp_zh_cn.ts

Large diffs are not rendered by default.

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="fr_FR">
<TS version="2.0" language="zh_CN">
</TS>
4 changes: 4 additions & 0 deletions Base/QTApp/Resources/Translations/qSlicerBaseQTApp_zh_tw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0">
</TS>
70 changes: 0 additions & 70 deletions Base/QTCLI/Resources/Translations/qSlicerBaseQTCLI_fr.ts

This file was deleted.

128 changes: 128 additions & 0 deletions Base/QTCLI/Resources/Translations/qSlicerBaseQTCLI_zh_cn.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="zh_CN">
<context>
<name>QObject</name>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="124"/>
<source>Show details</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="148"/>
<source>Status:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="149"/>
<source>Idle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>qSlicerCLIModuleWidget</name>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="14"/>
<source>Module</source>
<translation>模块</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="47"/>
<source>Module Title</source>
<translation>模块标题</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="59"/>
<source>vtkMRMLCommandLineModuleNode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="70"/>
<source>Parameter set:</source>
<translation>参数</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="104"/>
<source>Reset parameters to default.</source>
<translation>重设参数回默认值</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="107"/>
<source>Restore Defaults</source>
<translation>恢复默认值</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="117"/>
<source>AutoRun</source>
<translation>自动执行</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="143"/>
<source>Cancel the execution of the module</source>
<translation>取消模块的执行</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="146"/>
<source>Cancel</source>
<translation>取消</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="156"/>
<source>Execute the module</source>
<translation>执行模块</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="159"/>
<source>Apply</source>
<translation>套用</translation>
</message>
</context>
<context>
<name>qSlicerCLIModuleWidgetPrivate</name>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="98"/>
<source>AutoRun</source>
<translation>自动执行</translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="101"/>
<source>AutoRun on changed parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="103"/>
<source>As long as the AutoRun button is down, the module is run anytime a parameter value is changed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="110"/>
<source>AutoRun on modified input</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="112"/>
<source>As long as the AutoRun button is down, the module is run anytime an input node is modified.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="119"/>
<source>AutoRun on other input events</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="121"/>
<source>As long as the AutoRun button is down, the module is run anytime an input node fires an event other than a modified event.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="128"/>
<source>AutoRun cancels running process</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="130"/>
<source>When checked, on apply, the module cancels/stops the existing running instance if any, otherwise it waits the completion to start a new run.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
128 changes: 128 additions & 0 deletions Base/QTCLI/Resources/Translations/qSlicerBaseQTCLI_zh_tw.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="zh_TW">
<context>
<name>QObject</name>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="124"/>
<source>Show details</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="148"/>
<source>Status:</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIProgressBar.cxx" line="149"/>
<source>Idle</source>
<translation type="unfinished"></translation>
</message>
</context>
<context>
<name>qSlicerCLIModuleWidget</name>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="14"/>
<source>Module</source>
<translation>模組</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="47"/>
<source>Module Title</source>
<translation>模組標題</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="59"/>
<source>vtkMRMLCommandLineModuleNode</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="70"/>
<source>Parameter set:</source>
<translation>參數</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="104"/>
<source>Reset parameters to default.</source>
<translation>重設參數回預設值</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="107"/>
<source>Restore Defaults</source>
<translation>恢復預設值</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="117"/>
<source>AutoRun</source>
<translation>自動執行</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="143"/>
<source>Cancel the execution of the module</source>
<translation>取消模組的執行</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="146"/>
<source>Cancel</source>
<translation>取消</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="156"/>
<source>Execute the module</source>
<translation>執行模組</translation>
</message>
<message>
<location filename="../UI/qSlicerCLIModuleWidget.ui" line="159"/>
<source>Apply</source>
<translation>套用</translation>
</message>
</context>
<context>
<name>qSlicerCLIModuleWidgetPrivate</name>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="98"/>
<source>AutoRun</source>
<translation>自動執行</translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="101"/>
<source>AutoRun on changed parameter</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="103"/>
<source>As long as the AutoRun button is down, the module is run anytime a parameter value is changed.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="110"/>
<source>AutoRun on modified input</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="112"/>
<source>As long as the AutoRun button is down, the module is run anytime an input node is modified.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="119"/>
<source>AutoRun on other input events</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="121"/>
<source>As long as the AutoRun button is down, the module is run anytime an input node fires an event other than a modified event.</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="128"/>
<source>AutoRun cancels running process</source>
<translation type="unfinished"></translation>
</message>
<message>
<location filename="../../qSlicerCLIModuleWidget.cxx" line="130"/>
<source>When checked, on apply, the module cancels/stops the existing running instance if any, otherwise it waits the completion to start a new run.</source>
<translation type="unfinished"></translation>
</message>
</context>
</TS>
8 changes: 8 additions & 0 deletions Base/QTCLI/qSlicerCLIModuleUIHelper.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
// Qt includes
#include <QDebug>
#include <QCheckBox>
#include <QCoreApplication>
#include <QLineEdit>
#include <QRadioButton>
#include <QSpinBox>
Expand Down Expand Up @@ -667,6 +668,9 @@ QWidget* qSlicerCLIModuleUIHelperPrivate::createImageTagWidget(const ModuleParam
QString imageLabel = QString::fromStdString(moduleParameter.GetLabel());
QString imageName = QString::fromStdString(moduleParameter.GetName());

imageLabel = QCoreApplication::translate(
this->CLIModuleWidget->moduleName().toLatin1(), imageLabel.toLatin1());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@u8621011 It would be nice to add translation for all the other tags.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I will add translation for all the other tags later.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be also better not to use the CLI module name as translation context but indicate that it is a CLI module translation, by using a prefix, such as "CLI.". Something like this:

CLINodeTranslationContextPrefix = "CLI."
...
imageLabel = QCoreApplication::translate((CLINodeTranslationContextPrefix+this->CLIModuleWidget->moduleName()).toLatin1(), imageLabel.toLatin1());

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@lassoan why better not to use the CLI module name as translation context?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just the module name is not specific enough. We need to know that a particular context belongs to a CLI module.

Copy link
Author

@u8621011 u8621011 Mar 1, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@jcfr the CLI Module we translate likes steps below.

  1. modify our version xml file, partial BRAINSFit.xml for example
  <parameters advanced="false">
    <label>Output Settings (At least one output must be specified)</label>
    <transform fileExtensions=".h5,.hdf5,.mat,.txt" type="linear" reference="movingVolume">
      <name>linearTransform</name>
      <longflag>linearTransform</longflag>
      <label>QT_TRANSLATE_NOOP("BRAINSFit","Slicer Linear Transform")</label>
      <description>(optional) Output estimated transform - in case the computed transform is not BSpline. NOTE: You must set at least one output object (transform and/or output volume).</description>
      <channel>output</channel>
    </transform>
    <transform fileExtensions=".h5,.hdf5,.mat,.txt" type="bspline" reference="movingVolume">
      <name>bsplineTransform</name>
      <longflag>bsplineTransform</longflag>
      <label>QT_TRANSLATE_NOOP("BRAINSFit","Slicer BSpline Transform")</label>
      <description>(optional) Output estimated transform - in case the computed transform is BSpline. NOTE: You must set at least one output object (transform and/or output volume).</description>
      <channel>output</channel>
    </transform>
    <image>
      <name>outputVolume</name>
      <longflag>outputVolume</longflag>
      <label>QT_TRANSLATE_NOOP("BRAINSFit","Output Image Volume")</label>
      <description>(optional) Output image: the moving image warped to the fixed image space. NOTE: You must set at least one output object (transform and/or output volume).</description>
      <channel>output</channel>
    </image>
  </parameters>
  1. run qt lupdate over our xml
C:\D\Support\qt-4.8.7-64-vs2013-deb\bin\lupdate BRAINSFit.xml -ts  BRAINSFit_zh_tw.ts
  1. translated the generated ts file. below are how lupdate generated for us.
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE TS>
<TS version="2.0" language="zh_TW">
<context>
    <name>BRAINSFit</name>
    <message>
        <location filename="BRAINSFit.xml" line="52"/>
        <source>Slicer Linear Transform</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="BRAINSFit.xml" line="59"/>
        <source>Slicer BSpline Transform</source>
        <translation type="unfinished"></translation>
    </message>
    <message>
        <location filename="BRAINSFit.xml" line="66"/>
        <source>Output Image Volume</source>
        <translation type="unfinished"></translation>
    </message>
</context>
</TS>

  1. copy translated cli ts context to some cmake generated ts file by hand. (qSlicerBaseQTApp_zh_tw.ts for example)

the step 2 & 4 should possibly be done in cmake but we don't know how to script it.
Any ideas?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks, now you provided the step it will be easy to automate the generation.

For step 2, we could ensure that an intermediate file named BRAINSFit.xml.tr is generated based on the original xml file. This file could then be given as input to lupdate.

Here are two possible approaches:

  • (1) all strings in the same context (e.g BRAINSFit)
  • (2) label/description associated with their corresponding parameter group context (e.g BRAINSFit.Input Images ...
import os
import xml.etree.ElementTree as ET

def tr(context, value):
    return 'QT_TRANSLATE_NOOP("%s", "%s")' % (context, value)

def generate_lupdate_input_v1(xmlFile, candidates=["label"]):
    """Process CLI XML description file and generate an new XML file
    including `QT_TRANSLATE_NOOP()` for each `candidates` xml tags.
    """
    moduleName = os.path.splitext(xmlFile)[0]

    tree = ET.parse(xmlFile)
    root = tree.getroot()

    for candidate in candidates:
        for label in root.iter(candidate):
            label.text = tr(moduleName, label.text)

    tree.write(xmlFile + ".v1.tr")
    
def generate_lupdate_input_v2(xmlFile, candidates=["label"]):
    """Process CLI XML description file and generate an new XML file
    including `QT_TRANSLATE_NOOP()` for each `candidates` xml tags.
    """
    moduleName = os.path.splitext(xmlFile)[0]

    tree = ET.parse(xmlFile)
    root = tree.getroot()
    
    for parameters in root.findall("./parameters"):
        context = moduleName + "." + parameters.find("label").text
        labels = parameters.findall(".//label")
        for label in labels:
            label.text = tr(context, label.text)

    tree.write(xmlFile + ".v2.tr")

generate_lupdate_input_v1("BRAINSFit.xml")
generate_lupdate_input_v2("BRAINSFit.xml")

... and corresponding screenshot for the linguist application:

Without parameter group context

linguist_v1

With parameter group context

linguist_v2

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think approach (2) with context will make translation work easier. What do you think ?

After we agree on an approach, it will be easy to integrate it with the CLI build system.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I feel if the CLI cmake script can parse and compile ts into qm for every cli module directory, approach should be ok.(eg, each CLI module has its own ts/qm file)

Approach 2 looks too long the name but if we have to put every cli module sentences into same/single ts file, approach 2 is necessary.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need to be careful with enumerated strings. While the values should be translated in the GUI, the original text should be sent on the command-line.

For example:

https://github.com/Slicer/Slicer/blob/aa07f6b274778f4cbab626b35228bea8768d38ce/Modules/CLI/ThresholdScalarVolume/ThresholdScalarVolume.xml#L31-L40

User will see "outside", "below", "above" translated on GUI, but when the selection is made, the CLI module must receive the original "outside", "below", "above" values.

This goes the other direction, too. Returning strings is rare, but eventually, any strings returned by the CLI should be possible to translate.


// If "type" is specified, only display nodes of type nodeType
// (e.g. vtkMRMLScalarVolumeNode), don't display subclasses
// (e.g. vtkMRMLDiffusionTensorVolumeNode)
Expand Down Expand Up @@ -1079,6 +1083,10 @@ QWidget* qSlicerCLIModuleUIHelper::createTagWidget(const ModuleParameter& module
if (widget)
{
QString description = QString::fromStdString(moduleParameter.GetDescription());

description = QCoreApplication::translate(
d->CLIModuleWidget->moduleName().toLatin1(), description.toLatin1());

widget->setToolTip(description);
QString widgetName = QString::fromStdString(moduleParameter.GetName());
widget->setObjectName(widgetName);
Expand Down
4 changes: 4 additions & 0 deletions Base/QTCLI/qSlicerCLIModuleWidget.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,7 @@ void qSlicerCLIModuleWidgetPrivate::addParameter(QFormLayout* _layout,
const ModuleParameter& moduleParameter)
{
Q_ASSERT(_layout);
Q_Q(qSlicerCLIModuleWidget);

if (moduleParameter.GetHidden() == "true")
{
Expand All @@ -298,6 +299,9 @@ void qSlicerCLIModuleWidgetPrivate::addParameter(QFormLayout* _layout,
QString _label = QString::fromStdString(moduleParameter.GetLabel());
QString description = QString::fromStdString(moduleParameter.GetDescription());

_label = QCoreApplication::translate(q->moduleName().toLatin1(), _label.toLatin1());
description = QCoreApplication::translate(q->moduleName().toLatin1(), description.toLatin1());

// TODO Parameters with flags can support the None node because they are optional
//int noneEnabled = 0;
//if (moduleParameter.GetLongFlag() != "" || moduleParameter.GetFlag() != "")
Expand Down
Loading