update to localization tools

This commit is contained in:
remi durand 2021-05-02 17:01:29 +02:00
parent 43d0d22532
commit b41ffaa4f3
63 changed files with 13999 additions and 179774 deletions

File diff suppressed because it is too large Load Diff

View File

@ -1,57 +1,118 @@
# How to create / update your own language
## 1) initialisation
Note that Slic3r only supports left-to-right languages (if you want to change that, you're welcome to contribute).
Your language should have [a code](https://www.loc.gov/standards/iso639-2/php/code_list.php) (use the ISO 639-1, second column). Creating a directory and putting a .mo file inside it will add automatically the translation in Slic3r.
You will have to complete a '.po' text file, with a text editor or a specific ide like poedit. When your translation work is finished, you'll have to compile it into a '.mo' file, this one is not readable with a text editor but is readable by the software.
If your language is already here, you remove all current files to start from scratch or rename them to use them as the start point.
If you found an odd translation and want to change it, go to section B)
## A) Make your own translation
Useful tools:
* windows:
* [python](https://www.python.org/)
* [gettext](http://gnuwin32.sourceforge.net/downlinks/gettext.php)
* linux:
* python: make sure you can execute python3, install it if it isn't here.
* gettext: if you can't execute msgfmt, install the package 'gettext'
* macos: maybe like linux?
### 1) initialisation
open the settings.ini
for each file that can contains useful translation, create / edit a "data" line to point to the said file.
for each file that can contain useful translation, create/edit a "data" line to point to the said file.
The 'input' property must be the Slic3r.pot path
The 'output' must be the Slic3r.po
The 'todo' contains the path of the po file to complete.
note that the first data line has the priority over the other ones (the first translation encountered is the one used)
In this exemple, we are going to update the spanish translation.
We are going to use the old slic3++ translation and the prusa one.
In this example, we are going to update the Spanish translation.
We are going to use your old translation file and the current PrusaSlicer one.
To decompile the .mo of Prusaslicer, use the command `msgunfmt PrusaSlicer.mo -o PrusaSlicer.po`.
So the settings.ini contains these lines :
```
data = es/Slic3r++.po
data = es/my_old_po_file.po
data = es/PrusaSlicer_es.po
ui_dir = ../ui_layout
allow_msgctxt = false
ignore_case = false
input = Slic3r.pot
todo = es/todo.po
output = es/Slic3r.po
```
## 2) launch the utility.
Notes:
* thee 'todo' and 'output' files will be erased, so be sure nothing has this name (or write another name)
* ui_dir should be the path to the slic3r/resources/ui_layout directory. If you're in slic3r/resources/localization, this value is good.
* allow_msgctxt is a bool to allow to keep the msgctxt tags. You need a recent version of gettext to use that.
* ignore_case is a bool that will let the tool to ignore the case when comparing msgid if no translation is found.
### 2) launch the utility.
* You need to have python (v3). You can download it if you don't.
* Open a console
* cd into the localization directory,
* execute 'java -jar pomergeur.jar'
* execute 'python pom_merger.py'
* use python3 if python is the python v2 exe
* you can use the full path to python.exe if you just installed it and it isn't in your path. It's installed by default in your appdata on windows.
It will tell you if you made some mistakes about the paths, the number of translations reused and the number to do.
## 3) complete the translation file
Then, you have to open the es/toto.po file and complete all the translation.
* msgid lines are the english string
* msgstr is the translated one, should be empty string ("").
### 3) complete the translation file
Then, you have to open the es/todo.po file and complete all the translations.
* msgid lines are the English string
* msgstr is the translated one, should be an empty string ("").
Important:
* you must write it in one line, use \n to input a line change.
* the %1, %2, ... MUST be put also in the translation, as it's a placeholder for input numbers, if one is missed the software will crash, so be careful. '%%' means '%'.
* the %1, %2, ... MUST be put also in the translation, as it's a placeholder for input numbers, if one is missed the software will crash, so be careful. '%%' is the way to write '%' without making the program crash. The utility should warn you about every translation that has a different number of '%'.
## 4) relaunch the utility
You can copy/save the todo.po in an other file in case of.
### 4) relaunch the utility
You can copy/save the todo.po in another file in case of.
After filling the todo file, change the settings.ini:
```
data = es/todo.po
data = es/Slic3r.po
data = es/Slic3r++.po
data = es/my_old_po_file.po
data = es/PrusaSlicer_es.po
ui_dir = ../ui_layout
allow_msgctxt = false
ignore_case = false
input = Slic3r.pot
todo = es/todo.po
output = es/Slic3r.po
```
This will tell you to use your todo and your newly created/edited Slic3r.po before using the other older file to complete unfound strings. A translation from the third file won't erase the one from the second unless it's empty (less than 3 characters) and the new one isn't. If a translation is replaced, the tool will inform you.
And re-launch the utility.
Repeat (if needed) until you have almost nothing left in your todo.po file (one-letter translation like "." are not copied by the utility)
Repeat (if needed) until you have almost nothing left in your todo.po file (one-letter translation like "." are not copied by the utility)
When you're finished, compile your .po with the command `msgfmt Slic3r.po -o Slic3r.mo`.
Then you can launch Slic3r to test it.
Note that you have to rename it to SuperSlicer.mo if you're using Superslicer and same for PrusaSlicer.
## B) Update an existing .po file
* Open the problematic .po language file in a text editor.
* Search the offending sentence.
* *Modify the translated sentence (at the right of 'msgstr').
* Note that you must keep a '"' at each side of every line, but you can concatenate the multiple lines into one if you prefer.
* You can use '\n' to add a 'newline' into your sentence.
* When finished, compile your .po into a .mo and replace the current .mo
* Open Slic3r and check that your sentence works. If Slic3r crashes, it's probably wrong.
* Submit your change by opening an issue (search before for 'is:issue translation' in the issue search bar, to piggyback an existing one if it exists).

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,155 +0,0 @@
msgid ""
msgstr ""
"Language: en\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
"Project-Id-Version: \n"
"POT-Creation-Date: \n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
"X-Generator: Poedit 2.3\n"
#: src/slic3r/GUI/UpdateDialogs.cpp:71
msgid "Don't notify about new releases any more"
msgstr "Don't notify about new releases anymore"
#: src/libslic3r/PrintConfig.cpp:287
msgid "A boolean expression using the configuration values of an active print profile. If this expression evaluates to true, this profile is considered compatible with the active print profile."
msgstr "A Boolean expression using the configuration values of an active print profile. If this expression evaluates to true, this profile is considered compatible with the active print profile."
#: src/libslic3r/PrintConfig.cpp:272
msgid "A boolean expression using the configuration values of an active printer profile. If this expression evaluates to true, this profile is considered compatible with the active printer profile."
msgstr "A Boolean expression using the configuration values of an active printer profile. If this expression evaluates to true, this profile is considered compatible with the active printer profile."
#: src/libslic3r/PrintConfig.cpp:409
msgid "Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top+bottom solid layers)."
msgstr "Add solid infill near sloping surfaces to guarantee the vertical shell thickness (top + bottom solid layers)."
#: src/slic3r/GUI/ConfigWizard.cpp:791
msgid "Additionally a backup snapshot of the whole configuration is created before an update is applied."
msgstr "Additionally, a backup snapshot of the whole configuration is created before an update is applied."
#: src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp:1268
msgid "Autogeneration will erase all manually edited points."
msgstr "Auto Generation will erase all manually edited points."
#: src/slic3r/GUI/Tab.cpp:1171
msgid "Autospeed (advanced)"
msgstr "Auto Speed (advanced)"
#: src/slic3r/GUI/ConfigSnapshotDialog.cpp:22
msgid "Before roll back"
msgstr "Before rollback"
#: src/slic3r/GUI/ButtonsDescription.cpp:16
msgid "Buttons And Text Colors Description"
msgstr "Buttons and Text Colors Description"
#: src/libslic3r/PrintConfig.cpp:791
msgid "Density of internal infill, expressed in the range 0% - 100%."
msgstr "Density of internal infill, expressed in the range 0 % - 100 %."
#: src/slic3r/GUI/ConfigWizard.cpp:773
#, c-format
msgid "If enabled, %s checks for new application versions online. When a new version becomes available, a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."
msgstr "If enabled, %s checks for new application versions online. When a new version becomes available, a notification is displayed at the next application startup (never during program usage). This is only a notification mechanism, no automatic installation is done."
#: src/slic3r/GUI/ConfigWizard.cpp:783
#, c-format
msgid "If enabled, %s downloads updates of built-in system presets in the background.These updates are downloaded into a separate temporary location.When a new preset version becomes available it is offered at application startup."
msgstr "If enabled, %s downloads updates of built-in system presets in the background. These updates are downloaded into a separate temporary location. When a new preset version becomes available it is offered at application startup."
#: src/slic3r/GUI/Preferences.cpp:66
msgid "If enabled, PrusaSlicer will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanisms, no automatic installation is done."
msgstr "If enabled, PrusaSlicer will check for the new versions of itself online. When a new version becomes available a notification is displayed at the next application startup (never during program usage). This is only a notification mechanism, no automatic installation is done."
#: src/libslic3r/PrintConfig.cpp:1858
msgid "If enabled, the wipe tower will not be printed on layers with no toolchanges. On layers with a toolchange, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print."
msgstr "If enabled, the wipe tower will not be printed on layers with no tool changes. On layers with a tool change, extruder will travel downward to print the wipe tower. User is responsible for ensuring there is no collision with the print."
#: src/libslic3r/PrintConfig.cpp:2262
msgid "Object will be used to purge the nozzle after a toolchange to save material that would otherwise end up in the wipe tower and decrease print time. Colours of the objects will be mixed as a result."
msgstr "Object will be used to purge the nozzle after a tool change to save material that would otherwise end up in the wipe tower and decrease print time. Colours of the objects will be mixed as a result."
#: src/libslic3r/Print.cpp:1365
msgid "One or more object were assigned an extruder that the printer does not have."
msgstr "One or more objects were assigned an extruder that the printer does not have."
#: src/libslic3r/PrintConfig.cpp:2254
msgid "Purging after toolchange will done inside this object's infills. This lowers the amount of waste but may result in longer print time due to additional travel moves."
msgstr "Purging after tool change will done inside this object's infills. This lowers the amount of waste but may result in longer print time due to additional travel moves."
#: src/libslic3r/PrintConfig.cpp:1552
msgid "Retraction Length (Toolchange)"
msgstr "Retraction Length (Tool change)"
#: src/libslic3r/PrintConfig.cpp:556
msgid "Set this to a non-zero value to allow a manual extrusion width. If left to zero, Slic3r derives extrusion widths from the nozzle diameter (see the tooltips for perimeter extrusion width, infill extrusion width etc). If expressed as percentage (for example: 230%), it will be computed over layer height."
msgstr "Set this to a non-zero value to allow a manual extrusion width. If left to zero, Slic3r derives extrusion widths from the nozzle diameter (see the tooltips for perimeter extrusion width, infill extrusion width etc.). If expressed as percentage (for example: 230%), it will be computed over layer height."
#: src/libslic3r/PrintConfig.cpp:2824
msgid "Some objects can get along with a few smaller pads instead of a single big one. This parameter defines how far the center of two smaller pads should be. If theyare closer, they will get merged into one pad."
msgstr "Some objects can get along with a few smaller pads instead of a single big one. This parameter defines how far the center of two smaller pads should be. If they are closer, they will get merged into one pad."
#: src/libslic3r/PrintConfig.cpp:624
msgid "Speed used for unloading the filament on the wipe tower (does not affect initial part of unloading just after ramming)."
msgstr "Speed used for unloading the filament on the wipe tower (does not affect initial part of unloading just after ramming)."
#: src/libslic3r/PrintConfig.cpp:2044
msgid "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represent the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended)."
msgstr "Support material will not be generated for overhangs whose slope angle (90° = vertical) is above the given threshold. In other words, this value represents the most horizontal slope (measured from the horizontal plane) that you can print without support material. Set to zero for automatic detection (recommended)."
#: src/slic3r/GUI/DoubleSlider.cpp:998
msgid ""
"The sequential print is on.\n"
"It's impossible to apply any custom G-code for objects printing sequentually.\n"
"This code won't be processed during G-code generation."
msgstr ""
"The sequential print is on.\n"
"It's impossible to apply any custom G-code for objects printing sequentially.\n"
"This code won't be processed during G-code generation."
#: src/libslic3r/PrintConfig.cpp:2094
msgid "This custom code is inserted before every toolchange. Placeholder variables for all PrusaSlicer settings as well as {previous_extruder} and {next_extruder} can be used. When a tool-changing command which changes to the correct extruder is included (such as T{next_extruder}), PrusaSlicer will emit no other such command. It is therefore possible to script custom behaviour both before and after the toolchange."
msgstr "This custom code is inserted before every tool change. Placeholder variables for all PrusaSlicer settings as well as {previous_extruder} and {next_extruder} can be used. When a tool-changing command which changes to the correct extruder is included (such as T{next_extruder}), PrusaSlicer will emit no other such command. It is therefore possible to script custom behaviour both before and after the tool change."
#: src/libslic3r/PrintConfig.cpp:396
msgid "This end procedure is inserted at the end of the output file, before the printer end gcode (and before any toolchange from this filament in case of multimaterial printers). Note that you can use placeholder variables for all PrusaSlicer settings. If you have multiple extruders, the gcode is processed in extruder order."
msgstr "This end procedure is inserted at the end of the output file, before the printer end gcode (and before any tool change from this filament in case of multimaterial printers). Note that you can use placeholder variables for all PrusaSlicer settings. If you have multiple extruders, the gcode is processed in extruder order."
#: src/libslic3r/PrintConfig.cpp:2215
msgid "This matrix describes volumes (in cubic milimetres) required to purge the new filament on the wipe tower for any given pair of tools."
msgstr "This matrix describes volumes (in cubic millimetres) required to purge the new filament on the wipe tower for any given pair of tools."
#: src/libslic3r/PrintConfig.cpp:1829
msgid "This start procedure is inserted at the beginning, after any printer start gcode (and after any toolchange to this filament in case of multi-material printers). This is used to override settings for a specific filament. If PrusaSlicer detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all PrusaSlicer settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want. If you have multiple extruders, the gcode is processed in extruder order."
msgstr "This start procedure is inserted at the beginning, after any printer start gcode (and after any tool change to this filament in case of multi-material printers). This is used to override settings for a specific filament. If PrusaSlicer detects M104, M109, M140 or M190 in your custom codes, such commands will not be prepended automatically so you're free to customize the order of heating commands and other custom actions. Note that you can use placeholder variables for all PrusaSlicer settings, so you can put a \"M109 S[first_layer_temperature]\" command wherever you want. If you have multiple extruders, the gcode is processed in extruder order."
#: src/libslic3r/PrintConfig.cpp:641
msgid "Time to wait after the filament is unloaded. May help to get reliable toolchanges with flexible materials that may need more time to shrink to original dimensions."
msgstr "Time to wait after the filament is unloaded. May help to get reliable tool changes with flexible materials that may need more time to shrink to original dimensions."
#: src/slic3r/GUI/Tab.cpp:1491
msgid "Toolchange parameters with single extruder MM printers"
msgstr "Toolchange parameters with single extruder MM printers"
#: src/slic3r/Utils/Duet.cpp:82 src/slic3r/Utils/Duet.cpp:137
#: src/slic3r/Utils/FlashAir.cpp:119 src/slic3r/Utils/FlashAir.cpp:140
#: src/slic3r/Utils/FlashAir.cpp:156
msgid "Unknown error occured"
msgstr "Unknown error occurred"
#: src/libslic3r/PrintConfig.cpp:253
msgid "When printing multi-material objects, this settings will make Slic3r to clip the overlapping object parts one by the other (2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc)."
msgstr "When printing multi-material objects, this settings will make Slic3r to clip the overlapping object parts one by the other (2nd part will be clipped by the 1st, 3rd part will be clipped by the 1st and 2nd etc.)."
#: src/libslic3r/PrintConfig.cpp:1391
msgid "When set to zero, the distance the filament is moved from parking position during load is exactly the same as it was moved back during unload. When positive, it is loaded further, if negative, the loading move is shorter than unloading."
msgstr "When set to zero, the distance the filament is moved from parking position during load is exactly the same as it was moved back during unload. When positive, it is loaded further, if negative, the loading move is shorter than unloading."
#: src/slic3r/GUI/Tab.cpp:3285
msgid "WHITE BULLET icon indicates a non system (or non default) preset."
msgstr "WHITE BULLET icon indicates a non-system (or non-default) preset."

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,91 +0,0 @@
src/slic3r/GUI/AboutDialog.cpp
src/slic3r/GUI/BackgroundSlicingProcess.cpp
src/slic3r/GUI/BedShapeDialog.cpp
src/slic3r/GUI/BedShapeDialog.hpp
src/slic3r/GUI/BonjourDialog.cpp
src/slic3r/GUI/ButtonsDescription.cpp
src/slic3r/GUI/ConfigManipulation.cpp
src/slic3r/GUI/ConfigSnapshotDialog.cpp
src/slic3r/GUI/ConfigWizard.cpp
src/slic3r/GUI/DoubleSlider.cpp
src/slic3r/GUI/ExtraRenderers.cpp
src/slic3r/GUI/ExtruderSequenceDialog.cpp
src/slic3r/GUI/Field.cpp
src/slic3r/GUI/FirmwareDialog.cpp
src/slic3r/GUI/GCodeViewer.cpp
src/slic3r/GUI/GLCanvas3D.cpp
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
src/slic3r/GUI/Gizmos/GLGizmoHollow.cpp
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
src/slic3r/GUI/Gizmos/GLGizmoSlaSupports.cpp
src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
src/slic3r/GUI/GUI.cpp
src/slic3r/GUI/GUI_App.cpp
src/slic3r/GUI/GUI_Init.cpp
src/slic3r/GUI/GUI_ObjectLayers.cpp
src/slic3r/GUI/GUI_ObjectList.cpp
src/slic3r/GUI/GUI_ObjectManipulation.cpp
src/slic3r/GUI/GUI_ObjectSettings.cpp
src/slic3r/GUI/GUI_Preview.cpp
src/slic3r/GUI/ImGuiWrapper.cpp
src/slic3r/GUI/Jobs/ArrangeJob.cpp
src/slic3r/GUI/Jobs/Job.cpp
src/slic3r/GUI/Jobs/RotoptimizeJob.cpp
src/slic3r/GUI/Jobs/SLAImportJob.cpp
src/slic3r/GUI/KBShortcutsDialog.cpp
src/slic3r/GUI/MainFrame.cpp
src/slic3r/GUI/Mouse3DController.cpp
src/slic3r/GUI/MsgDialog.cpp
src/slic3r/GUI/NotificationManager.hpp
src/slic3r/GUI/NotificationManager.cpp
src/slic3r/GUI/ObjectDataViewModel.cpp
src/slic3r/GUI/OpenGLManager.cpp
src/slic3r/GUI/OptionsGroup.cpp
src/slic3r/GUI/PhysicalPrinterDialog.cpp
src/slic3r/GUI/Plater.cpp
src/slic3r/GUI/Preferences.cpp
src/slic3r/GUI/PresetComboBoxes.cpp
src/slic3r/GUI/PresetHints.cpp
src/slic3r/GUI/PrintHostDialogs.cpp
src/slic3r/GUI/ProgressStatusBar.cpp
src/slic3r/GUI/RammingChart.cpp
src/slic3r/GUI/SavePresetDialog.cpp
src/slic3r/GUI/Search.cpp
src/slic3r/GUI/Selection.cpp
src/slic3r/GUI/SysInfoDialog.cpp
src/slic3r/GUI/Tab.cpp
src/slic3r/GUI/Tab.hpp
src/slic3r/GUI/UnsavedChangesDialog.cpp
src/slic3r/GUI/UpdateDialogs.cpp
src/slic3r/GUI/WipeTowerDialog.cpp
src/slic3r/GUI/wxExtensions.cpp
src/slic3r/Utils/AstroBox.cpp
src/slic3r/Utils/Duet.cpp
src/slic3r/Utils/FixModelByWin10.cpp
src/slic3r/Utils/FlashAir.cpp
src/slic3r/Utils/OctoPrint.cpp
src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/Http.cpp
src/slic3r/Utils/Process.cpp
src/libslic3r/GCode.cpp
src/libslic3r/ExtrusionEntity.cpp
src/libslic3r/Flow.cpp
src/libslic3r/Format/3mf.cpp
src/libslic3r/Format/AMF.cpp
src/libslic3r/miniz_extension.cpp
src/libslic3r/Preset.cpp
src/libslic3r/Print.cpp
src/libslic3r/SLA/Pad.cpp
src/libslic3r/SLA/Hollowing.cpp
src/libslic3r/SLAPrint.cpp
src/libslic3r/SLAPrintSteps.cpp
src/libslic3r/PrintBase.cpp
src/libslic3r/PrintConfig.cpp
src/libslic3r/Zipper.cpp
src/libslic3r/PrintObject.cpp

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -0,0 +1,404 @@
import re
datastore = dict();
datastore_trim = dict();# key:string -> TranslationLine
regex_only_letters = re.compile(r"[^a-zA-Z]")
allow_msgctxt = True;
ignore_case = False;
def trim(str):
redo = True;
while redo:
while len(str) > 0 and (
str[0] == ":"
or str[0] == "."
or str[0] == ","
or str[0] == "!"
):
str = str[1:];
while len(str) > 0 and (
str[-1] == ":"
or str[-1] == "."
or str[-1] == ","
or str[-1] == "!"
):
str = str[:-1];
str_stripped = str.strip();
if str == str_stripped:
redo = False
else:
str = str_stripped;
return str;
class TranslationFiles:
file_in = ""
file_out = ""
file_todo = ""
class TranslationLine:
header_comment = ""
raw_msgid = ""
msgid = ""
raw_msgstr = ""
msgstr = ""
multivalue = False
def main():
global datastore, datastore_trim, regex_only_letters, allow_msgctxt, ignore_case;
data_files = list(); # list of file paths
ui_dir = "";
operations = list(); # list of TranslationFiles
settings_stream = open("./settings.ini", mode="r", encoding="utf-8")
lines = settings_stream.read().splitlines()
for line in lines:
if line.startswith("data"):
data_files.append(line[line.index('=')+1:].strip());
if line.startswith("input"):
operations.append(TranslationFiles());
operations[-1].file_in = line[line.index('=')+1:].strip();
if line.startswith("output") and operations:
operations[-1].file_out = line[line.index('=')+1:].strip();
if line.startswith("todo") and operations:
operations[-1].file_todo = line[line.index('=')+1:].strip();
if line.startswith("ui_dir"):
ui_dir = line[line.index('=')+1:].strip();
if line.startswith("allow_msgctxt"):
allow_msgctxt = (line[line.index('=')+1:].strip().lower() == "true");
print("Don't comment msgctxt" if allow_msgctxt else "Commenting msgctxt");
if line.startswith("ignore_case"):
ignore_case = (line[line.index('=')+1:].strip().lower() == "true");
if ignore_case:
print("If the string is not found, try by ignoring the case");
# all_lines = list();
for data_file in data_files:
new_data = createKnowledge(data_file);
for dataline in new_data:
if not dataline.msgid in datastore:
datastore[dataline.msgid] = dataline;
datastore_trim[trim(dataline.msgid)] = dataline;
if dataline.msgid == " Layers,":
print(trim(dataline.msgid)+" is inside? "+("oui" if "Layers" in datastore_trim else "non"));
else:
str_old_val = datastore[dataline.msgid].msgstr;
str_test_val = dataline.msgstr;
length_old = len(regex_only_letters.sub("", str_old_val));
length_new = len(regex_only_letters.sub("", str_test_val));
# if already exist, only change it if the previous was lower than 3 char
if length_new > length_old and length_old < 3:
print(str_old_val.replace('\n', ' ')+" replaced by "+str_test_val.replace('\n', ' '));
datastore.put(id, str_test_val);
datastore_trim.put(trim(id), str_test_val);
print("finish reading" + data_file + " of size "+ str(len(new_data)) + ", now we had "+ str(len(datastore)) + " items");
if ignore_case:
temp = list();
for msgid in datastore:
if not msgid.lower() in datastore:
temp.append(msgid);
for msgid in temp:
datastore[msgid.lower()] = datastore[msgid];
temp = list();
for msgid in datastore_trim:
if not msgid.lower() in datastore_trim:
temp.append(msgid);
for msgid in temp:
datastore_trim[msgid.lower()] = datastore_trim[msgid];
for operation in operations:
print("Translating " + operation.file_in);
dict_ope = dict();
ope_file_in = list();
lst_temp = createKnowledge(operation.file_in);
print("String from source files: " + str(len(lst_temp)));
nbTrans = 0;
#remove duplicate
for line in lst_temp:
if not line.msgid in dict_ope:
dict_ope[line.msgid] = line;
ope_file_in.append(line);
if line.msgstr:
nbTrans+=1;
print(line.header_comment);
print(line.raw_msgid);
print(line.msgid);
print(line.raw_msgstr);
print(line.msgstr);
#add def from conf files
if ui_dir:
new_data = parse_ui_file(ui_dir+"/extruder.ui");
new_data.extend(parse_ui_file(ui_dir+"/extruder.ui"));
new_data.extend(parse_ui_file(ui_dir+"/filament.ui"));
new_data.extend(parse_ui_file(ui_dir+"/milling.ui"));
new_data.extend(parse_ui_file(ui_dir+"/print.ui"));
new_data.extend(parse_ui_file(ui_dir+"/printer_fff.ui"));
new_data.extend(parse_ui_file(ui_dir+"/printer_sla.ui"));
new_data.extend(parse_ui_file(ui_dir+"/sla_material.ui"));
new_data.extend(parse_ui_file(ui_dir+"/sla_print.ui"));
print("String from ui files: " + str(len(new_data)));
for dataline in new_data:
if not dataline.msgid in dict_ope:
dict_ope[dataline.msgid] = dataline;
ope_file_in.append(dataline);
print("String to translate: " + str(len(ope_file_in) - nbTrans)+" and already translated: "+str(nbTrans));
#create TODO file
if operation.file_todo:
outputUntranslated(ope_file_in, operation.file_todo);
#create .po file
if operation.file_out:
translate(ope_file_in, operation.file_out);
print("End of merge");
def createKnowledge(file_path_in):
read_data_lines = list();
try:
file_in_stream = open(file_path_in, mode="r", encoding="utf-8")
lines = file_in_stream.read().splitlines();
lines.append("");
line_idx = 0;
current_line = TranslationLine();
nb = 0;
while line_idx < len(lines):
if not lines[line_idx].startswith("msgid") or len(lines[line_idx]) <= 7:
if (lines[line_idx].startswith("#")
or lines[line_idx].startswith("msgctxt")
or len(lines[line_idx].strip()) == 0
):
if not allow_msgctxt and lines[line_idx].startswith("msgctxt"):
current_line.header_comment += "\n#, " + lines[line_idx];
else:
current_line.header_comment += "\n" + lines[line_idx];
line_idx+=1;
continue;
# get the msgid line
current_line.raw_msgid = lines[line_idx];
current_line.msgid = lines[line_idx][7:];
#get the next line (can be whatever)
line_idx+=1;
if line_idx >= len(lines):
return read_data_lines;
#populate the full current_line.msgid string
while lines[line_idx].startswith("\"") or lines[line_idx].startswith("msgid"):
current_line.raw_msgid += "\n" + lines[line_idx];
if lines[line_idx].startswith("msgid"):
current_line.multivalue = True;
if lines[line_idx].startswith("\""):
current_line.msgid = current_line.msgid[0:-1];
current_line.msgid += lines[line_idx][1:];
else:
current_line.msgid += "\n" + lines[line_idx];
#todo: do something for msgid_plural. Not needed right now...
#get the next line (can be whatever)
line_idx+=1;
if line_idx >= len(lines):
return read_data_lines;
#check validity of the id
if len(current_line.msgid) < 3:
current_line = TranslationLine();
continue;
current_line.msgid = current_line.msgid[0:-1];
#there should be a msgstr just after
if not lines[line_idx].startswith("msgstr") or len(lines[line_idx]) <= 8:
current_line = TranslationLine();
continue;
current_line.raw_msgstr = lines[line_idx];
if lines[line_idx][7] == "\"":
current_line.msgstr = lines[line_idx][8:];
elif lines[line_idx][6] == "[":
current_line.msgstr = lines[line_idx][11:];
else:
#can't parse
print("error, can't parse msgstr: '"+lines[line_idx]+"'");
current_line.msgstr = "";
line_idx+=1;
if line_idx >= len(lines):
return read_data_lines;
while lines[line_idx].startswith("\"") or lines[line_idx].startswith("msgstr"):
current_line.raw_msgstr += "\n" + lines[line_idx];
if lines[line_idx].startswith("\""):
current_line.msgstr = current_line.msgstr[0:-1];
current_line.msgstr += lines[line_idx][1:];
elif lines[line_idx].startswith("msgstr["):
current_line.msgstr = lines[line_idx][11:];
current_line.multivalue = True;
else:
current_line.msgstr += "\n" + lines[line_idx];
#get the next line (can be whatever)
line_idx+=1;
if line_idx >= len(lines):
return read_data_lines;
if current_line.msgstr:
current_line.msgstr = current_line.msgstr[0:-1];
read_data_lines.append(current_line);
current_line = TranslationLine();
except Exception as error:
print("error, cannot read file " + file_path_in);
print(error);
return read_data_lines;
def getTranslation(item):
if len(item.msgid) == 0:
return "";
if item.msgid in datastore:
return datastore[item.msgid].raw_msgstr;
elif item.msgid in datastore_trim:
good = datastore_trim[item.msgid];
if not good.multivalue:
return "msgstr \""+trim(good.msgstr)+"\"";
else:
item_msg_trim = trim(item.msgid);
if item_msg_trim in datastore:
good = datastore[trim(item.msgid)];
if not good.multivalue:
if good.msgid in item.msgid:
start_at = item.msgid.index(good.msgid);
return "msgstr \"" + item.msgid[0:start_at] + good.msgstr + item.msgid[start_at+len(good.msgid):] + "\"";
elif item_msg_trim in datastore_trim:
good = datastore_trim[item_msg_trim];
if not good.multivalue:
good_msg_trim = trim(good.msgid);
if good_msg_trim in item.msgid:
start_at = item.msgid.index(good_msg_trim);
return "msgstr \"" + item.msgid[0:start_at] + trim(good.msgstr) + item.msgid[start_at+len(good_msg_trim):] + "\"";
if ignore_case:
lowercase = TranslationLine();
lowercase.msgid = item.msgid.lower();
if lowercase.msgid != item.msgid:
lowercase.header_comment = item.header_comment;
lowercase.raw_msgid = item.raw_msgid;
lowercase.raw_msgstr = item.raw_msgstr;
lowercase.msgstr = item.msgstr;
lowercase.multivalue = item.multivalue;
return getTranslation(lowercase)
return "";
def outputUntranslated(data_to_translate, file_path_out):
try:
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
nb_lines = 0;
#sort to have an easier time trnaslating.
# idealy, they shoud be grouped by proximity, but it's abit more complicated to code
sorted_lines = list()
for dataline in data_to_translate:
if not dataline.msgstr and dataline.msgid and len(getTranslation(dataline)) == 0:
sorted_lines.append(dataline);
sorted_lines.sort(key=lambda x:x.msgid.lower())
# output bits that are empty
for dataline in sorted_lines:
file_out_stream.write(dataline.header_comment)
file_out_stream.write("\n")
file_out_stream.write(dataline.raw_msgid)
file_out_stream.write("\n")
file_out_stream.write(dataline.raw_msgstr)
file_out_stream.write("\n")
nb_lines+=1;
print("There is " + str(nb_lines) +" string untranslated");
except Exception as error:
print("error, cannot write file " + file_path_out);
print(error);
def translate(data_to_translate, file_path_out):
# try:
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
nb_lines = 0;
# translate bits that are empty
for dataline in data_to_translate:
if not dataline.msgstr:
transl = getTranslation(dataline)
if len(transl) > 0:
file_out_stream.write(dataline.header_comment)
file_out_stream.write("\n")
file_out_stream.write(dataline.raw_msgid)
file_out_stream.write("\n")
file_out_stream.write(transl)
file_out_stream.write("\n")
nb_lines+=1;
if dataline.raw_msgid.count('%') != transl.count('%'):
print("WARNING: not same number of '%' ( "+ str(dataline.raw_msgid.count('%')) + " => " + str(transl.count('%')) + ")"
+"\n for string:'" + dataline.msgid + " '\n=>'"+transl[8:]);
else:
file_out_stream.write(dataline.header_comment)
file_out_stream.write("\n")
file_out_stream.write(dataline.raw_msgid)
file_out_stream.write("\n")
file_out_stream.write(dataline.raw_msgstr)
file_out_stream.write("\n")
if dataline.raw_msgid.count('%') != dataline.raw_msgstr.count('%'):
print("WARNING: not same number of '%'( "+ str(dataline.raw_msgid.count('%')) + " => " + str(dataline.raw_msgstr.count('%')) + ")"
+"\n for string:'" + dataline.msgid + " '\n=>'"+dataline.msgstr);
nb_lines+=1;
print("There is " + str(nb_lines) +" string translated in the .po");
# except Exception as error:
# print("error, cannot write file " + file_path_out);
# print(error);
def parse_ui_file(file_path):
read_data_lines = list();
# try:
file_in_stream = open(file_path, mode="r", encoding="utf-8")
lines = file_in_stream.read().splitlines();
lines.append("");
line_idx = 0;
nb = 0;
while line_idx < len(lines):
items = lines[line_idx].strip().split(":");
if len(items) > 1:
if items[0]=="page" or items[0]=="group" or items[0]=="line":
current_line = TranslationLine();
current_line.header_comment = "\n#: "+file_path+" : l"+str(line_idx);
current_line.raw_msgid = "msgid \""+items[-1]+"\"";
current_line.msgid = items[-1];
current_line.raw_msgstr = "msgstr \"\"";
current_line.msgstr = "";
read_data_lines.append(current_line);
if items[0]=="setting":
for item in items:
if item.startswith("label$") or item.startswith("sidetext$") or item.startswith("sidetext$"):
current_line = TranslationLine();
current_line.header_comment = "\n#: "+file_path+" : l"+str(line_idx);
current_line.msgid = item.split("$")[-1];
current_line.raw_msgid = "msgid \""+current_line.msgid+"\"";
current_line.raw_msgstr = "msgstr \"\"";
current_line.msgstr = "";
read_data_lines.append(current_line);
line_idx+=1;
return read_data_lines;
main();

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.

View File

@ -1,9 +1,15 @@
#data = C:/local/Slic3r/resources/localization/lang/TODO.po
#data = C:/local/Slic3r/resources/localization/lang/Slic3r.po
data = C:/local/Slic3r/resources/localization/lang/Slic3r++.po
data = C:/local/Slic3r/resources/localization/lang/PrusaSlicer_fr.po
#data = Slic3r++.po
data = ./PrusaSlicer_fr.po
data = ./my_current.po
data = ./Slic3r.po
input = C:/local/Slic3r/resources/localization/Slic3r.pot
todo = C:/local/Slic3r/resources/localization/lang/TODO.po
output = C:/local/Slic3r/resources/localization/lang/Slic3r.po
ui_dir = ../ui_layout
allow_msgctxt = false
ignore_case = false
input = Slic3r.pot
todo = TODO.po
output = Slic3r.po

File diff suppressed because it is too large Load Diff

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,28 @@
import os
import sys
import subprocess
languages = ["cs", "de", "en","es", "fr", "it", "ja", "ko", "nl", "pl", "pt_br", "ru", "tr", "uk", "zh_cn", "zh_tw"];
print(sys.getrecursionlimit())
for lang in languages:
if 'y' != input("translating "+lang+"? (y/n): "):
continue
# create .po
os.system("msgunfmt "+lang+"/PrusaSlicer.mo -o "+lang+"/PrusaSlicer.po > nul");
file_out_stream = open(lang+"/settings.ini", mode="w", encoding="utf-8");
file_out_stream.write("data = ./PrusaSlicer.po\n");
file_out_stream.write("ui_dir = ../../ui_layout\n");
file_out_stream.write("ignore_case = true\n");
file_out_stream.write("input = ../Slic3r.pot\n");
file_out_stream.write("todo = ./todo.po\n");
file_out_stream.write("output = ./Slic3r.po\n");
#flush
file_out_stream.close();
p = subprocess.Popen(["python","../pom_merger.py"], cwd=lang);
p.wait();
#create .mo
os.system("msgfmt "+lang+"/Slic3r.po -o "+lang+"/SuperSlicer.mo");
# msgfmt Slic3r.po -o Slic3r.mo

File diff suppressed because it is too large Load Diff

Binary file not shown.

File diff suppressed because it is too large Load Diff

Binary file not shown.