From 97040210c9c80efb0fbb5fde1f17ab363f2fc94c Mon Sep 17 00:00:00 2001 From: Haru Date: Fri, 8 Jul 2016 12:02:25 +0200 Subject: HULD: Improved .po file parser to properly read concatenated strings - For compliance with gettext's .po file specifications Signed-off-by: Haru --- src/map/script.c | 198 +++++++++++++++++++++++++++++++++++++++---------------- src/map/script.h | 1 + 2 files changed, 142 insertions(+), 57 deletions(-) (limited to 'src/map') diff --git a/src/map/script.c b/src/map/script.c index ed38ed1c5..90a70e48d 100644 --- a/src/map/script.c +++ b/src/map/script.c @@ -4960,6 +4960,78 @@ const char *script_get_translation_file_name(const char *file) return basename; } +/** + * Parses and adds a translated string to the translations database. + * + * @param file Translations file being parsed (for error messages). + * @param lang_id Language ID being parsed. + * @param msgctxt Message context (i.e. NPC name) + * @param msgid Message ID (source string) + * @param msgstr Translated message + * @return success state + * @retval true if a new string was added. + */ +bool script_load_translation_addstring(const char *file, uint8 lang_id, const char *msgctxt, const struct script_string_buf *msgid, const struct script_string_buf *msgstr) +{ + nullpo_retr(false, file); + nullpo_retr(false, msgctxt); + nullpo_retr(false, msgid); + nullpo_retr(false, msgstr); + + if (VECTOR_LENGTH(*msgid) <= 1) { + // Empty ID (i.e. header) to be ignored + return false; + } + + if (VECTOR_LENGTH(*msgstr) <= 1) { + // Empty (untranslated) string to be ignored + return false; + } + + if (msgctxt[0] == '\0') { + // Missing context + ShowWarning("script_load_translation: Missing context for msgid '%s' in '%s'. Skipping.\n", + VECTOR_DATA(*msgid), file); + return false; + } + + if (strcasecmp(msgctxt, "messages.conf") == 0) { + int i; + for (i = 0; i < MAX_MSG; i++) { + if (atcommand->msg_table[0][i] != NULL && strcmpi(atcommand->msg_table[0][i], VECTOR_DATA(*msgid)) == 0) { + if (atcommand->msg_table[lang_id][i] != NULL) + aFree(atcommand->msg_table[lang_id][i]); + atcommand->msg_table[lang_id][i] = aStrdup(VECTOR_DATA(*msgstr)); + break; + } + } + } else { + int msgstr_len = VECTOR_LENGTH(*msgstr); + int inner_len = 1 + msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0' + struct string_translation *st = NULL; + struct DBMap *string_db; + + if ((string_db = strdb_get(script->translation_db, msgctxt)) == NULL) { + string_db = strdb_alloc(DB_OPT_DUP_KEY, 0); + strdb_put(script->translation_db, msgctxt, string_db); + } + + if ((st = strdb_get(string_db, VECTOR_DATA(*msgid))) == NULL) { + CREATE(st, struct string_translation, 1); + st->string_id = script->string_dup(VECTOR_DATA(*msgid)); + strdb_put(string_db, VECTOR_DATA(*msgid), st); + } + RECREATE(st->buf, uint8, st->len + inner_len); + + WBUFB(st->buf, st->len) = lang_id; + safestrncpy(WBUFP(st->buf, st->len + 1), VECTOR_DATA(*msgstr), msgstr_len + 1); + + st->translations++; + st->len += inner_len; + } + return true; +} + /** * Parses an individual translation file. * @@ -4972,12 +5044,11 @@ int script_load_translation(const char *file, uint8 lang_id) int translations = 0; char line[1024]; char msgctxt[NAME_LENGTH*2+1] = { 0 }; - struct DBMap *string_db; - size_t i; FILE *fp; + int lineno = 0; struct script_string_buf msgid, msgstr; - if( !(fp = fopen(file,"rb")) ) { + if ((fp = fopen(file,"rb")) == NULL) { ShowError("load_translation: failed to open '%s' for reading\n",file); return 0; } @@ -4986,36 +5057,73 @@ int script_load_translation(const char *file, uint8 lang_id) VECTOR_INIT(msgstr); script->add_language(script->get_translation_file_name(file)); - if( lang_id >= atcommand->max_message_table ) + if (lang_id >= atcommand->max_message_table) atcommand->expand_message_table(); - while(fgets(line, sizeof(line), fp)) { - size_t len = strlen(line); + while (fgets(line, sizeof(line), fp) != NULL) { + int len = (int)strlen(line); + int i; + lineno++; - if( len <= 1 ) + if(len <= 1) continue; - if( line[0] == '#' ) + if (line[0] == '#') continue; - if( strncasecmp(line,"msgctxt \"", 9) == 0 ) { + if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + if (line[0] == '"') { + // Continuation line + (void)VECTOR_POP(msgstr); // Pop final '\0' + for (i = 8; i < len - 2; i++) { + VECTOR_ENSURE(msgstr, 1, 512); + if (line[i] == '\\' && line[i+1] == '"') { + VECTOR_PUSH(msgstr, '"'); + i++; + } else { + VECTOR_PUSH(msgstr, line[i]); + } + } + VECTOR_ENSURE(msgstr, 1, 512); + VECTOR_PUSH(msgstr, '\0'); + continue; + } + + // Add string + if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) + translations++; + + msgctxt[0] = '\0'; + VECTOR_TRUNCATE(msgid); + VECTOR_TRUNCATE(msgstr); + } + + if (strncasecmp(line,"msgctxt \"", 9) == 0) { int cursor = 0; msgctxt[0] = '\0'; - for(i = 9; i < len - 2; i++) { - if( line[i] == '\\' && line[i+1] == '"' ) { + for (i = 9; i < len - 2; i++) { + if (line[i] == '\\' && line[i+1] == '"') { msgctxt[cursor] = '"'; i++; - } else + } else { msgctxt[cursor] = line[i]; + } if (++cursor >= (int)sizeof(msgctxt) - 1) break; } msgctxt[cursor] = '\0'; - } else if ( strncasecmp(line, "msgid \"", 7) == 0 ) { + + // New context, reset everything VECTOR_TRUNCATE(msgid); - for(i = 7; i < len - 2; i++) { + VECTOR_TRUNCATE(msgstr); + continue; + } + + if (strncasecmp(line, "msgid \"", 7) == 0) { + VECTOR_TRUNCATE(msgid); + for (i = 7; i < len - 2; i++) { VECTOR_ENSURE(msgid, 1, 512); - if( line[i] == '\\' && line[i+1] == '"' ) { + if (line[i] == '\\' && line[i+1] == '"') { VECTOR_PUSH(msgid, '"'); i++; } else { @@ -5024,11 +5132,17 @@ int script_load_translation(const char *file, uint8 lang_id) } VECTOR_ENSURE(msgid, 1, 512); VECTOR_PUSH(msgid, '\0'); - } else if ( len > 9 && line[9] != '"' && strncasecmp(line, "msgstr \"",8) == 0 ) { + + // New id, reset string if any VECTOR_TRUNCATE(msgstr); - for(i = 8; i < len - 2; i++) { + continue; + } + + if (VECTOR_LENGTH(msgid) > 0 && strncasecmp(line, "msgstr \"", 8) == 0) { + VECTOR_TRUNCATE(msgstr); + for (i = 8; i < len - 2; i++) { VECTOR_ENSURE(msgstr, 1, 512); - if( line[i] == '\\' && line[i+1] == '"' ) { + if (line[i] == '\\' && line[i+1] == '"') { VECTOR_PUSH(msgstr, '"'); i++; } else { @@ -5037,49 +5151,18 @@ int script_load_translation(const char *file, uint8 lang_id) } VECTOR_ENSURE(msgstr, 1, 512); VECTOR_PUSH(msgstr, '\0'); - } - if( msgctxt[0] && VECTOR_LENGTH(msgid) > 1 && VECTOR_LENGTH(msgstr) > 1 ) { - int msgstr_len = VECTOR_LENGTH(msgstr); - unsigned int inner_len = 1 + (uint32)msgstr_len + 1; //uint8 lang_id + msgstr_len + '\0' - - if( strcasecmp(msgctxt, "messages.conf") == 0 ) { - int k; - - for(k = 0; k < MAX_MSG; k++) { - if( atcommand->msg_table[0][k] && strcmpi(atcommand->msg_table[0][k], VECTOR_DATA(msgid)) == 0 ) { - if( atcommand->msg_table[lang_id][k] ) - aFree(atcommand->msg_table[lang_id][k]); - atcommand->msg_table[lang_id][k] = aStrdup(VECTOR_DATA(msgstr)); - break; - } - } - } else { - struct string_translation *st = NULL; - - if( !( string_db = strdb_get(script->translation_db, msgctxt) ) ) { - string_db = strdb_alloc(DB_OPT_DUP_KEY, 0); - strdb_put(script->translation_db, msgctxt, string_db); - } - - if ((st = strdb_get(string_db, VECTOR_DATA(msgid))) == NULL) { - CREATE(st, struct string_translation, 1); - st->string_id = script->string_dup(VECTOR_DATA(msgid)); - strdb_put(string_db, VECTOR_DATA(msgid), st); - } - RECREATE(st->buf, uint8, st->len + inner_len); + continue; + } - WBUFB(st->buf, st->len) = lang_id; - safestrncpy(WBUFP(st->buf, st->len + 1), VECTOR_DATA(msgstr), msgstr_len + 1); + ShowWarning("script_load_translation: Unexpected input at '%s' in file '%s' line %d. Skipping.\n", + line, file, lineno); + } - st->translations++; - st->len += inner_len; - } - msgctxt[0] = '\0'; - VECTOR_TRUNCATE(msgid); - VECTOR_TRUNCATE(msgstr); + // Add last string + if (VECTOR_LENGTH(msgid) > 0 && VECTOR_LENGTH(msgstr) > 0) { + if (script->load_translation_addstring(file, lang_id, msgctxt, &msgid, &msgstr)) translations++; - } } fclose(fp); @@ -21429,6 +21512,7 @@ void script_defaults(void) { script->mapindexname2id = script_mapindexname2id; script->string_dup = script_string_dup; script->load_translations = script_load_translations; + script->load_translation_addstring = script_load_translation_addstring; script->load_translation = script_load_translation; script->translation_db_destroyer = script_translation_db_destroyer; script->clear_translations = script_clear_translations; diff --git a/src/map/script.h b/src/map/script.h index 4df8941b7..86cb20226 100644 --- a/src/map/script.h +++ b/src/map/script.h @@ -816,6 +816,7 @@ struct script_interface { unsigned short (*mapindexname2id) (struct script_state *st, const char* name); int (*string_dup) (char *str); void (*load_translations) (void); + bool (*load_translation_addstring) (const char *file, uint8 lang_id, const char *msgctxt, const struct script_string_buf *msgid, const struct script_string_buf *msgstr); int (*load_translation) (const char *file, uint8 lang_id); int (*translation_db_destroyer) (union DBKey key, struct DBData *data, va_list ap); void (*clear_translations) (bool reload); -- cgit v1.2.3-70-g09d2