mirror of
https://git.mirrors.martin98.com/https://github.com/slic3r/Slic3r.git
synced 2025-08-13 19:25:54 +08:00
update to localization.
* keep Slic3r in tooltip, as it's replaced * use %S or %1% to set the name of the software in other places. * pom_merger now don't save empty translations. * pom_merger now can propose similar but not identical translations, to be able to copy & modify them * pom_merger now can add language to header via a setting. * github actions will now compile the pot each time. No need to store it in github
This commit is contained in:
parent
a758dd8913
commit
14e6ed3e56
3
.github/workflows/ccpp_mac.yml
vendored
3
.github/workflows/ccpp_mac.yml
vendored
@ -52,6 +52,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: make gettext_po_to_mo
|
run: make gettext_po_to_mo
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: make gettext_make_pot
|
||||||
- name: update Info.plist
|
- name: update Info.plist
|
||||||
working-directory: ./build/src
|
working-directory: ./build/src
|
||||||
run: sed "s/+UNKNOWN/_$(date '+%F')/" Info.plist >Info.date.plist
|
run: sed "s/+UNKNOWN/_$(date '+%F')/" Info.plist >Info.date.plist
|
||||||
|
3
.github/workflows/ccpp_mac_rc.yml
vendored
3
.github/workflows/ccpp_mac_rc.yml
vendored
@ -52,6 +52,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: make gettext_po_to_mo
|
run: make gettext_po_to_mo
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: make gettext_make_pot
|
||||||
- name: update Info.plist
|
- name: update Info.plist
|
||||||
working-directory: ./build/src
|
working-directory: ./build/src
|
||||||
run: sed "s/+UNKNOWN/_$(date '+%F')/" Info.plist >Info.date.plist
|
run: sed "s/+UNKNOWN/_$(date '+%F')/" Info.plist >Info.date.plist
|
||||||
|
3
.github/workflows/ccpp_ubuntu.yml
vendored
3
.github/workflows/ccpp_ubuntu.yml
vendored
@ -53,6 +53,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: make gettext_po_to_mo
|
run: make gettext_po_to_mo
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: make gettext_make_pot
|
||||||
- name: create directory and copy into it
|
- name: create directory and copy into it
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: |
|
run: |
|
||||||
|
3
.github/workflows/ccpp_ubuntu_rc.yml
vendored
3
.github/workflows/ccpp_ubuntu_rc.yml
vendored
@ -53,6 +53,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: make gettext_po_to_mo
|
run: make gettext_po_to_mo
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: make gettext_make_pot
|
||||||
- name: create directory and copy into it
|
- name: create directory and copy into it
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: |
|
run: |
|
||||||
|
3
.github/workflows/ccpp_win.yml
vendored
3
.github/workflows/ccpp_win.yml
vendored
@ -62,6 +62,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: msbuild /m /P:Configuration=Release gettext_po_to_mo.vcxproj
|
run: msbuild /m /P:Configuration=Release gettext_po_to_mo.vcxproj
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: msbuild /m /P:Configuration=Release gettext_make_pot.vcxproj
|
||||||
- name: create directory and copy into it
|
- name: create directory and copy into it
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: ls
|
run: ls
|
||||||
|
3
.github/workflows/ccpp_win_rc.yml
vendored
3
.github/workflows/ccpp_win_rc.yml
vendored
@ -62,6 +62,9 @@ jobs:
|
|||||||
- name: make .mo
|
- name: make .mo
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: msbuild /m /P:Configuration=Release gettext_po_to_mo.vcxproj
|
run: msbuild /m /P:Configuration=Release gettext_po_to_mo.vcxproj
|
||||||
|
- name: make .pot
|
||||||
|
working-directory: ./build
|
||||||
|
run: msbuild /m /P:Configuration=Release gettext_make_pot.vcxproj
|
||||||
- name: create directory and copy into it
|
- name: create directory and copy into it
|
||||||
working-directory: ./build
|
working-directory: ./build
|
||||||
run: ls
|
run: ls
|
||||||
|
@ -38,12 +38,20 @@ To decompile the .mo of Prusaslicer, use the command `msgunfmt PrusaSlicer.mo -o
|
|||||||
So the settings.ini contains these lines :
|
So the settings.ini contains these lines :
|
||||||
|
|
||||||
```
|
```
|
||||||
data = es/my_old_po_file.po
|
data = es/Slic3r.po
|
||||||
|
data = MyKnowledgeBase.po
|
||||||
data = es/PrusaSlicer_es.po
|
data = es/PrusaSlicer_es.po
|
||||||
|
|
||||||
|
database_out = MyKnowledgeBase.po
|
||||||
|
|
||||||
ui_dir = ../ui_layout
|
ui_dir = ../ui_layout
|
||||||
allow_msgctxt = false
|
allow_msgctxt = false
|
||||||
ignore_case = false
|
ignore_case = false
|
||||||
|
remove_comment = true
|
||||||
|
percent_error_similar = 0.4
|
||||||
|
max_similar = 3
|
||||||
|
language = french
|
||||||
|
language_code = fr
|
||||||
|
|
||||||
input = Slic3r.pot
|
input = Slic3r.pot
|
||||||
todo = es/todo.po
|
todo = es/todo.po
|
||||||
@ -51,10 +59,15 @@ output = es/Slic3r.po
|
|||||||
```
|
```
|
||||||
|
|
||||||
Notes:
|
Notes:
|
||||||
* thee 'todo' and 'output' files will be erased, so be sure nothing has this name (or write another name)
|
* thee 'todo' and 'output' files will be erased, so be sure nothing important has this name (or write another name)
|
||||||
|
* the file at 'database_out' will receive all the database created from the data files. That way, it will keep your new & old unused translations just in case the wording revert back to it, or to be used as reference for the helper.
|
||||||
* ui_dir should be the path to the slic3r/resources/ui_layout directory. If you're in slic3r/resources/localization, this value is good.
|
* 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.
|
* 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.
|
* ignore_case is a bool that will let the tool to ignore the case when comparing msgid if no translation is found.
|
||||||
|
* remove_comment is a bool taht will remove all comment in the output file. It's to avoid unecessary changes in the git commit.
|
||||||
|
* percent_error_similar is a number between 0 and 1. This will activate the helper that will write help comment in the TODO file. These will present similar string that are already translated, to let you pick chunk that are already translated to avoid redoing all the work. It's the percentage of difference allowed (0 = identical, 1 = everything, 0.5 = not more than half of the string is different), using (levenshtein distance / msgid length).
|
||||||
|
* max_similar: max number of help translation per item
|
||||||
|
* language and language_code: text to include in the header.
|
||||||
|
|
||||||
|
|
||||||
### 2) launch the utility.
|
### 2) launch the utility.
|
||||||
@ -83,13 +96,18 @@ After filling the todo file, change the settings.ini:
|
|||||||
|
|
||||||
```
|
```
|
||||||
data = es/todo.po
|
data = es/todo.po
|
||||||
data = es/Slic3r.po
|
data = MyKnowledgeBase.po
|
||||||
data = es/my_old_po_file.po
|
|
||||||
data = es/PrusaSlicer_es.po
|
database_out = MyKnowledgeBase.po
|
||||||
|
|
||||||
ui_dir = ../ui_layout
|
ui_dir = ../ui_layout
|
||||||
allow_msgctxt = false
|
allow_msgctxt = false
|
||||||
ignore_case = false
|
ignore_case = false
|
||||||
|
remove_comment = true
|
||||||
|
percent_error_similar = 0.4
|
||||||
|
max_similar = 3
|
||||||
|
language = french
|
||||||
|
language_code = fr
|
||||||
|
|
||||||
input = Slic3r.pot
|
input = Slic3r.pot
|
||||||
todo = es/todo.po
|
todo = es/todo.po
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -1,6 +1,13 @@
|
|||||||
import re
|
import re
|
||||||
from datetime import date
|
from datetime import date
|
||||||
|
|
||||||
|
try:
|
||||||
|
from Levenshtein import distance as levenshtein_distance
|
||||||
|
except ImportError:
|
||||||
|
print("you need to do 'python -m pip install python-Levenshtein'");
|
||||||
|
exit(0);
|
||||||
|
|
||||||
|
|
||||||
datastore = dict();
|
datastore = dict();
|
||||||
datastore_trim = dict();# key:string -> TranslationLine
|
datastore_trim = dict();# key:string -> TranslationLine
|
||||||
|
|
||||||
@ -8,6 +15,11 @@ regex_only_letters = re.compile(r"[^a-zA-Z]")
|
|||||||
allow_msgctxt = True;
|
allow_msgctxt = True;
|
||||||
ignore_case = False;
|
ignore_case = False;
|
||||||
remove_comment = False;
|
remove_comment = False;
|
||||||
|
percent_error_similar = 0
|
||||||
|
language_code = "??"
|
||||||
|
language = ""
|
||||||
|
max_similar = 3;
|
||||||
|
database_out = "";
|
||||||
|
|
||||||
def trim(str):
|
def trim(str):
|
||||||
redo = True;
|
redo = True;
|
||||||
@ -37,6 +49,7 @@ class TranslationFiles:
|
|||||||
file_in = ""
|
file_in = ""
|
||||||
file_out = ""
|
file_out = ""
|
||||||
file_todo = ""
|
file_todo = ""
|
||||||
|
database = ""
|
||||||
|
|
||||||
class TranslationLine:
|
class TranslationLine:
|
||||||
header_comment = ""
|
header_comment = ""
|
||||||
@ -48,6 +61,7 @@ class TranslationLine:
|
|||||||
|
|
||||||
def main():
|
def main():
|
||||||
global datastore, datastore_trim, regex_only_letters, allow_msgctxt, ignore_case, remove_comment;
|
global datastore, datastore_trim, regex_only_letters, allow_msgctxt, ignore_case, remove_comment;
|
||||||
|
global percent_error_similar, language, language_code, max_similar, database_out;
|
||||||
data_files = list(); # list of file paths
|
data_files = list(); # list of file paths
|
||||||
ui_dir = "";
|
ui_dir = "";
|
||||||
operations = list(); # list of TranslationFiles
|
operations = list(); # list of TranslationFiles
|
||||||
@ -55,7 +69,10 @@ def main():
|
|||||||
lines = settings_stream.read().splitlines()
|
lines = settings_stream.read().splitlines()
|
||||||
for line in lines:
|
for line in lines:
|
||||||
if line.startswith("data"):
|
if line.startswith("data"):
|
||||||
data_files.append(line[line.index('=')+1:].strip());
|
if line.startswith("database_out"):
|
||||||
|
database_out = line[line.index('=')+1:].strip();
|
||||||
|
else:
|
||||||
|
data_files.append(line[line.index('=')+1:].strip());
|
||||||
|
|
||||||
if line.startswith("input"):
|
if line.startswith("input"):
|
||||||
operations.append(TranslationFiles());
|
operations.append(TranslationFiles());
|
||||||
@ -67,6 +84,7 @@ def main():
|
|||||||
if line.startswith("todo") and operations:
|
if line.startswith("todo") and operations:
|
||||||
operations[-1].file_todo = line[line.index('=')+1:].strip();
|
operations[-1].file_todo = line[line.index('=')+1:].strip();
|
||||||
|
|
||||||
|
|
||||||
if line.startswith("ui_dir"):
|
if line.startswith("ui_dir"):
|
||||||
ui_dir = line[line.index('=')+1:].strip();
|
ui_dir = line[line.index('=')+1:].strip();
|
||||||
|
|
||||||
@ -82,27 +100,45 @@ def main():
|
|||||||
remove_comment = (line[line.index('=')+1:].strip().lower() == "true");
|
remove_comment = (line[line.index('=')+1:].strip().lower() == "true");
|
||||||
if remove_comment:
|
if remove_comment:
|
||||||
print("Will not output the comments");
|
print("Will not output the comments");
|
||||||
|
|
||||||
|
if line.startswith("percent_error_similar"):
|
||||||
|
percent_error_similar = float(line[line.index('=')+1:].strip());
|
||||||
|
print("percent_error_similar set to " + str(percent_error_similar));
|
||||||
|
|
||||||
|
if line.startswith("max_similar"):
|
||||||
|
max_similar = int(line[line.index('=')+1:].strip());
|
||||||
|
print("max_similar set to " + str(max_similar));
|
||||||
|
|
||||||
|
if line.startswith("language"):
|
||||||
|
if line.startswith("language_code"):
|
||||||
|
language_code = line[line.index('=')+1:].strip();
|
||||||
|
print("language_code set to " + language_code);
|
||||||
|
else:
|
||||||
|
language = line[line.index('=')+1:].strip();
|
||||||
|
print("language set to " + language);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# all_lines = list();
|
# all_lines = list();
|
||||||
for data_file in data_files:
|
for data_file in data_files:
|
||||||
new_data = createKnowledge(data_file);
|
new_data = createKnowledge(data_file);
|
||||||
for dataline in new_data:
|
for dataline in new_data:
|
||||||
if not dataline.msgid in datastore:
|
if len(dataline.msgstr) > 0:
|
||||||
datastore[dataline.msgid] = dataline;
|
if not dataline.msgid in datastore:
|
||||||
datastore_trim[trim(dataline.msgid)] = dataline;
|
datastore[dataline.msgid] = dataline;
|
||||||
if dataline.msgid == " Layers,":
|
datastore_trim[trim(dataline.msgid)] = dataline;
|
||||||
print(trim(dataline.msgid)+" is inside? "+("oui" if "Layers" in datastore_trim else "non"));
|
if dataline.msgid == " Layers,":
|
||||||
else:
|
print(trim(dataline.msgid)+" is inside? "+("oui" if "Layers" in datastore_trim else "non"));
|
||||||
str_old_val = datastore[dataline.msgid].msgstr;
|
else:
|
||||||
str_test_val = dataline.msgstr;
|
str_old_val = datastore[dataline.msgid].msgstr;
|
||||||
length_old = len(regex_only_letters.sub("", str_old_val));
|
str_test_val = dataline.msgstr;
|
||||||
length_new = len(regex_only_letters.sub("", str_test_val));
|
length_old = len(regex_only_letters.sub("", str_old_val));
|
||||||
# if already exist, only change it if the previous was lower than 3 char
|
length_new = len(regex_only_letters.sub("", str_test_val));
|
||||||
if length_new > length_old and length_old < 3:
|
# if already exist, only change it if the previous was lower than 3 char
|
||||||
print(str_old_val.replace('\n', ' ')+" replaced by "+str_test_val.replace('\n', ' '));
|
if length_new > length_old and length_old < 3:
|
||||||
datastore[dataline.msgid].msgstr = str_test_val;
|
print(str_old_val.replace('\n', ' ')+" replaced by "+str_test_val.replace('\n', ' '));
|
||||||
datastore_trim[trim(dataline.msgid)].msgstr = str_test_val;
|
datastore[dataline.msgid].msgstr = str_test_val;
|
||||||
|
datastore_trim[trim(dataline.msgid)].msgstr = str_test_val;
|
||||||
print("finish reading" + data_file + " of size "+ str(len(new_data)) + ", now we had "+ str(len(datastore)) + " items");
|
print("finish reading" + data_file + " of size "+ str(len(new_data)) + ", now we had "+ str(len(datastore)) + " items");
|
||||||
|
|
||||||
if ignore_case:
|
if ignore_case:
|
||||||
@ -155,6 +191,10 @@ def main():
|
|||||||
dict_ope[dataline.msgid] = dataline;
|
dict_ope[dataline.msgid] = dataline;
|
||||||
ope_file_in.append(dataline);
|
ope_file_in.append(dataline);
|
||||||
print("String to translate: " + str(len(ope_file_in) - nbTrans)+" and already translated: "+str(nbTrans));
|
print("String to translate: " + str(len(ope_file_in) - nbTrans)+" and already translated: "+str(nbTrans));
|
||||||
|
|
||||||
|
#create database
|
||||||
|
if database_out:
|
||||||
|
outputDatabase(database_out);
|
||||||
|
|
||||||
#create TODO file
|
#create TODO file
|
||||||
if operation.file_todo:
|
if operation.file_todo:
|
||||||
@ -260,7 +300,7 @@ def createKnowledge(file_path_in):
|
|||||||
read_data_lines.append(current_line);
|
read_data_lines.append(current_line);
|
||||||
current_line = TranslationLine();
|
current_line = TranslationLine();
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
print("error, cannot read file " + file_path_in);
|
print("Warning, cannot read file " + file_path_in);
|
||||||
print(error);
|
print(error);
|
||||||
return read_data_lines;
|
return read_data_lines;
|
||||||
|
|
||||||
@ -301,12 +341,21 @@ def getTranslation(item):
|
|||||||
return getTranslation(lowercase)
|
return getTranslation(lowercase)
|
||||||
return "";
|
return "";
|
||||||
|
|
||||||
|
def getTranslationNear(msgid_to_search, percent):
|
||||||
|
max_word_diff = 1 + int(percent * len(msgid_to_search));
|
||||||
|
possible_solutions = list();
|
||||||
|
for msgid in datastore:
|
||||||
|
dist = levenshtein_distance(msgid, msgid_to_search);
|
||||||
|
if dist < max_word_diff:
|
||||||
|
possible_solutions.append( (dist, datastore[msgid]) );
|
||||||
|
possible_solutions.sort(key=lambda x:x[0]);
|
||||||
|
return possible_solutions;
|
||||||
|
|
||||||
def outputUntranslated(data_to_translate, file_path_out):
|
def outputUntranslated(data_to_translate, file_path_out):
|
||||||
try:
|
try:
|
||||||
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
|
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
|
||||||
nb_lines = 0;
|
nb_lines = 0;
|
||||||
#sort to have an easier time trnaslating.
|
#sort to have an easier time translating.
|
||||||
# idealy, they shoud be grouped by proximity, but it's abit more complicated to code
|
# idealy, they shoud be grouped by proximity, but it's abit more complicated to code
|
||||||
sorted_lines = list()
|
sorted_lines = list()
|
||||||
for dataline in data_to_translate:
|
for dataline in data_to_translate:
|
||||||
@ -316,12 +365,20 @@ def outputUntranslated(data_to_translate, file_path_out):
|
|||||||
|
|
||||||
# output bits that are empty
|
# output bits that are empty
|
||||||
for dataline in sorted_lines:
|
for dataline in sorted_lines:
|
||||||
file_out_stream.write(dataline.header_comment)
|
file_out_stream.write(dataline.header_comment);
|
||||||
file_out_stream.write("\n")
|
file_out_stream.write("\n");
|
||||||
file_out_stream.write(dataline.raw_msgid)
|
# get translation that are near enough to be copy-pasted by humans.
|
||||||
file_out_stream.write("\n")
|
good_enough = getTranslationNear(dataline.msgid, 0.4);
|
||||||
file_out_stream.write(dataline.raw_msgstr)
|
if len(good_enough) >0:
|
||||||
file_out_stream.write("\n")
|
file_out_stream.write("#Similar to me: "+dataline.msgid+"\n");
|
||||||
|
for index in range(min(len(good_enough), max_similar)):
|
||||||
|
file_out_stream.write("# "+str(good_enough[index][0])+("" if len(str(good_enough[index][0]))>2 else " " if len(str(good_enough[index][0]))==2 else " ")
|
||||||
|
+" changes: " + good_enough[index][1].msgid+"\n");
|
||||||
|
file_out_stream.write("# translation: " + good_enough[index][1].msgstr+"\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;
|
nb_lines+=1;
|
||||||
|
|
||||||
print("There is " + str(nb_lines) +" string untranslated");
|
print("There is " + str(nb_lines) +" string untranslated");
|
||||||
@ -332,7 +389,7 @@ def outputUntranslated(data_to_translate, file_path_out):
|
|||||||
def translate(data_to_translate, file_path_out):
|
def translate(data_to_translate, file_path_out):
|
||||||
# try:
|
# try:
|
||||||
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
|
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
|
||||||
file_out_stream.write("# Translation file for ???\n");
|
file_out_stream.write("# Translation file for "+(language if len(language)>0 else language_code)+"\n");
|
||||||
file_out_stream.write("# Copyright (C) 2021\n");
|
file_out_stream.write("# Copyright (C) 2021\n");
|
||||||
file_out_stream.write("# This file is distributed under the same license as Slic3r.\n");
|
file_out_stream.write("# This file is distributed under the same license as Slic3r.\n");
|
||||||
file_out_stream.write("#\n");
|
file_out_stream.write("#\n");
|
||||||
@ -346,7 +403,7 @@ def translate(data_to_translate, file_path_out):
|
|||||||
file_out_stream.write("\"MIME-Version: 1.0\\n\"\n");
|
file_out_stream.write("\"MIME-Version: 1.0\\n\"\n");
|
||||||
file_out_stream.write("\"Content-Type: text/plain; charset=UTF-8\\n\"\n");
|
file_out_stream.write("\"Content-Type: text/plain; charset=UTF-8\\n\"\n");
|
||||||
file_out_stream.write("\"Content-Transfer-Encoding: 8bit\\n\"\n");
|
file_out_stream.write("\"Content-Transfer-Encoding: 8bit\\n\"\n");
|
||||||
file_out_stream.write("\"Language:\\n\"\n");
|
file_out_stream.write("\"Language:"+language_code+"\\n\"\n");
|
||||||
nb_lines = 0;
|
nb_lines = 0;
|
||||||
data_to_translate.sort(key=lambda x:x.msgid.lower().strip())
|
data_to_translate.sort(key=lambda x:x.msgid.lower().strip())
|
||||||
# translate bits that are empty
|
# translate bits that are empty
|
||||||
@ -384,6 +441,26 @@ def translate(data_to_translate, file_path_out):
|
|||||||
# print("error, cannot write file " + file_path_out);
|
# print("error, cannot write file " + file_path_out);
|
||||||
# print(error);
|
# print(error);
|
||||||
|
|
||||||
|
def outputDatabase(file_path_out):
|
||||||
|
try:
|
||||||
|
file_out_stream = open(file_path_out, mode="w", encoding="utf-8")
|
||||||
|
nb_lines = 0;
|
||||||
|
|
||||||
|
for msgid in datastore:
|
||||||
|
dataline = datastore[msgid];
|
||||||
|
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) +" in your database file");
|
||||||
|
except Exception as error:
|
||||||
|
print("error, cannot write file " + file_path_out);
|
||||||
|
print(error);
|
||||||
|
|
||||||
def parse_ui_file(file_path):
|
def parse_ui_file(file_path):
|
||||||
read_data_lines = list();
|
read_data_lines = list();
|
||||||
# try:
|
# try:
|
||||||
@ -414,7 +491,7 @@ def parse_ui_file(file_path):
|
|||||||
if items[0]=="setting":
|
if items[0]=="setting":
|
||||||
for item in items:
|
for item in items:
|
||||||
if item.startswith("label$") or item.startswith("full_label$") or item.startswith("sidetext$") or item.startswith("tooltip$"):
|
if item.startswith("label$") or item.startswith("full_label$") or item.startswith("sidetext$") or item.startswith("tooltip$"):
|
||||||
if item.split("$")[-1] != "_" and len(item.split("$")[-1]) > 0 :
|
if item.split("$")[-1] != '_' and len(item.split("$")[-1]) > 0 :
|
||||||
current_line = TranslationLine();
|
current_line = TranslationLine();
|
||||||
current_line.header_comment = "\n#: "+file_path+" : l"+str(line_idx);
|
current_line.header_comment = "\n#: "+file_path+" : l"+str(line_idx);
|
||||||
current_line.msgid = item.split("$")[-1];
|
current_line.msgid = item.split("$")[-1];
|
||||||
|
Binary file not shown.
@ -1,15 +1,35 @@
|
|||||||
|
# all files taken as input to construt the knowledge base
|
||||||
|
# a file can't overwrite what's already inside (unless it's empty), so put the best files in first.
|
||||||
|
# if a file ins't here, a warning message will be emmited
|
||||||
|
data = Slic3r.po
|
||||||
|
data = TODO.po
|
||||||
|
data = MyKnowledgeBase.po
|
||||||
|
|
||||||
#data = C:/local/Slic3r/resources/localization/lang/TODO.po
|
# optional: output all the knowledge base into a file, to be reused in the future.
|
||||||
#data = C:/local/Slic3r/resources/localization/lang/Slic3r.po
|
database_out = MyKnowledgeBase.po
|
||||||
#data = Slic3r++.po
|
|
||||||
data = it/TODO.po
|
|
||||||
data = it/Slic3r.po
|
|
||||||
|
|
||||||
|
|
||||||
|
# path to the ui_layout dir, to grab all ui string defined here
|
||||||
ui_dir = ../ui_layout
|
ui_dir = ../ui_layout
|
||||||
|
# to allow to keep the msgctxt tags. You need a recent version of gettext to use that.
|
||||||
allow_msgctxt = false
|
allow_msgctxt = false
|
||||||
|
# the tool to ignore the case when comparing msgid if no exact translation is found.
|
||||||
ignore_case = false
|
ignore_case = false
|
||||||
|
# will remove the comments in the output files (not the todo).
|
||||||
remove_comment = true
|
remove_comment = true
|
||||||
|
# flaot between 0 and 1. If higher than 0, the tool may porpose you some similar transaltion from the knowledge base in the comment
|
||||||
|
# to help you write the translation. It's useful when only a part of the original string changes, so you can reuse almost everything.
|
||||||
|
# it's the % of diff (levenshtein distance / msgid length) allowed for an other string to be proposed
|
||||||
|
percent_error_similar = 0.4
|
||||||
|
# max number of proposed translation per item. Work with percent_error_similar.
|
||||||
|
max_similar = 3
|
||||||
|
# strings written in the header
|
||||||
|
language = french
|
||||||
|
language_code = fr
|
||||||
|
|
||||||
|
# input is the pot (or po) where the msgid are picked
|
||||||
input = Slic3r.pot
|
input = Slic3r.pot
|
||||||
todo = it/TODO.po
|
# the todo will receive the msgid for which no translation are found
|
||||||
output = it/Slic3r.po
|
todo = fr/TODO.po
|
||||||
|
# the output will receive the other msgid with their translation
|
||||||
|
output = fr/Slic3r.po
|
||||||
|
@ -1754,8 +1754,8 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"You can use this to force fatter extrudates for better adhesion. If expressed "
|
"You can use this to force fatter extrudates for better adhesion. If expressed "
|
||||||
"as percentage (for example 140%) it will be computed over the nozzle diameter "
|
"as percentage (for example 140%) it will be computed over the nozzle diameter "
|
||||||
"of the nozzle used for the type of extrusion. "
|
"of the nozzle used for the type of extrusion. "
|
||||||
"If set to zero, it will use the default extrusion width."
|
"If set to zero, it will use the default extrusion width.") + std::string("\n") +
|
||||||
"\nYou can set either 'Spacing', or 'Width'; the other will be calculated, using the perimeter 'Overlap' percentages and default layer height.");
|
L("You can set either 'Spacing', or 'Width'; the other will be calculated, using the perimeter 'Overlap' percentages and default layer height.");
|
||||||
def->sidetext = L("mm or %");
|
def->sidetext = L("mm or %");
|
||||||
def->ratio_over = "nozzle_diameter";
|
def->ratio_over = "nozzle_diameter";
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
@ -1992,8 +1992,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->label = L("Length of the infill anchor");
|
def->label = L("Length of the infill anchor");
|
||||||
def->category = OptionCategory::infill;
|
def->category = OptionCategory::infill;
|
||||||
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||||
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Slic3r tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||||
SLIC3R_APP_NAME " tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
|
||||||
"shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side "
|
"shorter than infill_anchor_max is found, the infill line is connected to a perimeter segment at just one side "
|
||||||
"and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max. "
|
"and the length of the perimeter segment taken is limited to this parameter, but no longer than anchor_length_max. "
|
||||||
"\nSet this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
"\nSet this parameter to zero to disable anchoring perimeters connected to a single infill line.");
|
||||||
@ -2019,8 +2018,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->label = L("Maximum length of the infill anchor");
|
def->label = L("Maximum length of the infill anchor");
|
||||||
def->category = def_infill_anchor_min->category;
|
def->category = def_infill_anchor_min->category;
|
||||||
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
def->tooltip = L("Connect an infill line to an internal perimeter with a short segment of an additional perimeter. "
|
||||||
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. "
|
"If expressed as percentage (example: 15%) it is calculated over infill extrusion width. Slic3r tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
||||||
SLIC3R_APP_NAME " tries to connect two close infill lines to a short perimeter segment. If no such perimeter segment "
|
|
||||||
"shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side "
|
"shorter than this parameter is found, the infill line is connected to a perimeter segment at just one side "
|
||||||
"and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter. "
|
"and the length of the perimeter segment taken is limited to infill_anchor, but no longer than this parameter. "
|
||||||
"\nIf set to 0, the old algorithm for infill connection will be used, it should create the same result as with 1000 & 0.");
|
"\nIf set to 0, the old algorithm for infill connection will be used, it should create the same result as with 1000 & 0.");
|
||||||
@ -2873,7 +2871,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->label = L("Round corners");
|
def->label = L("Round corners");
|
||||||
def->full_label = L("Round corners for perimeters");
|
def->full_label = L("Round corners for perimeters");
|
||||||
def->category = OptionCategory::perimeter;
|
def->category = OptionCategory::perimeter;
|
||||||
def->tooltip = L("Internal periemters will go around sharp corners by tunring around isntead of making the same sharp corner."
|
def->tooltip = L("Internal perimeters will go around sharp corners by turning around instead of making the same sharp corner."
|
||||||
" This can help when there is visible holes in sharp corners on perimeters");
|
" This can help when there is visible holes in sharp corners on perimeters");
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
@ -3023,7 +3021,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
"the slicing job and reducing memory usage. High-resolution models often carry "
|
"the slicing job and reducing memory usage. High-resolution models often carry "
|
||||||
"more details than printers can render. Set to zero to disable any simplification "
|
"more details than printers can render. Set to zero to disable any simplification "
|
||||||
"and use full resolution from input. "
|
"and use full resolution from input. "
|
||||||
"\nNote: " SLIC3R_APP_NAME " has an internal working resolution of 0.0001mm."
|
"\nNote: Slic3r has an internal working resolution of 0.0001mm."
|
||||||
"\nInfill & Thin areas are simplified up to 0.0125mm.");
|
"\nInfill & Thin areas are simplified up to 0.0125mm.");
|
||||||
def->sidetext = L("mm");
|
def->sidetext = L("mm");
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
@ -3915,7 +3913,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->full_label = L("Thin walls");
|
def->full_label = L("Thin walls");
|
||||||
def->category = OptionCategory::perimeter;
|
def->category = OptionCategory::perimeter;
|
||||||
def->tooltip = L("Detect single-width walls (parts where two extrusions don't fit and we need "
|
def->tooltip = L("Detect single-width walls (parts where two extrusions don't fit and we need "
|
||||||
"to collapse them into a single trace). If unchecked, slic3r may try to fit perimeters "
|
"to collapse them into a single trace). If unchecked, Slic3r may try to fit perimeters "
|
||||||
"where it's not possible, creating some overlap leading to over-extrusion.");
|
"where it's not possible, creating some overlap leading to over-extrusion.");
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->set_default_value(new ConfigOptionBool(true));
|
def->set_default_value(new ConfigOptionBool(true));
|
||||||
@ -3926,7 +3924,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->category = OptionCategory::perimeter;
|
def->category = OptionCategory::perimeter;
|
||||||
def->tooltip = L("Minimum width for the extrusion to be extruded (widths lower than the nozzle diameter will be over-extruded at the nozzle diameter)."
|
def->tooltip = L("Minimum width for the extrusion to be extruded (widths lower than the nozzle diameter will be over-extruded at the nozzle diameter)."
|
||||||
" If expressed as percentage (for example 110%) it will be computed over nozzle diameter."
|
" If expressed as percentage (for example 110%) it will be computed over nozzle diameter."
|
||||||
" The default behavior of slic3r and slic3rPE is with a 33% value. Put 100% to avoid any sort of over-extrusion.");
|
" The default behavior of PrusaSlicer is with a 33% value. Put 100% to avoid any sort of over-extrusion.");
|
||||||
def->ratio_over = "nozzle_diameter";
|
def->ratio_over = "nozzle_diameter";
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
@ -3976,7 +3974,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def = this->add("time_estimation_compensation", coPercent);
|
def = this->add("time_estimation_compensation", coPercent);
|
||||||
def->label = L("Time estimation compensation");
|
def->label = L("Time estimation compensation");
|
||||||
def->category = OptionCategory::firmware;
|
def->category = OptionCategory::firmware;
|
||||||
def->tooltip = L("This setting allow you to modify the time estiamtion by a % amount. As slic3r only use the marlin algorithm, it's not precise enough if an other firmware is used.");
|
def->tooltip = L("This setting allow you to modify the time estiamtion by a % amount. As Slic3r only use the marlin algorithm, it's not precise enough if an other firmware is used.");
|
||||||
def->mode = comAdvanced;
|
def->mode = comAdvanced;
|
||||||
def->sidetext = L("%");
|
def->sidetext = L("%");
|
||||||
def->min = 0;
|
def->min = 0;
|
||||||
@ -3986,10 +3984,10 @@ void PrintConfigDef::init_fff_params()
|
|||||||
def->label = L("Tool change G-code");
|
def->label = L("Tool change G-code");
|
||||||
def->category = OptionCategory::customgcode;
|
def->category = OptionCategory::customgcode;
|
||||||
def->tooltip = L("This custom code is inserted at every extruder change. If you don't leave this empty, you are "
|
def->tooltip = L("This custom code is inserted at every extruder change. If you don't leave this empty, you are "
|
||||||
"expected to take care of the toolchange yourself - slic3r will not output any other G-code to "
|
"expected to take care of the toolchange yourself - Slic3r will not output any other G-code to "
|
||||||
"change the filament. You can use placeholder variables for all Slic3r settings as well as [previous_extruder] "
|
"change the filament. You can use placeholder variables for all Slic3r settings as well as [previous_extruder] "
|
||||||
"and [next_extruder], so e.g. the standard toolchange command can be scripted as T[next_extruder]."
|
"and [next_extruder], so e.g. the standard toolchange command can be scripted as T[next_extruder]."
|
||||||
"!! Warning !!: if any charater is written here, slic3r won't output any toochange command by itself.");
|
"!! Warning !!: if any charater is written here, Slic3r won't output any toochange command by itself.");
|
||||||
def->multiline = true;
|
def->multiline = true;
|
||||||
def->full_width = true;
|
def->full_width = true;
|
||||||
def->height = 5;
|
def->height = 5;
|
||||||
@ -4167,7 +4165,7 @@ void PrintConfigDef::init_fff_params()
|
|||||||
|
|
||||||
def = this->add("wipe_advanced", coBool);
|
def = this->add("wipe_advanced", coBool);
|
||||||
def->label = L("Enable advanced wiping volume");
|
def->label = L("Enable advanced wiping volume");
|
||||||
def->tooltip = L("Allow slic3r to compute the purge volume via smart computations. Use the pigment% of each filament and following parameters");
|
def->tooltip = L("Allow Slic3r to compute the purge volume via smart computations. Use the pigment% of each filament and following parameters");
|
||||||
def->mode = comExpert;
|
def->mode = comExpert;
|
||||||
def->set_default_value(new ConfigOptionBool(false));
|
def->set_default_value(new ConfigOptionBool(false));
|
||||||
|
|
||||||
@ -6504,8 +6502,8 @@ CLIMiscConfigDef::CLIMiscConfigDef()
|
|||||||
|
|
||||||
def = this->add("single_instance", coBool);
|
def = this->add("single_instance", coBool);
|
||||||
def->label = L("Single instance mode");
|
def->label = L("Single instance mode");
|
||||||
def->tooltip = L("If enabled, the command line arguments are sent to an existing instance of GUI " SLIC3R_APP_NAME ", "
|
def->tooltip = L("If enabled, the command line arguments are sent to an existing instance of GUI Slic3r, "
|
||||||
"or an existing " SLIC3R_APP_NAME " window is activated. "
|
"or an existing Slic3r window is activated. "
|
||||||
"Overrides the \"single_instance\" configuration value from application preferences.");
|
"Overrides the \"single_instance\" configuration value from application preferences.");
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -1141,15 +1141,15 @@ wxString Preview::get_option_type_string(OptionType type) const
|
|||||||
{
|
{
|
||||||
case OptionType::Travel: { return _L("Travel"); }
|
case OptionType::Travel: { return _L("Travel"); }
|
||||||
case OptionType::Wipe: { return _L("Wipe"); }
|
case OptionType::Wipe: { return _L("Wipe"); }
|
||||||
case OptionType::Retractions: { return _L(m_width_screen == tiny ? "Retr." : "Retractions"); }
|
case OptionType::Retractions: { return m_width_screen == tiny ? _L("Retr.") : _L("Retractions"); }
|
||||||
case OptionType::Unretractions: { return _L(m_width_screen == tiny ? "Dere." : "Deretractions"); }
|
case OptionType::Unretractions: { return m_width_screen == tiny ? _L("Dere.") : _L("Deretractions"); }
|
||||||
case OptionType::ToolChanges: { return _L(m_width_screen == tiny ? "Tool/C" : "Tool changes"); }
|
case OptionType::ToolChanges: { return m_width_screen == tiny ? _L("Tool/C") : _L("Tool changes"); }
|
||||||
case OptionType::ColorChanges: { return _L(m_width_screen == tiny ? "Col/C" : "Color changes"); }
|
case OptionType::ColorChanges: { return m_width_screen == tiny ? _L("Col/C") : _L("Color changes"); }
|
||||||
case OptionType::PausePrints: { return _L(m_width_screen == tiny ? "Pause" : "Print pauses"); }
|
case OptionType::PausePrints: { return m_width_screen == tiny ? _L("Pause") : _L("Print pauses"); }
|
||||||
case OptionType::CustomGCodes: { return _L(m_width_screen == tiny ? "Custom" : "Custom G-codes"); }
|
case OptionType::CustomGCodes: { return m_width_screen == tiny ? _L("Custom") : _L("Custom G-codes"); }
|
||||||
case OptionType::Shells: { return _L("Shells"); }
|
case OptionType::Shells: { return _L("Shells"); }
|
||||||
case OptionType::ToolMarker: { return _L(m_width_screen == tiny ? "Marker" : "Tool marker"); }
|
case OptionType::ToolMarker: { return m_width_screen == tiny ? _L("Marker") : _L("Tool marker"); }
|
||||||
case OptionType::Legend: { return _L(m_width_screen == tiny ? "Legend" : "Legend/Estimated printing time"); }
|
case OptionType::Legend: { return m_width_screen == tiny ? _L("Legend") : _L("Legend/Estimated printing time"); }
|
||||||
default: { return ""; }
|
default: { return ""; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -168,11 +168,11 @@ void PreferencesDialog::build()
|
|||||||
|
|
||||||
|
|
||||||
#if __APPLE__
|
#if __APPLE__
|
||||||
def.label = (L("Allow just a single Slic3r instance")) % SLIC3R_APP_NAME).str();
|
def.label = (boost::format(L("Allow just a single %1% instance")) % SLIC3R_APP_NAME).str();
|
||||||
def.type = coBool;
|
def.type = coBool;
|
||||||
def.tooltip = L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances of same app from the command line. In such case this settings will allow only one instance.");
|
def.tooltip = L("On OSX there is always only one instance of app running by default. However it is allowed to run multiple instances of same app from the command line. In such case this settings will allow only one instance.");
|
||||||
#else
|
#else
|
||||||
def.label = (boost::format(L("Allow just a single Slic3r instance")) % SLIC3R_APP_NAME).str();
|
def.label = (boost::format(L("Allow just a single %1% instance")) % SLIC3R_APP_NAME).str();
|
||||||
def.type = coBool;
|
def.type = coBool;
|
||||||
def.tooltip = L("If this is enabled, when starting Slic3r and another instance of the same Slic3r is already running, that instance will be reactivated instead.");
|
def.tooltip = L("If this is enabled, when starting Slic3r and another instance of the same Slic3r is already running, that instance will be reactivated instead.");
|
||||||
#endif
|
#endif
|
||||||
@ -211,9 +211,9 @@ void PreferencesDialog::build()
|
|||||||
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
#if ENABLE_CUSTOMIZABLE_FILES_ASSOCIATION_ON_WIN
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
else {
|
else {
|
||||||
def.label = (boost::format(L("Associate .gcode files to Slic3r G-code Viewer")) % SLIC3R_APP_NAME).str();
|
def.label = (boost::format(L("Associate .gcode files to %1%")) % GCODEVIEWER_APP_NAME).str();
|
||||||
def.type = coBool;
|
def.type = coBool;
|
||||||
def.tooltip = L("If enabled, sets Slic3r G-code Viewer as default application to open .gcode files.");
|
def.tooltip = (boost::format(L("If enabled, sets %1% as default application to open .gcode files.")) % GCODEVIEWER_APP_NAME).str();
|
||||||
def.set_default_value(new ConfigOptionBool(app_config->get("associate_gcode") == "1"));
|
def.set_default_value(new ConfigOptionBool(app_config->get("associate_gcode") == "1"));
|
||||||
option = Option(def, "associate_gcode");
|
option = Option(def, "associate_gcode");
|
||||||
m_optgroup_general->append_single_option_line(option);
|
m_optgroup_general->append_single_option_line(option);
|
||||||
|
@ -178,7 +178,7 @@ const char* SL1Host::get_name() const { return "SL1Host"; }
|
|||||||
|
|
||||||
wxString SL1Host::get_test_ok_msg () const
|
wxString SL1Host::get_test_ok_msg () const
|
||||||
{
|
{
|
||||||
return _(L("Connection to Prusa SL1 works correctly."));
|
return wxString::Format(_L("Connection to %s works correctly."), "Prusa SL1");
|
||||||
}
|
}
|
||||||
|
|
||||||
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
wxString SL1Host::get_test_failed_msg (wxString &msg) const
|
||||||
|
Loading…
x
Reference in New Issue
Block a user