mirror of
https://github.com/Relintai/godot_translation_editor.git
synced 2024-11-20 08:37:18 +01:00
Added support for string prefix and json files
This commit is contained in:
parent
9bb25b67e9
commit
c01422d4b7
@ -16,21 +16,23 @@ var _time_before := 0.0
|
||||
var _ignored_paths := {}
|
||||
var _paths := []
|
||||
var _logger = Logger.get_for(self)
|
||||
var _prefix := ""
|
||||
const _prefix_exclusive := true
|
||||
|
||||
|
||||
func extract_async(root: String, ignored_paths := []):
|
||||
_prepare(root, ignored_paths)
|
||||
func extract_async(root: String, ignored_paths := [], prefix := ""):
|
||||
_prepare(root, ignored_paths, prefix)
|
||||
_thread = Thread.new()
|
||||
_thread.start(self, "_extract_thread_func", root)
|
||||
|
||||
|
||||
func extract(root: String, ignored_paths := []) -> Dictionary:
|
||||
_prepare(root, ignored_paths)
|
||||
func extract(root: String, ignored_paths := [], prefix := "") -> Dictionary:
|
||||
_prepare(root, ignored_paths, prefix)
|
||||
_extract(root)
|
||||
return _strings
|
||||
|
||||
|
||||
func _prepare(root: String, ignored_paths: Array):
|
||||
func _prepare(root: String, ignored_paths: Array, prefix: String):
|
||||
_time_before = OS.get_ticks_msec()
|
||||
assert(_thread == null)
|
||||
|
||||
@ -38,6 +40,8 @@ func _prepare(root: String, ignored_paths: Array):
|
||||
for p in ignored_paths:
|
||||
_ignored_paths[root.plus_file(p)] = true
|
||||
|
||||
_prefix = prefix
|
||||
|
||||
_strings.clear()
|
||||
|
||||
|
||||
@ -57,6 +61,10 @@ func _extract(root: String):
|
||||
_process_tscn(f, fpath)
|
||||
"gd":
|
||||
_process_gd(f, fpath)
|
||||
"json":
|
||||
_process_quoted_text_generic(f, fpath)
|
||||
"cs":
|
||||
_process_quoted_text_generic(f, fpath)
|
||||
f.close()
|
||||
call_deferred("_report_progress", float(i) / float(len(_paths)))
|
||||
|
||||
@ -95,12 +103,19 @@ func _index_file(fpath: String):
|
||||
|
||||
|
||||
func _process_tscn(f: File, fpath: String):
|
||||
# TOOD Also search for "window_title" and "dialog_text"
|
||||
var patterns := [
|
||||
"text =",
|
||||
"window_title =",
|
||||
"dialog_text ="
|
||||
"dialog_text =",
|
||||
]
|
||||
|
||||
if _prefix != "":
|
||||
var p = str("\"", _prefix)
|
||||
if _prefix_exclusive:
|
||||
patterns = [p]
|
||||
else:
|
||||
patterns.append(p)
|
||||
|
||||
var text := ""
|
||||
var state := STATE_SEARCHING
|
||||
var line_number := 0
|
||||
@ -115,22 +130,29 @@ func _process_tscn(f: File, fpath: String):
|
||||
match state:
|
||||
STATE_SEARCHING:
|
||||
var pattern : String
|
||||
var i : int
|
||||
var pattern_begin_index : int = -1
|
||||
|
||||
for p in patterns:
|
||||
i = line.find(p)
|
||||
if i != -1:
|
||||
var i := line.find(p)
|
||||
if i != -1 and (i < pattern_begin_index or pattern_begin_index == -1):
|
||||
pattern_begin_index = i
|
||||
pattern = p
|
||||
break
|
||||
|
||||
if i == -1:
|
||||
if pattern_begin_index == -1:
|
||||
continue
|
||||
|
||||
var begin_quote_index := line.find('"', i + len(pattern))
|
||||
if begin_quote_index == -1:
|
||||
_logger.error(
|
||||
"Could not find begin quote after text property, in {0}, line {1}" \
|
||||
.format([fpath, line_number]))
|
||||
continue
|
||||
var begin_quote_index := -1
|
||||
|
||||
if pattern[0] == "\"":
|
||||
begin_quote_index = pattern_begin_index
|
||||
|
||||
else:
|
||||
begin_quote_index = line.find('"', pattern_begin_index + len(pattern))
|
||||
if begin_quote_index == -1:
|
||||
_logger.error(
|
||||
"Could not find begin quote after text property, in {0}, line {1}" \
|
||||
.format([fpath, line_number]))
|
||||
continue
|
||||
|
||||
var end_quote_index := line.rfind('"')
|
||||
|
||||
@ -139,7 +161,7 @@ func _process_tscn(f: File, fpath: String):
|
||||
text = line.substr(begin_quote_index + 1,
|
||||
end_quote_index - begin_quote_index - 1)
|
||||
|
||||
if text != "":
|
||||
if text != "" and text != _prefix:
|
||||
_add_string(fpath, line_number, text)
|
||||
text = ""
|
||||
|
||||
@ -163,6 +185,18 @@ func _process_gd(f: File, fpath: String):
|
||||
var text := ""
|
||||
var line_number := 0
|
||||
|
||||
var patterns := [
|
||||
"tr(",
|
||||
"TranslationServer.translate("
|
||||
]
|
||||
|
||||
if _prefix != "":
|
||||
var p = str("\"", _prefix)
|
||||
if _prefix_exclusive:
|
||||
patterns = [p]
|
||||
else:
|
||||
patterns.append(p)
|
||||
|
||||
while not f.eof_reached():
|
||||
var line := f.get_line().strip_edges()
|
||||
line_number += 1
|
||||
@ -173,49 +207,92 @@ func _process_gd(f: File, fpath: String):
|
||||
# Search for one or multiple tr("...") in the same line
|
||||
var search_index := 0
|
||||
var counter := 0
|
||||
while true:
|
||||
var pattern := "tr("
|
||||
var call_index := line.find(pattern, search_index)
|
||||
if call_index == -1:
|
||||
pattern = "TranslationServer.translate("
|
||||
call_index = line.find(pattern, search_index)
|
||||
if call_index == -1:
|
||||
break
|
||||
while search_index < len(line):
|
||||
# Find closest pattern
|
||||
var pattern : String
|
||||
var pattern_start_index := -1
|
||||
for p in patterns:
|
||||
var i = line.find(p, search_index)
|
||||
if i != -1 and (i < pattern_start_index or pattern_start_index == -1):
|
||||
pattern_start_index = i
|
||||
pattern = p
|
||||
|
||||
if call_index != 0:
|
||||
if line.substr(call_index - 1, 3).is_valid_identifier():
|
||||
# not a tr( call, skip
|
||||
search_index = call_index + len(pattern)
|
||||
if pattern_start_index == -1:
|
||||
# No pattern found in entire line
|
||||
break
|
||||
|
||||
var begin_quote_index = -1
|
||||
if pattern[0] == "\"":
|
||||
# Detected by prefix
|
||||
begin_quote_index = pattern_start_index
|
||||
|
||||
else:
|
||||
# Detect by call to TranslationServer
|
||||
if line.substr(pattern_start_index - 1, 3).is_valid_identifier() \
|
||||
or line[pattern_start_index - 1] == '"':
|
||||
# not a tr( call, or inside a string. skip
|
||||
search_index = pattern_start_index + len(pattern)
|
||||
continue
|
||||
if line[call_index - 1] == '"':
|
||||
break
|
||||
# TODO There may be more cases to handle
|
||||
# They may need regexes or a simplified GDScript parser to extract properly
|
||||
|
||||
var begin_quote_index := line.find('"', call_index)
|
||||
if begin_quote_index == -1:
|
||||
# Multiline or procedural strings not supported
|
||||
_logger.error("Begin quote not found in {0}, line {1}".format([fpath, line_number]))
|
||||
break
|
||||
begin_quote_index = line.find('"', pattern_start_index)
|
||||
if begin_quote_index == -1:
|
||||
# Multiline or procedural strings not supported
|
||||
_logger.error("Begin quote not found in {0}, line {1}" \
|
||||
.format([fpath, line_number]))
|
||||
# No quote found in entire line, skip
|
||||
break
|
||||
|
||||
var end_quote_index := find_unescaped_quote(line, begin_quote_index + 1)
|
||||
if end_quote_index == -1:
|
||||
# Multiline or procedural strings not supported
|
||||
_logger.error("End quote not found in {0}, line {1}".format([fpath, line_number]))
|
||||
break
|
||||
|
||||
text = line.substr(begin_quote_index + 1, end_quote_index - begin_quote_index - 1)
|
||||
var end_bracket_index := line.find(')', end_quote_index)
|
||||
if end_bracket_index == -1:
|
||||
# Multiline or procedural strings not supported
|
||||
_logger.error("End bracket not found in {0}, line {1}".format([fpath, line_number]))
|
||||
break
|
||||
_add_string(fpath, line_number, text)
|
||||
search_index = end_bracket_index
|
||||
# var end_bracket_index := line.find(')', end_quote_index)
|
||||
# if end_bracket_index == -1:
|
||||
# # Multiline or procedural strings not supported
|
||||
# _logger.error("End bracket not found in {0}, line {1}".format([fpath, line_number]))
|
||||
# break
|
||||
|
||||
if text != "" and text != _prefix:
|
||||
_add_string(fpath, line_number, text)
|
||||
# search_index = end_bracket_index
|
||||
search_index = end_quote_index + 1
|
||||
|
||||
counter += 1
|
||||
# If that fails it means we spent 100 iterations in the same line, that's suspicious
|
||||
assert(counter < 100)
|
||||
|
||||
|
||||
func _process_quoted_text_generic(f: File, fpath: String):
|
||||
var pattern := str("\"", _prefix)
|
||||
var line_number := 0
|
||||
|
||||
while not f.eof_reached():
|
||||
var line := f.get_line().strip_edges()
|
||||
line_number += 1
|
||||
|
||||
var search_index := 0
|
||||
while search_index < len(line):
|
||||
var i := line.find(pattern, search_index)
|
||||
if i == -1:
|
||||
break
|
||||
|
||||
var begin_quote_index := i
|
||||
var end_quote_index := find_unescaped_quote(line, begin_quote_index + 1)
|
||||
if end_quote_index == -1:
|
||||
break
|
||||
|
||||
var text := line.substr(begin_quote_index + 1, end_quote_index - begin_quote_index - 1)
|
||||
if text != "" and text != _prefix:
|
||||
_add_string(fpath, line_number, text)
|
||||
|
||||
search_index = end_quote_index + 1
|
||||
|
||||
|
||||
static func find_unescaped_quote(s, from) -> int:
|
||||
while true:
|
||||
var i = s.find('"', from)
|
||||
|
@ -6,13 +6,14 @@ const Logger = preload("./util/logger.gd")
|
||||
|
||||
signal import_selected(strings)
|
||||
|
||||
onready var _root_path_edit : LineEdit = $VBoxContainer/HBoxContainer/RootPathEdit
|
||||
onready var _excluded_dirs_edit : LineEdit = $VBoxContainer/Options/ExcludedDirsEdit
|
||||
onready var _summary_label : Label = $VBoxContainer/StatusBar/SummaryLabel
|
||||
onready var _results_list : Tree = $VBoxContainer/Results
|
||||
onready var _progress_bar : ProgressBar = $VBoxContainer/StatusBar/ProgressBar
|
||||
onready var _extract_button : Button = $VBoxContainer/Buttons/ExtractButton
|
||||
onready var _import_button : Button = $VBoxContainer/Buttons/ImportButton
|
||||
onready var _root_path_edit : LineEdit = $VB/HB/RootPathEdit
|
||||
onready var _excluded_dirs_edit : LineEdit = $VB/HB2/ExcludedDirsEdit
|
||||
onready var _prefix_edit : LineEdit = $VB/HB3/PrefixLineEdit
|
||||
onready var _summary_label : Label = $VB/StatusBar/SummaryLabel
|
||||
onready var _results_list : Tree = $VB/Results
|
||||
onready var _progress_bar : ProgressBar = $VB/StatusBar/ProgressBar
|
||||
onready var _extract_button : Button = $VB/Buttons/ExtractButton
|
||||
onready var _import_button : Button = $VB/Buttons/ImportButton
|
||||
|
||||
var _extractor : Extractor = null
|
||||
# { string => { fpath => line number } }
|
||||
@ -57,10 +58,12 @@ func _on_ExtractButton_pressed():
|
||||
for i in len(excluded_dirs):
|
||||
excluded_dirs[i] = excluded_dirs[i].strip_edges()
|
||||
|
||||
var prefix := _prefix_edit.text.strip_edges()
|
||||
|
||||
_extractor = Extractor.new()
|
||||
_extractor.connect("progress_reported", self, "_on_Extractor_progress_reported")
|
||||
_extractor.connect("finished", self, "_on_Extractor_finished")
|
||||
_extractor.extract_async(root, excluded_dirs)
|
||||
_extractor.extract_async(root, excluded_dirs, prefix)
|
||||
|
||||
_progress_bar.value = 0
|
||||
_progress_bar.show()
|
||||
|
@ -3,6 +3,7 @@
|
||||
[ext_resource path="res://addons/zylann.translation_editor/tools/extractor_dialog.gd" type="Script" id=1]
|
||||
|
||||
[node name="ExtractorDialog" type="WindowDialog"]
|
||||
visible = true
|
||||
margin_left = 54.0
|
||||
margin_top = 68.0
|
||||
margin_right = 694.0
|
||||
@ -11,44 +12,50 @@ rect_min_size = Vector2( 640, 480 )
|
||||
window_title = "String extractor"
|
||||
resizable = true
|
||||
script = ExtResource( 1 )
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="VBoxContainer" type="VBoxContainer" parent="."]
|
||||
[node name="VB" type="VBoxContainer" parent="."]
|
||||
anchor_right = 1.0
|
||||
anchor_bottom = 1.0
|
||||
margin_left = 8.0
|
||||
margin_top = 8.0
|
||||
margin_right = -8.0
|
||||
margin_bottom = -8.0
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="HBoxContainer" type="HBoxContainer" parent="VBoxContainer"]
|
||||
[node name="HB" type="HBoxContainer" parent="VB"]
|
||||
margin_right = 624.0
|
||||
margin_bottom = 24.0
|
||||
|
||||
[node name="Label" type="Label" parent="VBoxContainer/HBoxContainer"]
|
||||
[node name="Label" type="Label" parent="VB/HB"]
|
||||
margin_top = 5.0
|
||||
margin_right = 29.0
|
||||
margin_bottom = 19.0
|
||||
text = "Root"
|
||||
|
||||
[node name="RootPathEdit" type="LineEdit" parent="VBoxContainer/HBoxContainer"]
|
||||
[node name="RootPathEdit" type="LineEdit" parent="VB/HB"]
|
||||
margin_left = 33.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
text = "res://"
|
||||
|
||||
[node name="Options" type="HBoxContainer" parent="VBoxContainer"]
|
||||
[node name="HB2" type="HBoxContainer" parent="VB"]
|
||||
margin_top = 28.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 52.0
|
||||
|
||||
[node name="ExcludedDirsLabel" type="Label" parent="VBoxContainer/Options"]
|
||||
[node name="ExcludedDirsLabel" type="Label" parent="VB/HB2"]
|
||||
margin_top = 5.0
|
||||
margin_right = 122.0
|
||||
margin_bottom = 19.0
|
||||
text = "Ignored directories"
|
||||
|
||||
[node name="ExcludedDirsEdit" type="LineEdit" parent="VBoxContainer/Options"]
|
||||
[node name="ExcludedDirsEdit" type="LineEdit" parent="VB/HB2"]
|
||||
margin_left = 126.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 24.0
|
||||
@ -56,69 +63,89 @@ hint_tooltip = "Directories seperated by semicolons `;`"
|
||||
size_flags_horizontal = 3
|
||||
text = "addons"
|
||||
|
||||
[node name="StatusBar" type="Control" parent="VBoxContainer"]
|
||||
[node name="HB3" type="HBoxContainer" parent="VB"]
|
||||
margin_top = 56.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 80.0
|
||||
|
||||
[node name="Label" type="Label" parent="VB/HB3"]
|
||||
margin_top = 5.0
|
||||
margin_right = 36.0
|
||||
margin_bottom = 19.0
|
||||
text = "Prefix"
|
||||
|
||||
[node name="PrefixLineEdit" type="LineEdit" parent="VB/HB3"]
|
||||
margin_left = 40.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 24.0
|
||||
size_flags_horizontal = 3
|
||||
|
||||
[node name="StatusBar" type="Control" parent="VB"]
|
||||
margin_top = 84.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 108.0
|
||||
rect_min_size = Vector2( 0, 24 )
|
||||
|
||||
[node name="SummaryLabel" type="Label" parent="VBoxContainer/StatusBar"]
|
||||
[node name="SummaryLabel" type="Label" parent="VB/StatusBar"]
|
||||
margin_top = 4.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 18.0
|
||||
align = 1
|
||||
__meta__ = {
|
||||
"_edit_use_anchors_": false
|
||||
}
|
||||
|
||||
[node name="ProgressBar" type="ProgressBar" parent="VBoxContainer/StatusBar"]
|
||||
[node name="ProgressBar" type="ProgressBar" parent="VB/StatusBar"]
|
||||
visible = false
|
||||
margin_right = 624.0
|
||||
margin_bottom = 16.0
|
||||
step = 1.0
|
||||
|
||||
[node name="Results" type="Tree" parent="VBoxContainer"]
|
||||
margin_top = 84.0
|
||||
[node name="Results" type="Tree" parent="VB"]
|
||||
margin_top = 112.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 416.0
|
||||
size_flags_vertical = 3
|
||||
hide_root = true
|
||||
select_mode = 1
|
||||
|
||||
[node name="Spacer" type="Control" parent="VBoxContainer"]
|
||||
[node name="Spacer" type="Control" parent="VB"]
|
||||
margin_top = 420.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 428.0
|
||||
rect_min_size = Vector2( 0, 8 )
|
||||
|
||||
[node name="Buttons" type="HBoxContainer" parent="VBoxContainer"]
|
||||
[node name="Buttons" type="HBoxContainer" parent="VB"]
|
||||
margin_top = 432.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 452.0
|
||||
custom_constants/separation = 16
|
||||
alignment = 1
|
||||
|
||||
[node name="ExtractButton" type="Button" parent="VBoxContainer/Buttons"]
|
||||
[node name="ExtractButton" type="Button" parent="VB/Buttons"]
|
||||
margin_left = 184.0
|
||||
margin_right = 239.0
|
||||
margin_bottom = 20.0
|
||||
text = "Extract"
|
||||
|
||||
[node name="ImportButton" type="Button" parent="VBoxContainer/Buttons"]
|
||||
[node name="ImportButton" type="Button" parent="VB/Buttons"]
|
||||
margin_left = 255.0
|
||||
margin_right = 370.0
|
||||
margin_bottom = 20.0
|
||||
disabled = true
|
||||
text = "Import selected"
|
||||
|
||||
[node name="CancelButton" type="Button" parent="VBoxContainer/Buttons"]
|
||||
[node name="CancelButton" type="Button" parent="VB/Buttons"]
|
||||
margin_left = 386.0
|
||||
margin_right = 440.0
|
||||
margin_bottom = 20.0
|
||||
text = "Cancel"
|
||||
|
||||
[node name="Spacer2" type="Control" parent="VBoxContainer"]
|
||||
[node name="Spacer2" type="Control" parent="VB"]
|
||||
margin_top = 456.0
|
||||
margin_right = 624.0
|
||||
margin_bottom = 464.0
|
||||
rect_min_size = Vector2( 0, 8 )
|
||||
[connection signal="pressed" from="VBoxContainer/Buttons/ExtractButton" to="." method="_on_ExtractButton_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/Buttons/ImportButton" to="." method="_on_ImportButton_pressed"]
|
||||
[connection signal="pressed" from="VBoxContainer/Buttons/CancelButton" to="." method="_on_CancelButton_pressed"]
|
||||
[connection signal="pressed" from="VB/Buttons/ExtractButton" to="." method="_on_ExtractButton_pressed"]
|
||||
[connection signal="pressed" from="VB/Buttons/ImportButton" to="." method="_on_ImportButton_pressed"]
|
||||
[connection signal="pressed" from="VB/Buttons/CancelButton" to="." method="_on_CancelButton_pressed"]
|
||||
|
Loading…
Reference in New Issue
Block a user