Add support for external translation tools (#968)
* Make some minor translation fixes * Remove crowdin file and add import function for crowdin zip file * Rename fallback project lang file to en_GB * Update all i18n files from crowdin exports * Fix tests * Update docs
This commit is contained in:
committed by
GitHub
parent
537bf921d2
commit
25038e6cbb
+1
-2
@@ -9,8 +9,7 @@ The following contributions are welcome:
|
||||
|
||||
* Bugfixes for new or existing bugs. Please also report new bugs in the issue tracker even if you
|
||||
also provide a fix. It makes it easier to keep track of what has been fixed and when.
|
||||
* New translations. You can read more about contributing translations
|
||||
[here](https://github.com/vkbo/novelWriter/blob/main/i18n/README.md).
|
||||
* Translations via the [crowdin project page](https://crowdin.com/project/novelwriter).
|
||||
* Improvements to the documentation. Particularly if the documentation is unclear. Please don't
|
||||
make any larger changes to the documentation without discussing if with the maintainer first.
|
||||
* Adaptations, installation or packaging features targeting specific operating systems.
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
[](https://github.com/vkbo/novelWriter/releases)
|
||||
[](https://pypi.org/project/novelWriter)
|
||||
[](https://pypi.org/project/novelWriter)
|
||||
[](https://crowdin.com/project/novelwriter)
|
||||
|
||||
<img align="left" style="margin: 0 16px 4px 0;" src="https://raw.githubusercontent.com/vkbo/novelWriter/main/setup/novelwriter.png">
|
||||
|
||||
@@ -67,8 +68,11 @@ also be appreciated, but please make an issue or a discussion topic first. Befor
|
||||
code, please also read the full
|
||||
[Contributing Guide](https://github.com/vkbo/novelWriter/blob/main/CONTRIBUTING.md).
|
||||
|
||||
New translations are always welcome. Please read the additional
|
||||
[instructions](https://github.com/vkbo/novelWriter/blob/main/i18n/README.md) for further details.
|
||||
### Translations
|
||||
|
||||
New translations are always welcome. This project uses crowdin to maintain translations, and you
|
||||
can contribute translations at the [crowdin project page](https://crowdin.com/project/novelwriter).
|
||||
See also the [Translations of novelWriter](https://github.com/vkbo/novelWriter/issues/93).
|
||||
|
||||
## Key Features
|
||||
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
files:
|
||||
- source: /i18n/nw_base.ts
|
||||
translation: /i18n/nw_%locale_with_underscore%.ts
|
||||
- source: /novelwriter/assets/i18n/project_en.json
|
||||
translation: /novelwriter/assets/i18n/project_%locale_with_underscore%.json
|
||||
-116
@@ -1,116 +0,0 @@
|
||||
# novelWriter Internationalisation (i18n)
|
||||
|
||||
Here you will find instructions for translating novelWriter to a new language or updating the
|
||||
translations for an already existing supported language.
|
||||
|
||||
There are two areas relevant to localisation:
|
||||
|
||||
* The Qt5 GUI translation files, which consists of `nw_XX.ts` files. This is the bulk of the
|
||||
translation work.
|
||||
* The `project_XX.json` files. See [Project Localisation](#project-localisation) below.
|
||||
|
||||
|
||||
**Important:**
|
||||
|
||||
When making a new translation, or updating an existing one, only commit the `nw_XX.ts` (and
|
||||
`project_XX.json`) files you have made changes to. The `qtlupdate` command mentioned below may
|
||||
modify all `nw_XX.ts` slightly, but please _don't_ commit those changes to the pull request.
|
||||
|
||||
## Qt5 GUI Localisation
|
||||
|
||||
You will need the translation tool Qt 5 Linguist on your system.
|
||||
|
||||
For Ubuntu/Debian, run:
|
||||
```bash
|
||||
sudo apt install qttools5-dev-tools
|
||||
```
|
||||
|
||||
See also [Qt Linguist Manual: Translators](https://doc.qt.io/qt-5/linguist-translators.html).
|
||||
|
||||
### Workflow
|
||||
|
||||
Here's an overview of the localisation workflow:
|
||||
|
||||
1. First, create a new branch off of `main`, and there, generate a new `nw_XX.ts` translation file.
|
||||
1. Load the translation file in Qt Linguist. Translate and save your changes.
|
||||
1. Generate the `nw_XX.qm` file.
|
||||
1. Run the application and verify your changes. Iterate from step 2.
|
||||
1. Once finished, commit your changes and submit your pull request.
|
||||
|
||||
When you want to translate new changes, again create a new branch off of `main` and re-generate the
|
||||
translation file. Continue from step 2.
|
||||
|
||||
### Generate an Updated Translation File
|
||||
|
||||
The `i18n` folder at the root of the repository contains the `nw_XX.ts` translation files.
|
||||
|
||||
Whether to add a new language to the translation framework, or to update the file against the
|
||||
current source code, you must first run the `qtlupdate` command:
|
||||
```bash
|
||||
python3 setup.py qtlupdate i18n/nw_XX.ts
|
||||
```
|
||||
|
||||
The file name must be in the correct format. All translation files must be located in the `i18n`
|
||||
folder, start with `nw_` and end with `.ts`. In between goes the language code. It must be a valid
|
||||
ISO language code, otherwise novelWriter will not accept the file.
|
||||
|
||||
For instance, the French translation uses the language code `fr`, so its translation file will be
|
||||
`nw_fr.ts`
|
||||
|
||||
**Note:** If you run the `qtlupdate` command without a file name, it will update translation files
|
||||
for all languages.
|
||||
|
||||
### Edit the Translation File in Qt Linguist
|
||||
|
||||
The `i18n/nw_XX.ts` file can be edited with the Qt Linguist application provided by Qt 5. This is
|
||||
by far easier than manually editing the `.ts` file.
|
||||
|
||||
Please select "English" and "United Kingdom" as the _source_ language if prompted by Qt Linguist.
|
||||
|
||||
### Verify Your Translation
|
||||
|
||||
The application does not use `.ts` files directly. The `novelwriter/assets/i18n/nw_XX.qm` files are
|
||||
the actual files used to translate the GUI into another language other than the default British
|
||||
English. These files are not generated by default, but they can be built with:
|
||||
```bash
|
||||
python3 setup.py qtlrelease
|
||||
```
|
||||
|
||||
You can now test the translation in novelWriter. The Preferences dialog should list the newly added
|
||||
language, so go ahead and select it.
|
||||
|
||||
**Note:** Please do not submit the `.qm` files to the repository. Only the `.ts` file you just
|
||||
added in the `i18n` folder is needed.
|
||||
|
||||
### Missing QtBase Translations
|
||||
|
||||
The default Qt dialogs also have translations, for instance for standard buttons like "Yes", "No",
|
||||
"Ok", "Cancel", etc. Generally, these translation files are installed with the Qt libraries on your
|
||||
system, and novelWriter will collect those translations from there. However, these translations are
|
||||
missing for many languages.
|
||||
|
||||
As a starting point, there is no need to translate any entries in the `.ts` files that are under
|
||||
elements starting with the letter "Q", like "QPlatformTheme", "QWizard", etc. If these turn up in
|
||||
English in novelWriter after activating a translation, it means they are probably missing in the Qt
|
||||
library, and you may also need to translate these.
|
||||
|
||||
These additional translation entries are generated from a file named `i18n/qtbase.py`, which is not
|
||||
a file that novelWriter uses. It is there only to generate these additional entries for the `.ts`
|
||||
files.
|
||||
|
||||
## Project Localisation
|
||||
|
||||
Projects can have a different language setting than the GUI itself. The files with format
|
||||
`project_XX.json` in `novelwriter/assets/i18n` are simple translation maps for text that goes into
|
||||
the exported documents generated by the Build Novel Project tool.
|
||||
|
||||
The files are loaded based on the language setting in the Build Novel Project tool. At the present
|
||||
time, no other parts of the application use these files. Since these are used as replacement
|
||||
lookups, they are maintained as plain JSON files. The main usage is to generate chapter headers as
|
||||
number words.
|
||||
|
||||
Adding new translations for these files is easy. Just copy the `project_en.json` file, rename it to
|
||||
the appropriate language coded filename, and open it in a text editor. Please use the underscore as
|
||||
separator if a language needs to be localised to a specific country under a language. The importer
|
||||
will for instance look for `en_US` first, then use `en` if it is not found. If there are no
|
||||
matching files, the project falls back to using English.
|
||||
+32
-40
@@ -801,7 +801,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../novelwriter/tools/build.py" line="1354" />
|
||||
<source><b>Build Time:</b> {0}</source>
|
||||
<source>Build Time:</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
@@ -984,7 +984,7 @@
|
||||
</message>
|
||||
<message>
|
||||
<location filename="../novelwriter/gui/doceditor.py" line="480" />
|
||||
<source>This document has been changed outside of novelWriter while it was open. Overvrite the file on disk?</source>
|
||||
<source>This document has been changed outside of novelWriter while it was open. Overwrite the file on disk?</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
@@ -5029,103 +5029,95 @@
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QGuiApplication</name>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="32" />
|
||||
<source>Translate this string to the string 'LTR' in left-to-right languages or to 'RTL' in right-to-left languages (such as Hebrew and Arabic) to get proper widget layout.</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
</context>
|
||||
<context>
|
||||
<name>QPlatformTheme</name>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="40" />
|
||||
<location filename="qtbase.py" line="32" />
|
||||
<source>OK</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="41" />
|
||||
<location filename="qtbase.py" line="33" />
|
||||
<source>Save</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="42" />
|
||||
<location filename="qtbase.py" line="34" />
|
||||
<source>Save All</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="43" />
|
||||
<location filename="qtbase.py" line="35" />
|
||||
<source>Open</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="44" />
|
||||
<location filename="qtbase.py" line="36" />
|
||||
<source>&Yes</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="45" />
|
||||
<location filename="qtbase.py" line="37" />
|
||||
<source>Yes to &All</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="46" />
|
||||
<location filename="qtbase.py" line="38" />
|
||||
<source>&No</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="47" />
|
||||
<location filename="qtbase.py" line="39" />
|
||||
<source>N&o to All</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="48" />
|
||||
<location filename="qtbase.py" line="40" />
|
||||
<source>Abort</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="49" />
|
||||
<location filename="qtbase.py" line="41" />
|
||||
<source>Retry</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="50" />
|
||||
<location filename="qtbase.py" line="42" />
|
||||
<source>Ignore</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="51" />
|
||||
<location filename="qtbase.py" line="43" />
|
||||
<source>Close</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="52" />
|
||||
<location filename="qtbase.py" line="44" />
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="53" />
|
||||
<location filename="qtbase.py" line="45" />
|
||||
<source>Discard</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="54" />
|
||||
<location filename="qtbase.py" line="46" />
|
||||
<source>Help</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="55" />
|
||||
<location filename="qtbase.py" line="47" />
|
||||
<source>Apply</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="56" />
|
||||
<location filename="qtbase.py" line="48" />
|
||||
<source>Reset</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="57" />
|
||||
<location filename="qtbase.py" line="49" />
|
||||
<source>Restore Defaults</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
@@ -5133,58 +5125,58 @@
|
||||
<context>
|
||||
<name>QWizard</name>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="62" />
|
||||
<location filename="qtbase.py" line="54" />
|
||||
<source>Go Back</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="63" />
|
||||
<location filename="qtbase.py" line="55" />
|
||||
<source>< &Back</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="64" />
|
||||
<location filename="qtbase.py" line="56" />
|
||||
<source>Continue</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="65" />
|
||||
<location filename="qtbase.py" line="57" />
|
||||
<source>&Next</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="66" />
|
||||
<location filename="qtbase.py" line="58" />
|
||||
<source>&Next ></source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="67" />
|
||||
<location filename="qtbase.py" line="59" />
|
||||
<source>Commit</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="68" />
|
||||
<location filename="qtbase.py" line="60" />
|
||||
<source>Done</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="69" />
|
||||
<location filename="qtbase.py" line="61" />
|
||||
<source>&Finish</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="71" />
|
||||
<location filename="qtbase.py" line="70" />
|
||||
<location filename="qtbase.py" line="63" />
|
||||
<location filename="qtbase.py" line="62" />
|
||||
<source>Cancel</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="72" />
|
||||
<location filename="qtbase.py" line="64" />
|
||||
<source>Help</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
<message>
|
||||
<location filename="qtbase.py" line="73" />
|
||||
<location filename="qtbase.py" line="65" />
|
||||
<source>&Help</source>
|
||||
<translation type="unfinished" />
|
||||
</message>
|
||||
|
||||
+3156
-3164
File diff suppressed because it is too large
Load Diff
+3156
-3190
File diff suppressed because it is too large
Load Diff
-5210
File diff suppressed because it is too large
Load Diff
+5202
File diff suppressed because it is too large
Load Diff
+3156
-3164
File diff suppressed because it is too large
Load Diff
-5210
File diff suppressed because it is too large
Load Diff
+5202
File diff suppressed because it is too large
Load Diff
+3157
-3165
File diff suppressed because it is too large
Load Diff
@@ -26,14 +26,6 @@ QT_TRANSLATE_NOOP("QGnomeTheme", "&Cancel")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "&Close")
|
||||
QT_TRANSLATE_NOOP("QGnomeTheme", "Close without Saving")
|
||||
|
||||
# QGuiApplication
|
||||
# ===============
|
||||
|
||||
QT_TRANSLATE_NOOP("QGuiApplication", (
|
||||
"Translate this string to the string 'LTR' in left-to-right languages or to "
|
||||
"'RTL' in right-to-left languages (such as Hebrew and Arabic) to get proper widget layout."
|
||||
))
|
||||
|
||||
# QPlatformTheme
|
||||
# ==============
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
{
|
||||
"Synopsis": "Synopsis",
|
||||
"Comment": "Comment",
|
||||
"Notes": "Notes",
|
||||
"0": "Zero",
|
||||
"1": "One",
|
||||
"2": "Two",
|
||||
"3": "Three",
|
||||
"4": "Four",
|
||||
"5": "Five",
|
||||
"6": "Six",
|
||||
"7": "Seven",
|
||||
"8": "Eight",
|
||||
"9": "Nine",
|
||||
"10": "Ten",
|
||||
"11": "Eleven",
|
||||
"12": "Twelve",
|
||||
"13": "Thirteen",
|
||||
"14": "Fourteen",
|
||||
"15": "Fifteen",
|
||||
"16": "Sixteen",
|
||||
"17": "Seventeen",
|
||||
"18": "Eighteen",
|
||||
"19": "Nineteen",
|
||||
"20": "Twenty",
|
||||
"21": "Twenty-One",
|
||||
"22": "Twenty-Two",
|
||||
"23": "Twenty-Three",
|
||||
"24": "Twenty-Four",
|
||||
"25": "Twenty-Five",
|
||||
"26": "Twenty-Six",
|
||||
"27": "Twenty-Seven",
|
||||
"28": "Twenty-Eight",
|
||||
"29": "Twenty-Nine",
|
||||
"30": "Thirty",
|
||||
"31": "Thirty-One",
|
||||
"32": "Thirty-Two",
|
||||
"33": "Thirty-Three",
|
||||
"34": "Thirty-Four",
|
||||
"35": "Thirty-Five",
|
||||
"36": "Thirty-Six",
|
||||
"37": "Thirty-Seven",
|
||||
"38": "Thirty-Eight",
|
||||
"39": "Thirty-Nine",
|
||||
"40": "Forty",
|
||||
"41": "Forty-One",
|
||||
"42": "Forty-Two",
|
||||
"43": "Forty-Three",
|
||||
"44": "Forty-Four",
|
||||
"45": "Forty-Five",
|
||||
"46": "Forty-Six",
|
||||
"47": "Forty-Seven",
|
||||
"48": "Forty-Eight",
|
||||
"49": "Forty-Nine",
|
||||
"50": "Fifty",
|
||||
"51": "Fifty-One",
|
||||
"52": "Fifty-Two",
|
||||
"53": "Fifty-Three",
|
||||
"54": "Fifty-Four",
|
||||
"55": "Fifty-Five",
|
||||
"56": "Fifty-Six",
|
||||
"57": "Fifty-Seven",
|
||||
"58": "Fifty-Eight",
|
||||
"59": "Fifty-Nine",
|
||||
"60": "Sixty",
|
||||
"61": "Sixty-One",
|
||||
"62": "Sixty-Two",
|
||||
"63": "Sixty-Three",
|
||||
"64": "Sixty-Four",
|
||||
"65": "Sixty-Five",
|
||||
"66": "Sixty-Six",
|
||||
"67": "Sixty-Seven",
|
||||
"68": "Sixty-Eight",
|
||||
"69": "Sixty-Nine",
|
||||
"70": "Seventy",
|
||||
"71": "Seventy-One",
|
||||
"72": "Seventy-Two",
|
||||
"73": "Seventy-Three",
|
||||
"74": "Seventy-Four",
|
||||
"75": "Seventy-Five",
|
||||
"76": "Seventy-Six",
|
||||
"77": "Seventy-Seven",
|
||||
"78": "Seventy-Eight",
|
||||
"79": "Seventy-Nine",
|
||||
"80": "Eighty",
|
||||
"81": "Eighty-One",
|
||||
"82": "Eighty-Two",
|
||||
"83": "Eighty-Three",
|
||||
"84": "Eighty-Four",
|
||||
"85": "Eighty-Five",
|
||||
"86": "Eighty-Six",
|
||||
"87": "Eighty-Seven",
|
||||
"88": "Eighty-Eight",
|
||||
"89": "Eighty-Nine",
|
||||
"90": "Ninety",
|
||||
"91": "Ninety-One",
|
||||
"92": "Ninety-Two",
|
||||
"93": "Ninety-Three",
|
||||
"94": "Ninety-Four",
|
||||
"95": "Ninety-Five",
|
||||
"96": "Ninety-Six",
|
||||
"97": "Ninety-Seven",
|
||||
"98": "Ninety-Eight",
|
||||
"99": "Ninety-Nine"
|
||||
}
|
||||
@@ -32,7 +32,7 @@
|
||||
"27": "二十七",
|
||||
"28": "二十八",
|
||||
"29": "二十九",
|
||||
"30": "三十",
|
||||
"30": "三十",
|
||||
"31": "三十一",
|
||||
"32": "三十二",
|
||||
"33": "三十三",
|
||||
|
||||
@@ -396,7 +396,7 @@ class Config:
|
||||
elif lngSet == self.LANG_PROJ:
|
||||
fPre = "project_"
|
||||
fExt = ".json"
|
||||
langList = {"en": "English"}
|
||||
langList = {"en_GB": QLocale("en_GB").nativeLanguageName().title()}
|
||||
else:
|
||||
return []
|
||||
|
||||
@@ -407,7 +407,7 @@ class Config:
|
||||
continue
|
||||
qmLang = qmFile[len(fPre):-len(fExt)]
|
||||
qmName = QLocale(qmLang).nativeLanguageName().title()
|
||||
if qmLang and qmName and qmLang != "en":
|
||||
if qmLang and qmName and qmLang != "en_GB":
|
||||
langList[qmLang] = qmName
|
||||
|
||||
return sorted(langList.items(), key=lambda x: x[0])
|
||||
|
||||
@@ -1242,20 +1242,14 @@ class NWProject():
|
||||
self.langData = {}
|
||||
return False
|
||||
|
||||
lngShort = self.projLang.split("_")[0]
|
||||
loadFile = os.path.join(self.mainConf.nwLangPath, "project_en.json")
|
||||
chkFile1 = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % self.projLang)
|
||||
chkFile2 = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % lngShort)
|
||||
|
||||
if os.path.isfile(chkFile1):
|
||||
loadFile = chkFile1
|
||||
elif os.path.isfile(chkFile2):
|
||||
loadFile = chkFile2
|
||||
langFile = os.path.join(self.mainConf.nwLangPath, "project_%s.json" % self.projLang)
|
||||
if not os.path.isfile(langFile):
|
||||
langFile = os.path.join(self.mainConf.nwLangPath, "project_en_GB.json")
|
||||
|
||||
try:
|
||||
with open(loadFile, mode="r", encoding="utf-8") as inFile:
|
||||
with open(langFile, mode="r", encoding="utf-8") as inFile:
|
||||
self.langData = json.load(inFile)
|
||||
logger.debug("Loaded project language file: %s", os.path.basename(loadFile))
|
||||
logger.debug("Loaded project language file: %s", os.path.basename(langFile))
|
||||
|
||||
except Exception:
|
||||
logger.error("Failed to project language file")
|
||||
|
||||
@@ -479,7 +479,7 @@ class GuiDocEditor(QTextEdit):
|
||||
self.tr("File Changed on Disk"),
|
||||
self.tr(
|
||||
"This document has been changed outside of novelWriter "
|
||||
"while it was open. Overvrite the file on disk?"
|
||||
"while it was open. Overwrite the file on disk?"
|
||||
)
|
||||
)
|
||||
if msgYes:
|
||||
|
||||
@@ -1351,7 +1351,7 @@ class GuiBuildNovelDocView(QTextBrowser):
|
||||
else:
|
||||
strBuildTime = self.tr("Unknown")
|
||||
|
||||
self.theTitle.setText(self.tr("<b>Build Time:</b> {0}").format(strBuildTime))
|
||||
self.theTitle.setText("%s %s" % (self.tr("Build Time:"), strBuildTime))
|
||||
|
||||
return
|
||||
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version='1.0' encoding='utf-8'?>
|
||||
<novelWriterXML appVersion="1.6-beta1" hexVersion="0x010600b1" fileVersion="1.3" timeStamp="2022-01-18 09:44:14">
|
||||
<novelWriterXML appVersion="1.6-beta1" hexVersion="0x010600b1" fileVersion="1.3" timeStamp="2022-01-18 18:48:36">
|
||||
<project>
|
||||
<name>Sample Project</name>
|
||||
<title>Sample Project</title>
|
||||
<author>Jane Smith</author>
|
||||
<author>Jay Doh</author>
|
||||
<saveCount>1276</saveCount>
|
||||
<autoCount>198</autoCount>
|
||||
<editTime>63357</editTime>
|
||||
<saveCount>1283</saveCount>
|
||||
<autoCount>199</autoCount>
|
||||
<editTime>63909</editTime>
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<language>en</language>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>True</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<autoOutline>True</autoOutline>
|
||||
|
||||
@@ -27,6 +27,7 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
import zipfile
|
||||
import datetime
|
||||
import subprocess
|
||||
import email.utils
|
||||
@@ -435,6 +436,47 @@ def buildSampleZip():
|
||||
# Python Packaging
|
||||
# =============================================================================================== #
|
||||
|
||||
##
|
||||
# Import Translations (import-i18n)
|
||||
##
|
||||
|
||||
def importI18nUpdates(sysArgs):
|
||||
"""Import new translation files from a zip file.
|
||||
"""
|
||||
print("")
|
||||
print("Import Updated Translations")
|
||||
print("===========================")
|
||||
print("")
|
||||
|
||||
fileName = None
|
||||
if len(sysArgs) >= 2:
|
||||
if os.path.isfile(sysArgs[1]):
|
||||
fileName = sysArgs[1]
|
||||
|
||||
if fileName is None:
|
||||
print("File not found ...")
|
||||
sys.exit(1)
|
||||
|
||||
projPath = os.path.join("novelwriter", "assets", "i18n")
|
||||
mainPath = "i18n"
|
||||
|
||||
print("Loading file: %s" % fileName)
|
||||
with zipfile.ZipFile(fileName) as zipObj:
|
||||
for archFile in zipObj.namelist():
|
||||
if archFile.startswith("nw_") and archFile.endswith(".ts"):
|
||||
zipObj.extract(archFile, mainPath)
|
||||
print("Extracted: %s > %s" % (archFile, os.path.join(mainPath, archFile)))
|
||||
elif archFile.startswith("project_") and archFile.endswith(".json"):
|
||||
zipObj.extract(archFile, projPath)
|
||||
print("Extracted: %s > %s" % (archFile, os.path.join(projPath, archFile)))
|
||||
else:
|
||||
print("Skipped: %s" % archFile)
|
||||
|
||||
print("")
|
||||
|
||||
return
|
||||
|
||||
|
||||
##
|
||||
# Make Minimal Package (minimal-zip)
|
||||
##
|
||||
@@ -1514,6 +1556,7 @@ if __name__ == "__main__":
|
||||
"",
|
||||
"Python Packaging:",
|
||||
"",
|
||||
" import-i18n Import updated i18n files from a zip file.",
|
||||
" minimal-zip Creates a minimal zip file of the core application without",
|
||||
" all the other source files. Defaults to tailor the zip file",
|
||||
" for the current OS, but accepts a target OS flag to build",
|
||||
@@ -1597,6 +1640,11 @@ if __name__ == "__main__":
|
||||
# Python Packaging
|
||||
# ================
|
||||
|
||||
if "import-i18n" in sys.argv:
|
||||
sys.argv.remove("import-i18n")
|
||||
importI18nUpdates(sys.argv)
|
||||
sys.exit(0) # Don't continue execution
|
||||
|
||||
if "minimal-zip" in sys.argv:
|
||||
sys.argv.remove("minimal-zip")
|
||||
makeMinimalPackage(targetOS)
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>False</doBackup>
|
||||
<language>en</language>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<autoOutline>True</autoOutline>
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
</project>
|
||||
<settings>
|
||||
<doBackup>True</doBackup>
|
||||
<language>en</language>
|
||||
<language>en_GB</language>
|
||||
<spellCheck>False</spellCheck>
|
||||
<spellLang>None</spellLang>
|
||||
<autoOutline>True</autoOutline>
|
||||
|
||||
@@ -243,7 +243,7 @@ def testBaseConfig_Init(monkeypatch, tmpDir, fncDir, outDir, refDir, filesDir):
|
||||
theList = tstConf.listLanguages(tstConf.LANG_NW)
|
||||
assert theList == [("en_GB", "British English")]
|
||||
theList = tstConf.listLanguages(tstConf.LANG_PROJ)
|
||||
assert theList == [("en", "English")]
|
||||
assert theList == [("en_GB", "British English")]
|
||||
theList = tstConf.listLanguages(None)
|
||||
assert theList == []
|
||||
|
||||
|
||||
Reference in New Issue
Block a user