Skip to content

Commit

Permalink
GUI Translation
Browse files Browse the repository at this point in the history
Translation files prepared and German translation completed
  • Loading branch information
Krekeler committed Apr 13, 2019
1 parent ade6c06 commit d6ad7c2
Show file tree
Hide file tree
Showing 21 changed files with 3,785 additions and 102 deletions.
1 change: 1 addition & 0 deletions contrib/dms-qt.pro
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ FORMS += \
../src/qt/forms/coincontroldialog.ui \
../src/qt/forms/darksendconfig.ui \
../src/qt/forms/debugwindow.ui \
../src/qt/forms/documentlist.ui \
../src/qt/forms/editaddressdialog.ui \
../src/qt/forms/helpmessagedialog.ui \
../src/qt/forms/intro.ui \
Expand Down
108 changes: 33 additions & 75 deletions doc/translation_process.md
Original file line number Diff line number Diff line change
@@ -1,87 +1,45 @@
Translations
============

The DMS Core project has been designed to support multiple localisations. This makes adding new phrases, and completely new languages easily achievable. For managing all application translations, DMS Core makes use of the Transifex online translation management tool.
The DMS Core project has been designed to support multiple localisations. This makes adding new phrases, and completely new languages easily achievable.

### Helping to translate (using Transifex)
Transifex is setup to monitor the GitHub repo for updates, and when code containing new translations is found, Transifex will process any changes. It may take several hours after a pull-request has been merged, to appear in the Transifex web interface.
### Translation process
In DMS Core, unlike Bitcoin, we currently do not use a translation platform. The translation is done manually in the .ts files:
1. Add new phrases to the *.ts files in `src/qt/locale`.
1. Give the language files to the appropriate translators.

Multiple language support is critical in assisting DMS’s global adoption, and growth. One of DMS’s greatest strengths is cross-border money transfers, any help making that easier is greatly appreciated.
`src/qt/locale/dms_en.ts` is treated in a special way. It is used as the source for all other translations.

