Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[3.x] Add context support for editor translation #59187

Merged
merged 1 commit into from
Mar 16, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 44 additions & 11 deletions core/io/translation_loader_po.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,25 +33,33 @@
#include "core/os/file_access.h"
#include "core/translation.h"

RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
RES TranslationLoaderPO::load_translation(FileAccess *f, bool p_use_context, Error *r_error) {
enum Status {
STATUS_NONE,
STATUS_READING_ID,
STATUS_READING_STRING,
STATUS_READING_CONTEXT,
};

Status status = STATUS_NONE;

String msg_id;
String msg_str;
String msg_context;
String config;

if (r_error) {
*r_error = ERR_FILE_CORRUPT;
}

Ref<Translation> translation = Ref<Translation>(memnew(Translation));
Ref<Translation> translation;
if (p_use_context) {
translation = Ref<Translation>(memnew(ContextTranslation));
} else {
translation.instance();
}
int line = 1;
bool entered_context = false;
bool skip_this = false;
bool skip_next = false;
bool is_eof = false;
Expand All @@ -63,40 +71,62 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {

// If we reached last line and it's not a content line, break, otherwise let processing that last loop
if (is_eof && l.empty()) {
if (status == STATUS_READING_ID) {
if (status == STATUS_READING_ID || status == STATUS_READING_CONTEXT) {
memdelete(f);
ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading 'msgid' at: " + path + ":" + itos(line));
ERR_FAIL_V_MSG(RES(), "Unexpected EOF while reading PO file at: " + path + ":" + itos(line));
} else {
break;
}
}

if (l.begins_with("msgctxt")) {
if (status != STATUS_READING_STRING) {
memdelete(f);
ERR_FAIL_V_MSG(RES(), "Unexpected 'msgctxt', was expecting 'msgstr' before 'msgctxt' while parsing: " + path + ":" + itos(line));
}

// In PO file, "msgctxt" appears before "msgid". If we encounter a "msgctxt", we add what we have read
// and set "entered_context" to true to prevent adding twice.
if (!skip_this && msg_id != "") {
translation->add_context_message(msg_id, msg_str, msg_context);
}
msg_context = "";
l = l.substr(7, l.length()).strip_edges();
status = STATUS_READING_CONTEXT;
entered_context = true;
}

if (l.begins_with("msgid")) {
if (status == STATUS_READING_ID) {
memdelete(f);
ERR_FAIL_V_MSG(RES(), "Unexpected 'msgid', was expecting 'msgstr' while parsing: " + path + ":" + itos(line));
}

if (msg_id != "") {
if (!skip_this) {
translation->add_message(msg_id, msg_str);
if (!skip_this && !entered_context) {
translation->add_context_message(msg_id, msg_str, msg_context);
}
} else if (config == "") {
config = msg_str;
}

l = l.substr(5, l.length()).strip_edges();
status = STATUS_READING_ID;
// If we did not encounter msgctxt, we reset context to empty to reset it.
if (!entered_context) {
msg_context = "";
}
msg_id = "";
msg_str = "";
skip_this = skip_next;
skip_next = false;
entered_context = false;
}

if (l.begins_with("msgstr")) {
if (status != STATUS_READING_ID) {
memdelete(f);
ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' while parsing: " + path + ":" + itos(line));
ERR_FAIL_V_MSG(RES(), "Unexpected 'msgstr', was expecting 'msgid' before 'msgstr' while parsing: " + path + ":" + itos(line));
}

l = l.substr(6, l.length()).strip_edges();
Expand All @@ -108,7 +138,7 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {
skip_next = true;
}
line++;
continue; //nothing to read or comment
continue; // Nothing to read or comment.
}

if (!l.begins_with("\"") || status == STATUS_NONE) {
Expand Down Expand Up @@ -146,19 +176,22 @@ RES TranslationLoaderPO::load_translation(FileAccess *f, Error *r_error) {

if (status == STATUS_READING_ID) {
msg_id += l;
} else {
} else if (status == STATUS_READING_STRING) {
msg_str += l;
} else if (status == STATUS_READING_CONTEXT) {
msg_context += l;
}

line++;
}

memdelete(f);

// Add the last set of data from last iteration.
if (status == STATUS_READING_STRING) {
if (msg_id != "") {
if (!skip_this) {
translation->add_message(msg_id, msg_str);
translation->add_context_message(msg_id, msg_str, msg_context);
}
} else if (config == "") {
config = msg_str;
Expand Down Expand Up @@ -197,7 +230,7 @@ RES TranslationLoaderPO::load(const String &p_path, const String &p_original_pat
FileAccess *f = FileAccess::open(p_path, FileAccess::READ);
ERR_FAIL_COND_V_MSG(!f, RES(), "Cannot open file '" + p_path + "'.");

return load_translation(f, r_error);
return load_translation(f, false, r_error);
}

void TranslationLoaderPO::get_recognized_extensions(List<String> *p_extensions) const {
Expand Down
2 changes: 1 addition & 1 deletion core/io/translation_loader_po.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@

class TranslationLoaderPO : public ResourceFormatLoader {
public:
static RES load_translation(FileAccess *f, Error *r_error = nullptr);
static RES load_translation(FileAccess *f, bool p_use_context, Error *r_error = nullptr);
virtual RES load(const String &p_path, const String &p_original_path = "", Error *r_error = nullptr);
virtual void get_recognized_extensions(List<String> *p_extensions) const;
virtual bool handles_type(const String &p_type) const;
Expand Down
45 changes: 43 additions & 2 deletions core/translation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -870,9 +870,24 @@ void Translation::set_locale(const String &p_locale) {
}
}

void Translation::add_context_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
if (p_context != StringName()) {
WARN_PRINT("Translation class doesn't handle context.");
}
add_message(p_src_text, p_xlated_text);
}

StringName Translation::get_context_message(const StringName &p_src_text, const StringName &p_context) const {
if (p_context != StringName()) {
WARN_PRINT("Translation class doesn't handle context.");
}
return get_message(p_src_text);
}

void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text) {
translation_map[p_src_text] = p_xlated_text;
}

StringName Translation::get_message(const StringName &p_src_text) const {
if (get_script_instance()) {
return get_script_instance()->call("_get_message", p_src_text);
Expand Down Expand Up @@ -923,6 +938,32 @@ Translation::Translation() :

///////////////////////////////////////////////

void ContextTranslation::add_context_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
if (p_context == StringName()) {
add_message(p_src_text, p_xlated_text);
} else {
context_translation_map[p_context][p_src_text] = p_xlated_text;
}
}

StringName ContextTranslation::get_context_message(const StringName &p_src_text, const StringName &p_context) const {
if (p_context == StringName()) {
return get_message(p_src_text);
}

const Map<StringName, Map<StringName, StringName>>::Element *context = context_translation_map.find(p_context);
if (!context) {
return StringName();
}
const Map<StringName, StringName>::Element *message = context->get().find(p_src_text);
if (!message) {
return StringName();
}
return message->get();
}

///////////////////////////////////////////////

bool TranslationServer::is_locale_valid(const String &p_locale) {
const char **ptr = locale_list;

Expand Down Expand Up @@ -1202,9 +1243,9 @@ void TranslationServer::set_tool_translation(const Ref<Translation> &p_translati
tool_translation = p_translation;
}

StringName TranslationServer::tool_translate(const StringName &p_message) const {
StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
if (tool_translation.is_valid()) {
StringName r = tool_translation->get_message(p_message);
StringName r = tool_translation->get_context_message(p_message, p_context);
if (r) {
return r;
}
Expand Down
16 changes: 15 additions & 1 deletion core/translation.h
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,23 @@ class Translation : public Resource {
void get_message_list(List<StringName> *r_messages) const;
int get_message_count() const;

// Not exposed to scripting. For easy usage of `ContextTranslation`.
virtual void add_context_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context);
virtual StringName get_context_message(const StringName &p_src_text, const StringName &p_context) const;

Translation();
};

class ContextTranslation : public Translation {
GDCLASS(ContextTranslation, Translation);

Map<StringName, Map<StringName, StringName>> context_translation_map;

public:
virtual void add_context_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context);
virtual StringName get_context_message(const StringName &p_src_text, const StringName &p_context) const;
};

class TranslationServer : public Object {
GDCLASS(TranslationServer, Object);

Expand Down Expand Up @@ -107,7 +121,7 @@ class TranslationServer : public Object {
static String get_language_code(const String &p_locale);

void set_tool_translation(const Ref<Translation> &p_translation);
StringName tool_translate(const StringName &p_message) const;
StringName tool_translate(const StringName &p_message, const StringName &p_context) const;
void set_doc_translation(const Ref<Translation> &p_translation);
StringName doc_translate(const StringName &p_message) const;

Expand Down
6 changes: 3 additions & 3 deletions core/ustring.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4493,9 +4493,9 @@ String String::unquote() const {
}

#ifdef TOOLS_ENABLED
String TTR(const String &p_text) {
String TTR(const String &p_text, const String &p_context) {
if (TranslationServer::get_singleton()) {
return TranslationServer::get_singleton()->tool_translate(p_text);
return TranslationServer::get_singleton()->tool_translate(p_text, p_context);
}

return p_text;
Expand All @@ -4519,7 +4519,7 @@ String DTR(const String &p_text) {

String RTR(const String &p_text) {
if (TranslationServer::get_singleton()) {
String rtr = TranslationServer::get_singleton()->tool_translate(p_text);
String rtr = TranslationServer::get_singleton()->tool_translate(p_text, StringName());
if (rtr == String() || rtr == p_text) {
return TranslationServer::get_singleton()->translate(p_text);
} else {
Expand Down
2 changes: 1 addition & 1 deletion core/ustring.h
Original file line number Diff line number Diff line change
Expand Up @@ -423,7 +423,7 @@ _FORCE_INLINE_ bool is_str_less(const L *l_ptr, const R *r_ptr) {
// and doc translate for the class reference (DTR).
#ifdef TOOLS_ENABLED
// Gets parsed.
String TTR(const String &);
String TTR(const String &p_text, const String &p_context = "");
String DTR(const String &);
// Use for C strings.
#define TTRC(m_value) (m_value)
Expand Down
4 changes: 2 additions & 2 deletions editor/editor_translation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ void load_editor_translations(const String &p_locale) {
FileAccessMemory *fa = memnew(FileAccessMemory);
fa->open_custom(data.ptr(), data.size());

Ref<Translation> tr = TranslationLoaderPO::load_translation(fa);
Ref<Translation> tr = TranslationLoaderPO::load_translation(fa, true);

if (tr.is_valid()) {
tr->set_locale(etl->lang);
Expand All @@ -87,7 +87,7 @@ void load_doc_translations(const String &p_locale) {
FileAccessMemory *fa = memnew(FileAccessMemory);
fa->open_custom(data.ptr(), data.size());

Ref<Translation> tr = TranslationLoaderPO::load_translation(fa);
Ref<Translation> tr = TranslationLoaderPO::load_translation(fa, false);

if (tr.is_valid()) {
tr->set_locale(dtl->lang);
Expand Down
Loading