diff --git a/core/ustring.cpp b/core/ustring.cpp
index 3013aa8bbc5..251981abf87 100644
--- a/core/ustring.cpp
+++ b/core/ustring.cpp
@@ -3144,6 +3144,27 @@ CharType String::ord_at(int p_idx) const {
return operator[](p_idx);
}
+String String::indent(const String &p_prefix) const {
+ String new_string;
+ int line_start = 0;
+
+ for (int i = 0; i < length(); i++) {
+ const char32_t c = operator[](i);
+ if (c == '\n') {
+ if (i == line_start) {
+ new_string += c; // Leave empty lines empty.
+ } else {
+ new_string += p_prefix + substr(line_start, i - line_start + 1);
+ }
+ line_start = i + 1;
+ }
+ }
+ if (line_start != length()) {
+ new_string += p_prefix + substr(line_start);
+ }
+ return new_string;
+}
+
String String::dedent() const {
String new_string;
String indent;
diff --git a/core/ustring.h b/core/ustring.h
index bec9163674a..14b9ab7b809 100644
--- a/core/ustring.h
+++ b/core/ustring.h
@@ -286,6 +286,7 @@ public:
String left(int p_pos) const;
String right(int p_pos) const;
+ String indent(const String &p_prefix) const;
String dedent() const;
String strip_edges(bool left = true, bool right = true) const;
String strip_escapes() const;
diff --git a/core/variant_call.cpp b/core/variant_call.cpp
index 44f84479e8b..c0f6da47ecf 100644
--- a/core/variant_call.cpp
+++ b/core/variant_call.cpp
@@ -272,6 +272,7 @@ struct _VariantCall {
VCALL_LOCALMEM0R(String, to_lower);
VCALL_LOCALMEM1R(String, left);
VCALL_LOCALMEM1R(String, right);
+ VCALL_LOCALMEM1R(String, indent);
VCALL_LOCALMEM0R(String, dedent);
VCALL_LOCALMEM2R(String, strip_edges);
VCALL_LOCALMEM0R(String, strip_escapes);
@@ -1681,6 +1682,7 @@ void register_variant_methods() {
ADDFUNC0R(STRING, STRING, String, get_basename, varray());
ADDFUNC1R(STRING, STRING, String, plus_file, STRING, "file", varray());
ADDFUNC1R(STRING, INT, String, ord_at, INT, "at", varray());
+ ADDFUNC1R(STRING, STRING, String, indent, STRING, "prefix", varray());
ADDFUNC0R(STRING, STRING, String, dedent, varray());
ADDFUNC2(STRING, NIL, String, erase, INT, "position", INT, "chars", varray());
ADDFUNC0R(STRING, INT, String, hash, varray());
diff --git a/doc/classes/String.xml b/doc/classes/String.xml
index 2541ed9790a..e7cc6b39082 100644
--- a/doc/classes/String.xml
+++ b/doc/classes/String.xml
@@ -241,7 +241,7 @@
- Returns a copy of the string with indentation (leading tabs and spaces) removed.
+ Returns a copy of the string with indentation (leading tabs and spaces) removed. See also [method indent] to add indentation.
@@ -393,6 +393,15 @@
[/codeblock]
+
+
+
+
+ Returns a copy of the string with lines indented with [code]prefix[/code].
+ For example, the string can be indented with two tabs using [code]"\t\t"[/code], or four spaces using [code]" "[/code]. The prefix can be any string so it can also be used to comment out strings with e.g. [code]"# "[/code]. See also [method dedent] to remove indentation.
+ [b]Note:[/b] Empty lines are kept empty.
+
+
diff --git a/editor/doc/doc_data.cpp b/editor/doc/doc_data.cpp
index 9e007d642eb..095d66c445d 100644
--- a/editor/doc/doc_data.cpp
+++ b/editor/doc/doc_data.cpp
@@ -37,9 +37,39 @@
#include "core/os/dir_access.h"
#include "core/project_settings.h"
#include "core/script_language.h"
+#include "core/translation.h"
#include "core/version.h"
#include "scene/resources/theme.h"
+static String _get_indent(const String &p_text) {
+ String indent;
+ bool has_text = false;
+ int line_start = 0;
+
+ for (int i = 0; i < p_text.length(); i++) {
+ const char32_t c = p_text[i];
+ if (c == '\n') {
+ line_start = i + 1;
+ } else if (c > 32) {
+ has_text = true;
+ indent = p_text.substr(line_start, i - line_start);
+ break; // Indentation of the first line that has text.
+ }
+ }
+ if (!has_text) {
+ return p_text;
+ }
+ return indent;
+}
+
+static String _translate_doc_string(const String &p_text) {
+ const String indent = _get_indent(p_text);
+ const String message = p_text.dedent().strip_edges();
+ const String translated = TranslationServer::get_singleton()->doc_translate(message);
+ // No need to restore stripped edges because they'll be stripped again later.
+ return translated.indent(indent);
+}
+
void DocData::merge_from(const DocData &p_data) {
for (Map::Element *E = class_list.front(); E; E = E->next()) {
ClassDoc &c = E->get();
@@ -1057,11 +1087,11 @@ Error DocData::save_classes(const String &p_default_path, const Map");
- _write_string(f, 2, c.brief_description.strip_edges().xml_escape());
+ _write_string(f, 2, _translate_doc_string(c.brief_description).strip_edges().xml_escape());
_write_string(f, 1, "");
_write_string(f, 1, "");
- _write_string(f, 2, c.description.strip_edges().xml_escape());
+ _write_string(f, 2, _translate_doc_string(c.description).strip_edges().xml_escape());
_write_string(f, 1, "");
_write_string(f, 1, "");
@@ -1110,7 +1140,7 @@ Error DocData::save_classes(const String &p_default_path, const Map");
- _write_string(f, 4, m.description.strip_edges().xml_escape());
+ _write_string(f, 4, _translate_doc_string(m.description).strip_edges().xml_escape());
_write_string(f, 3, "");
_write_string(f, 2, "");
@@ -1138,7 +1168,7 @@ Error DocData::save_classes(const String &p_default_path, const Map");
} else {
_write_string(f, 2, "");
- _write_string(f, 3, p.description.strip_edges().xml_escape());
+ _write_string(f, 3, _translate_doc_string(p.description).strip_edges().xml_escape());
_write_string(f, 2, "");
}
}
@@ -1158,7 +1188,7 @@ Error DocData::save_classes(const String &p_default_path, const Map");
- _write_string(f, 4, m.description.strip_edges().xml_escape());
+ _write_string(f, 4, _translate_doc_string(m.description).strip_edges().xml_escape());
_write_string(f, 3, "");
_write_string(f, 2, "");
@@ -1184,7 +1214,7 @@ Error DocData::save_classes(const String &p_default_path, const Map");
}
}
- _write_string(f, 3, k.description.strip_edges().xml_escape());
+ _write_string(f, 3, _translate_doc_string(k.description).strip_edges().xml_escape());
_write_string(f, 2, "");
}
@@ -1203,7 +1233,7 @@ Error DocData::save_classes(const String &p_default_path, const Map");
}
- _write_string(f, 3, ti.description.strip_edges().xml_escape());
+ _write_string(f, 3, _translate_doc_string(ti.description).strip_edges().xml_escape());
_write_string(f, 2, "");
}
diff --git a/editor/editor_settings.cpp b/editor/editor_settings.cpp
index a672aff6fb2..67145002b86 100644
--- a/editor/editor_settings.cpp
+++ b/editor/editor_settings.cpp
@@ -31,22 +31,18 @@
#include "editor_settings.h"
#include "core/io/certs_compressed.gen.h"
-#include "core/io/compression.h"
#include "core/io/config_file.h"
-#include "core/io/file_access_memory.h"
#include "core/io/ip.h"
#include "core/io/resource_loader.h"
#include "core/io/resource_saver.h"
-#include "core/io/translation_loader_po.h"
#include "core/os/dir_access.h"
#include "core/os/file_access.h"
#include "core/os/keyboard.h"
#include "core/os/os.h"
#include "core/project_settings.h"
#include "core/version.h"
-#include "editor/doc_translations.gen.h"
#include "editor/editor_node.h"
-#include "editor/editor_translations.gen.h"
+#include "editor/editor_translation.h"
#include "scene/main/node.h"
#include "scene/main/scene_tree.h"
#include "scene/main/viewport.h"
@@ -268,17 +264,14 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
const Vector locales_to_skip = String("ar,bn,fa,he,hi,ml,si,ta,te,ur").split(",");
String best;
-
- EditorTranslationList *etl = _editor_translations;
-
- while (etl->data) {
- const String &locale = etl->lang;
+ const Vector &locales = get_editor_locales();
+ for (int i = 0; i < locales.size(); i++) {
+ const String &locale = locales[i];
// Skip locales which we can't render properly (see above comment).
// Test against language code without regional variants (e.g. ur_PK).
String lang_code = locale.get_slice("_", 0);
if (locales_to_skip.find(lang_code) != -1) {
- etl++;
continue;
}
@@ -292,8 +285,6 @@ void EditorSettings::_load_defaults(Ref p_extra_config) {
if (best == String() && host_lang.begins_with(locale)) {
best = locale;
}
-
- etl++;
}
if (best == String()) {
@@ -1009,50 +1000,10 @@ void EditorSettings::setup_language() {
}
// Load editor translation for configured/detected locale.
- EditorTranslationList *etl = _editor_translations;
- while (etl->data) {
- if (etl->lang == lang) {
- Vector data;
- data.resize(etl->uncomp_size);
- Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE);
-
- FileAccessMemory *fa = memnew(FileAccessMemory);
- fa->open_custom(data.ptr(), data.size());
-
- Ref tr = TranslationLoaderPO::load_translation(fa);
-
- if (tr.is_valid()) {
- tr->set_locale(etl->lang);
- TranslationServer::get_singleton()->set_tool_translation(tr);
- break;
- }
- }
-
- etl++;
- }
+ load_editor_translations(lang);
// Load class reference translation.
- DocTranslationList *dtl = _doc_translations;
- while (dtl->data) {
- if (dtl->lang == lang) {
- Vector data;
- data.resize(dtl->uncomp_size);
- Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE);
-
- FileAccessMemory *fa = memnew(FileAccessMemory);
- fa->open_custom(data.ptr(), data.size());
-
- Ref tr = TranslationLoaderPO::load_translation(fa);
-
- if (tr.is_valid()) {
- tr->set_locale(dtl->lang);
- TranslationServer::get_singleton()->set_doc_translation(tr);
- break;
- }
- }
-
- dtl++;
- }
+ load_doc_translations(lang);
}
void EditorSettings::setup_network() {
diff --git a/editor/editor_translation.cpp b/editor/editor_translation.cpp
new file mode 100644
index 00000000000..23145c27c81
--- /dev/null
+++ b/editor/editor_translation.cpp
@@ -0,0 +1,99 @@
+/*************************************************************************/
+/* editor_translation.cpp */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#include "editor/editor_translation.h"
+
+#include "core/io/compression.h"
+#include "core/io/file_access_memory.h"
+#include "core/io/translation_loader_po.h"
+#include "editor/doc_translations.gen.h"
+#include "editor/editor_translations.gen.h"
+
+Vector get_editor_locales() {
+ Vector locales;
+
+ EditorTranslationList *etl = _editor_translations;
+ while (etl->data) {
+ const String &locale = etl->lang;
+ locales.push_back(locale);
+
+ etl++;
+ }
+
+ return locales;
+}
+
+void load_editor_translations(const String &p_locale) {
+ EditorTranslationList *etl = _editor_translations;
+ while (etl->data) {
+ if (etl->lang == p_locale) {
+ Vector data;
+ data.resize(etl->uncomp_size);
+ Compression::decompress(data.ptrw(), etl->uncomp_size, etl->data, etl->comp_size, Compression::MODE_DEFLATE);
+
+ FileAccessMemory *fa = memnew(FileAccessMemory);
+ fa->open_custom(data.ptr(), data.size());
+
+ Ref tr = TranslationLoaderPO::load_translation(fa);
+
+ if (tr.is_valid()) {
+ tr->set_locale(etl->lang);
+ TranslationServer::get_singleton()->set_tool_translation(tr);
+ break;
+ }
+ }
+
+ etl++;
+ }
+}
+
+void load_doc_translations(const String &p_locale) {
+ DocTranslationList *dtl = _doc_translations;
+ while (dtl->data) {
+ if (dtl->lang == p_locale) {
+ Vector data;
+ data.resize(dtl->uncomp_size);
+ Compression::decompress(data.ptrw(), dtl->uncomp_size, dtl->data, dtl->comp_size, Compression::MODE_DEFLATE);
+
+ FileAccessMemory *fa = memnew(FileAccessMemory);
+ fa->open_custom(data.ptr(), data.size());
+
+ Ref tr = TranslationLoaderPO::load_translation(fa);
+
+ if (tr.is_valid()) {
+ tr->set_locale(dtl->lang);
+ TranslationServer::get_singleton()->set_doc_translation(tr);
+ break;
+ }
+ }
+
+ dtl++;
+ }
+}
diff --git a/editor/editor_translation.h b/editor/editor_translation.h
new file mode 100644
index 00000000000..43e7fea8f1e
--- /dev/null
+++ b/editor/editor_translation.h
@@ -0,0 +1,41 @@
+/*************************************************************************/
+/* editor_translation.h */
+/*************************************************************************/
+/* This file is part of: */
+/* GODOT ENGINE */
+/* https://godotengine.org */
+/*************************************************************************/
+/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
+/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
+/* */
+/* Permission is hereby granted, free of charge, to any person obtaining */
+/* a copy of this software and associated documentation files (the */
+/* "Software"), to deal in the Software without restriction, including */
+/* without limitation the rights to use, copy, modify, merge, publish, */
+/* distribute, sublicense, and/or sell copies of the Software, and to */
+/* permit persons to whom the Software is furnished to do so, subject to */
+/* the following conditions: */
+/* */
+/* The above copyright notice and this permission notice shall be */
+/* included in all copies or substantial portions of the Software. */
+/* */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
+/*************************************************************************/
+
+#ifndef EDITOR_TRANSLATION_H
+#define EDITOR_TRANSLATION_H
+
+#include "core/ustring.h"
+#include "core/vector.h"
+
+Vector get_editor_locales();
+void load_editor_translations(const String &p_locale);
+void load_doc_translations(const String &p_locale);
+
+#endif // EDITOR_TRANSLATION_H
diff --git a/main/main.cpp b/main/main.cpp
index ae9ad68c3a1..c43980fbaf0 100644
--- a/main/main.cpp
+++ b/main/main.cpp
@@ -76,6 +76,7 @@
#include "editor/doc/doc_data_class_path.gen.h"
#include "editor/editor_node.h"
#include "editor/editor_settings.h"
+#include "editor/editor_translation.h"
#include "editor/progress_dialog.h"
#include "editor/project_manager.h"
#ifndef NO_EDITOR_SPLASH
@@ -1523,7 +1524,6 @@ Error Main::setup2(Thread::ID p_main_tid_override) {
VisualServer::get_singleton()->callbacks_register(visual_server_callbacks);
_start_success = true;
- locale = String();
ClassDB::set_current_api(ClassDB::API_NONE); //no more api is registered at this point
@@ -1631,6 +1631,11 @@ bool Main::start() {
if (doc_tool_path != "") {
Engine::get_singleton()->set_editor_hint(true); // Needed to instance editor-only classes for their default values
+ // Translate the class reference only when `-l LOCALE` parameter is given.
+ if (!locale.empty() && locale != "en") {
+ load_doc_translations(locale);
+ }
+
{
DirAccessRef da = DirAccess::open(doc_tool_path);
ERR_FAIL_COND_V_MSG(!da, false, "Argument supplied to --doctool must be a valid directory path.");