TODO: See the [Transifex Dash project](https://www.transifex.com/projects/p/dash/) to assist in translations. You should also join the translation mailing list for announcements - see details below.

### Writing code with translations
We use automated scripts to help extract translations in both Qt, and non-Qt source files. It is rarely necessary to manually edit the files in `src/qt/locale/`. The translation source files must adhere to the following format:
`dms_xx_YY.ts or dms_xx.ts`

`src/qt/locale/dms_en.ts` is treated in a special way. It is used as the source for all other translations. Whenever a string in the source code is changed, this file must be updated to reflect those changes. A custom script is used to extract strings from the non-Qt parts. This script makes use of `gettext`, so make sure that utility is installed (ie, `apt-get install gettext` on Ubuntu/Debian). Once this has been updated, `lupdate` (included in the Qt SDK) is used to update `dash_en.ts`.

To automatically regenerate the `dms_en.ts` file, run the following commands:
```sh
cd src/
make translate
```

`contrib/dms-qt.pro` takes care of generating `.qm` (binary compiled) files from `.ts` (source files) files. It’s mostly automated, and you shouldn’t need to worry about it.

**Example Qt translation**
```cpp
QToolBar *toolbar = addToolBar(tr("Tabs toolbar"));
```

### Creating a pull-request
For general PRs, you shouldn’t include any updates to the translation source files. They will be updated periodically, primarily around pre-releases, allowing time for any new phrases to be translated before public releases. This is also important in avoiding translation related merge conflicts.

When an updated source file is merged into the GitHub repo, Transifex will automatically detect it (although it can take several hours). Once processed, the new strings will show up as "Remaining" in the Transifex web interface and are ready for translators.

To create the pull-request, use the following commands:
```
git add src/qt/dmsstrings.cpp src/qt/locale/dms_en.ts
git commit
#### Example of the main file "dms_en.ts"
```
...
</context>
<context>
<name>DocumentList</name>
<message>
<location filename="../forms/documentlist.ui"/>
<source>Document Revision</source>
<translation>Document Revision</translation>
</message>
...
</context>
</TS>
```


### Creating a Transifex account
Visit the [Transifex Signup](https://www.transifex.com/signup/) page to create an account. Take note of your username and password, as they will be required to configure the command-line tool.

TODO: You can find the Dash translation project at [https://www.transifex.com/projects/p/dash/](https://www.transifex.com/projects/p/dash/).

### Installing the Transifex client command-line tool
The client it used to fetch updated translations. If you are having problems, or need more details, see [http://docs.transifex.com/developer/client/setup](http://docs.transifex.com/developer/client/setup)

**For Linux and Mac**

`pip install transifex-client`

Setup your transifex client config as follows. Please *ignore the token field*.

```ini
nano ~/.transifexrc

[https://www.transifex.com]
hostname = https://www.transifex.com
password = PASSWORD
token =
username = USERNAME
#### Example of a language file
```

**For Windows**

Please see [http://docs.transifex.com/developer/client/setup#windows](http://docs.transifex.com/developer/client/setup#windows) for details on installation.

The Transifex DMS project config file is included as part of the repo. It can be found at `.tx/config`, however you shouldn’t need change anything.

### Synchronising translations
To assist in updating translations, we have created a script to help.

1. `python contrib/devtools/update-translations.py`
2. Update `src/qt/dms_locale.qrc` manually or via
`ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(dms_\(.*\)\).ts/<file alias="\2">locale\/\1.qm<\/file>/'`
3. Update `src/Makefile.qt.include` manually or via
`ls src/qt/locale/*ts|xargs -n1 basename|sed 's/\(dms_\(.*\)\).ts/ qt\/locale\/\1.ts \\/'`
4. `git add` new translations from `src/qt/locale/`

**Do not directly download translations** one by one from the Transifex website, as we do a few post-processing steps before committing the translations.
...
</context>
<context>
<name>DocumentList</name>
<message>
<source>Document Revision</source>
<translation>Dokumentenrevision</translation>
</message>
...
</context>
</TS>
```

### Handling Plurals (in source files)
When new plurals are added to the source file, it's important to do the following steps:
Expand Down
82 changes: 56 additions & 26 deletions src/qt/documentlist.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,18 @@ namespace {
const QString descFileExt = ".desc";
const double minDocRevFee = 0.1; // DMS

/** frequent terms */
QString trDocument;
QString trFileHash;
QString trGUID;
QString trAttrHash;
QString trBlockchain;
QString trDocRev;
QString trExecute;
QString trTransID;
QString trTotalAmount;
QString trStored;

QString getTextHash(const QString text)
{
QCryptographicHash crypto(QCryptographicHash::Md5);
Expand Down Expand Up @@ -189,7 +201,7 @@ QString Document::documentRevision()

if (confirmations < minConfirms) {
revlog.append(revNoConf);
revlog.append(tr("<p>There are not enough blockchain confirmations available for revision. Please wait a while.</p>"));
revlog.append("<p>" + tr("There are not enough blockchain confirmations available for revision. Please wait a while.") + "</p>");
}
else if (compressGuid(guid) == bcguid && attrhash == bcattrhash && filehash == bcfilehash)
revlog.append(revOk);
Expand All @@ -200,47 +212,53 @@ QString Document::documentRevision()

revlog.append("<p>");
if (compressGuid(guid) == bcguid)
revlog.append("<b>GUID</b>: <span style=\"color:green\">" + accordance + "</span><br>");
revlog.append(QString("<b>%1</b>: <span style=\"color:green\">%2</span><br>").arg(trGUID, accordance));
else
revlog.append("<b>GUID</b>: <span style=\"color:red\">" + deviation.arg(compressGuid(guid), bcguid) + "</span><br>");
revlog.append(QString("<b>%1</b>: <span style=\"color:red\">%2</span><br>")
.arg(trGUID, deviation.arg(compressGuid(guid), bcguid)));
if (attrhash == bcattrhash)
revlog.append("<b>Attribuite hash</b>: <span style=\"color:green\">" + accordance + "</span><br>");
revlog.append(QString("<b>%1</b>: <span style=\"color:green\">%2</span><br>").arg(trAttrHash, accordance));
else
revlog.append("<b>Attribuite hash</b>: <span style=\"color:red\">" + deviation.arg(compressGuid(guid), bcguid) + "</span><br>");
revlog.append(QString("<b>%1</b>: <span style=\"color:red\">%2</span><br>")
.arg(trAttrHash, deviation.arg(compressGuid(guid), bcguid)));
if (filehash == bcfilehash) {
revlog.append("<b>File hash</b>: <span style=\"color:green\">" + accordance + "</span>");
revlog.append(QString("<b>%1</b>: <span style=\"color:green\">%2</span>").arg(trFileHash, accordance));
if (confirmations >= minConfirms)
revlog.append("</p><p>The blockchain confirms that <a href=\"open\">this document file</a> exists at least since " +
GUIUtil::dateTimeStr(blocktime) + " and has not been modified.");
revlog.append("</p><p>" + tr("The blockchain confirms that %1this document file%2 exists at least since %3 and has not been modified.")
.arg("<a href=\"open\">", "</a>", GUIUtil::dateTimeStr(blocktime)));
}
else
revlog.append("<b>File hash</b>: <span style=\"color:red\">" + deviation.arg(compressGuid(guid), bcguid) + "</span>");
revlog.append(QString("<b>%1</b>: <span style=\"color:red\">%2</span>")
.arg(trFileHash, deviation.arg(compressGuid(guid), bcguid)));

revlog.append("</p><p><b>Blockchain confirmations</b>: " + QString::number(confirmations)
revlog.append("</p><p><b>" + tr("Blockchain confirmations")
+ "</b>: " + QString::number(confirmations)
+ ((confirmations < minConfirms) ? ("/" + QString::number(minConfirms)) : "") + "</p>");

return revlog;

} catch (const std::exception& e) {
return revError + "<p>" + QString::fromStdString(e.what()) + "</p>";
} catch (...) {
return revError + "<p>Unknown error.</p>";
return revError + "<p>" + tr("Unknown Error.") + "</p>";
}
}

QString Document::getInformationHtml()
{
return tr("<h1>%1</h1><h2>Document</h2>"
return QString("<h1>%1</h1><h2>%9</h2>"
"<p><a href=\"open\">%2</a> (%3 byte)</p>"
"<p><b>GUID</b><br>%4</p>"
"<p><b>File hash</b><br>%5<br>"
"<b>Attribute hash</b><br>%6</p>"
"<h2>Blockchain</h2>"
"<p><b>Transaction ID</b><br>%7</p>"
"<p><b>Stored</b><br>%8 (local system time)</p>"
"<p><b>%10</b><br>%4</p>"
"<p><b>%11</b><br>%5<br>"
"<b>%12</b><br>%6</p>"
"<h2>%13</h2>"
"<p><b>%14</b><br>%7</p>"
"<p><b>%15</b><br>%8 (local system time)</p>"
).arg(
name, filename, QString::number(filesize), guid, filehash, attrhash, txid,
GUIUtil::dateTimeStr(savetime));
GUIUtil::dateTimeStr(savetime) // %8
).arg(
trDocument, trGUID, trFileHash, trAttrHash, trBlockchain, trTransID, trStored);
}

/** we are using RPC functions to create the transaction
Expand Down Expand Up @@ -274,7 +292,7 @@ QString Document::writeToBlockchain()
QString attrhash = descFile.value("attrhash", "").toString();
descFile.endGroup();
if ( comprguid.length() != 32 || filehash.length() != 32 || attrhash.length() != 32 ) {
QMessageBox::critical(NULL, tr("Desc File"), tr("Invalid document description."));
QMessageBox::critical(NULL, "Desc File", tr("Invalid document description."));
return "";
}

Expand All @@ -299,8 +317,8 @@ QString Document::writeToBlockchain()
}
if (txid.isEmpty()) { // TODO: remove the 55 DMS limit above
QMessageBox::critical(NULL, tr("Input"),
tr("No matching credit (input) found. At least one input with a credit "
"between 0.1 and 55 coins and 6 confirmation required."));
"No matching credit (input) found. At least one input with a credit "
"between 0.1 and 55 coins and 6 confirmation required.");
return "";
}

Expand Down Expand Up @@ -366,7 +384,7 @@ QString Document::writeToBlockchain()
QMessageBox::critical(NULL, tr("RPC Error"), QString::fromStdString(e.what()));
return "";
} catch (...) {
QMessageBox::critical(NULL, tr("RPC Error"), "Unknown error.");
QMessageBox::critical(NULL, tr("RPC Error"), tr("Unknown Error."));
return "";
}
}
Expand All @@ -383,6 +401,17 @@ DocumentList::DocumentList(const PlatformStyle *_platformStyle, QWidget *parent)
{
ui->setupUi(this);

trDocument = tr("Document");
trFileHash = tr("File hash");
trGUID = tr("GUID");
trAttrHash = tr("Attribute hash");
trBlockchain = tr("Blockchain");
trDocRev = tr("Document Revision");
trExecute = tr("Execute");
trTransID = tr("Transaction ID");
trTotalAmount = tr("Total Amount");
trStored = tr("Stored");

documentModel = new QStringListModel(this);
ui->listViewDocuments->setModel(documentModel);
ui->listViewDocuments->setEditTriggers(QAbstractItemView::NoEditTriggers);
Expand Down Expand Up @@ -471,7 +500,7 @@ bool DocumentList::TransactionConfirmDlg(const QString docName, const double txF
questionString.append("</b><hr><span style='color:#aa0000;'>");
questionString.append(BitcoinUnits::formatHtmlWithUnit(walletModel->getOptionsModel()->getDisplayUnit(), txFeeSat));
questionString.append("</span> ");
questionString.append(tr(" is paid as transaction fee."));
questionString.append(" " + tr("is paid as transaction fee."));

// add total amount in all subdivision units
questionString.append("<hr />");
Expand Down Expand Up @@ -603,8 +632,8 @@ void DocumentList::onlistViewDocumentsChanged(const QModelIndex &current, const

Document doc(getFileName(current, true));
ui->textBrowserRevision->setHtml(doc.getInformationHtml());
ui->textBrowserRevision->append("<h2>Document Revision</h2>"
"<p><a href=\"docrev\">Execute</a></p>");
ui->textBrowserRevision->append(QString("<h2>%1</h2><p><a href=\"docrev\">%2</a></p>")
.arg(trDocRev, trExecute));

ui->pushButtonOpenFile->setEnabled(true);
ui->pushButtonRevision->setEnabled(true);
Expand Down Expand Up @@ -632,6 +661,7 @@ void DocumentList::on_pushButtonAddFile_clicked()
QStringList srcFileNames = QFileDialog::getOpenFileNames(this,
tr("Select file(s) to append"),
GUIUtil::getOSDocumentsDir(), tr("All Files (*.*)"));
if (srcFileNames.isEmpty()) return;

QString lastAddedFile = addFiles(srcFileNames);

Expand Down
Loading

0 comments on commit d6ad7c2

Please sign in to comment.