From ecd117d80df7954fcf6ded43e787bb7832eb5a9f Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Wed, 25 Sep 2019 15:07:17 +0300 Subject: [PATCH 1/7] Add translation update script --- intl/template.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100755 intl/template.py diff --git a/intl/template.py b/intl/template.py new file mode 100755 index 0000000000..9201f694ab --- /dev/null +++ b/intl/template.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 + +# Apply template (us) updates to translations (fr, ja, chs, etc.) +# Usage: ./template.py xx +# xx is the language code postfix of translation files + +import re +import sys + +try: + lc = sys.argv[1] +except: + print("Usage: ./template.py ") + sys.exit(1) + +p = re.compile('MSG_HASH\(\s*[A-Z0-9_]+,\s*\".*\"\s*\)') + +header = """#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +#if (_MSC_VER >= 1700) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#endif +#pragma warning(disable:4566) +#endif +""" + +def parse_message(message): + key_start = message.find('(') + 1 + key_end = message.find(',') + key = message[key_start:key_end].strip() + value_start = message.find('"') + 1 + value_end = message.rfind('"') + value = message[value_start:value_end].strip() + return key, value + +def messages(text): + result = p.findall(text) + msg_list = [] + for msg in result: + key, val = parse_message(msg) + item = {'key': key, 'val': val, 'msg': msg} + msg_list.append(item) + return msg_list + +def update(translation, template): + new_translation = header + template + template_messages = messages(template) + translation_messages = messages(translation) + for tp_msg in template_messages: + for ts_msg in translation_messages: + if tp_msg['key'] == ts_msg['key']: + new_translation = new_translation.replace(tp_msg['msg'], ts_msg['msg']) + return new_translation + +with open('msg_hash_us.h', 'r') as template_file: + template = template_file.read() + with open('msg_hash_' + lc + '.h', 'r+') as translation_file: + translation = translation_file.read() + new_translation = update(translation, template) + translation_file.seek(0) + translation_file.write(new_translation) + translation_file.truncate() From c4c8778017581d38889a05a7d405e565611ff5b6 Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Tue, 22 Oct 2019 18:08:27 +0300 Subject: [PATCH 2/7] Rebase --- config.def.keybinds.h | 8 +++ intl/msg_hash_us.h | 34 +--------- menu/cbs/menu_cbs_sublabel.c | 32 +++++---- menu/menu_setting.c | 38 +++++++---- msg_hash.h | 1 + retroarch.c | 122 ++++++++++++++++++----------------- 6 files changed, 118 insertions(+), 117 deletions(-) diff --git a/config.def.keybinds.h b/config.def.keybinds.h index 791f4c071c..c2e6591ca2 100644 --- a/config.def.keybinds.h +++ b/config.def.keybinds.h @@ -71,7 +71,11 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_LOAD_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_SAVE_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_FULLSCREEN_TOGGLE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#ifdef HAVE_LAKKA + { true, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#else { true, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#endif { true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_UNKNOWN, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, @@ -153,7 +157,11 @@ static const struct retro_keybind retro_keybinds_1[] = { { true, RARCH_LOAD_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY, RETROK_F4, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_SAVE_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY, RETROK_F2, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_FULLSCREEN_TOGGLE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY, RETROK_f, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#ifdef HAVE_LAKKA + { true, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY, RETROK_ESCAPE, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#else { true, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY, RETROK_ESCAPE, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, +#endif { true, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, RETROK_F7, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, RETROK_F6, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, { true, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND, RETROK_r, NO_BTN, NO_BTN, 0, AXIS_NONE, AXIS_NONE, AXIS_NONE, NULL, NULL }, diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 1e7bf3fc3c..5dbcfe6a49 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1418,17 +1418,14 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_PAUSE_TOGGLE, "Pause toggle" ) -#ifdef HAVE_LAKKA MSG_HASH( - MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY, + MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY, "Restart RetroArch" ) -#else MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY, "Quit RetroArch" ) -#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_INPUT_META_RESET, "Reset game" @@ -2495,12 +2492,6 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_MIDI_SETTINGS, "MIDI" ) -#ifdef HAVE_LAKKA -MSG_HASH( - MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, - "Restart RetroArch" - ) -#else MSG_HASH( MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, "Quit RetroArch" @@ -2656,15 +2647,14 @@ MSG_HASH( ) #ifdef HAVE_LAKKA_SWITCH MSG_HASH( - MENU_ENUM_LABEL_VALUE_REBOOT, + MENU_ENUM_LABEL_VALUE_REBOOT_RCM, "Reboot into RCM" ) -#else +#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_REBOOT, "Reboot" ) -#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_RECORDING_CONFIG_DIRECTORY, "Recording Config" @@ -5945,12 +5935,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_SYSTEM_INFORMATION, "Show information specific to the device." ) -#ifdef HAVE_LAKKA -MSG_HASH( - MENU_ENUM_SUBLABEL_QUIT_RETROARCH, - "Restart the program." - ) -#else MSG_HASH( MENU_ENUM_SUBLABEL_QUIT_RETROARCH, "Quit the program." @@ -5959,7 +5943,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_RESTART_RETROARCH, "Restart the program." ) -#endif MSG_HASH( MENU_ENUM_SUBLABEL_VIDEO_WINDOW_WIDTH, "Set the custom width for the display window." @@ -7256,16 +7239,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MENU_SHOW_HELP, "Show/hide the 'Help' option." ) -#ifdef HAVE_LAKKA -MSG_HASH( - MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, - "Show Restart RetroArch" - ) -MSG_HASH( - MENU_ENUM_SUBLABEL_MENU_SHOW_QUIT_RETROARCH, - "Show/hide the 'Restart RetroArch' option." - ) -#else MSG_HASH( MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, "Show Quit RetroArch" @@ -7282,7 +7255,6 @@ MSG_HASH( MENU_ENUM_SUBLABEL_MENU_SHOW_RESTART_RETROARCH, "Show/hide the 'Restart RetroArch' option." ) -#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_MENU_SHOW_REBOOT, "Show Reboot" diff --git a/menu/cbs/menu_cbs_sublabel.c b/menu/cbs/menu_cbs_sublabel.c index b2b961d66e..4e5c216768 100644 --- a/menu/cbs/menu_cbs_sublabel.c +++ b/menu/cbs/menu_cbs_sublabel.c @@ -72,7 +72,7 @@ static int menu_action_sublabel_file_browser_core(file_list_t *list, unsigned ty unsigned j; for (j = 0; j < core_list->count; j++) { - if (string_is_equal(path_basename(core_list->list[j].path), + if (string_is_equal(path_basename(core_list->list[j].path), path)) { if (core_list->list[j].licenses_list) @@ -301,7 +301,11 @@ default_sublabel_macro(action_bind_sublabel_content_special, MENU_ default_sublabel_macro(action_bind_sublabel_load_content_history, MENU_ENUM_SUBLABEL_LOAD_CONTENT_HISTORY) default_sublabel_macro(action_bind_sublabel_network_information, MENU_ENUM_SUBLABEL_NETWORK_INFORMATION) default_sublabel_macro(action_bind_sublabel_system_information, MENU_ENUM_SUBLABEL_SYSTEM_INFORMATION) +#ifdef HAVE_LAKKA +default_sublabel_macro(action_bind_sublabel_quit_retroarch, MENU_ENUM_SUBLABEL_RESTART_RETROARCH) +#else default_sublabel_macro(action_bind_sublabel_quit_retroarch, MENU_ENUM_SUBLABEL_QUIT_RETROARCH) +#endif default_sublabel_macro(action_bind_sublabel_restart_retroarch, MENU_ENUM_SUBLABEL_RESTART_RETROARCH) default_sublabel_macro(action_bind_sublabel_menu_widgets, MENU_ENUM_SUBLABEL_MENU_WIDGETS_ENABLE) default_sublabel_macro(action_bind_sublabel_video_window_width, MENU_ENUM_SUBLABEL_VIDEO_WINDOW_WIDTH) @@ -1043,7 +1047,7 @@ static int action_bind_sublabel_playlist_entry( settings_t *settings = config_get_ptr(); playlist_t *playlist = NULL; const struct playlist_entry *entry = NULL; - + if (!settings->bools.playlist_show_sublabels || string_is_equal(settings->arrays.menu_driver, "ozone")) return 0; @@ -1055,19 +1059,19 @@ static int action_bind_sublabel_playlist_entry( if (i >= playlist_get_size(playlist)) return 0; - + /* Read playlist entry */ playlist_get_index(playlist, i, &entry); - + /* Only add sublabel if a core is currently assigned */ if (string_is_empty(entry->core_name) || string_is_equal(entry->core_name, "DETECT")) return 0; - + /* Add core name */ strlcpy(s, msg_hash_to_str(MENU_ENUM_LABEL_VALUE_PLAYLIST_SUBLABEL_CORE), len); strlcat(s, " ", len); strlcat(s, entry->core_name, len); - + /* Get runtime info *if* required runtime log is enabled * *and* this is a valid playlist type */ if (((settings->uints.playlist_sublabel_runtime_type == PLAYLIST_RUNTIME_PER_CORE) && @@ -1075,7 +1079,7 @@ static int action_bind_sublabel_playlist_entry( ((settings->uints.playlist_sublabel_runtime_type == PLAYLIST_RUNTIME_AGGREGATE) && !settings->bools.content_runtime_log_aggregate)) return 0; - + /* Note: This looks heavy, but each string_is_equal() call will * return almost immediately */ if (!string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_LOAD_CONTENT_HISTORY)) && @@ -1085,17 +1089,17 @@ static int action_bind_sublabel_playlist_entry( !string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_DEFERRED_PLAYLIST_LIST)) && !string_is_equal(label, msg_hash_to_str(MENU_ENUM_LABEL_HORIZONTAL_MENU))) return 0; - + /* Check whether runtime info should be loaded from log file */ if (entry->runtime_status == PLAYLIST_RUNTIME_UNKNOWN) runtime_update_playlist(playlist, i); - + /* Check whether runtime info is valid */ if (entry->runtime_status == PLAYLIST_RUNTIME_VALID) { int n = 0; char tmp[64]; - + tmp[0 ] = '\n'; tmp[1 ] = '\0'; @@ -1103,18 +1107,18 @@ static int action_bind_sublabel_playlist_entry( tmp[n ] = '\n'; tmp[n+1] = '\0'; - + /* Runtime/last played strings are now cached in the * playlist, so we can add both in one go */ n = strlcat(tmp, entry->last_played_str, sizeof(tmp)); - + if ((n < 0) || (n >= 64)) n = 0; /* Silence GCC warnings... */ - + if (!string_is_empty(tmp)) strlcat(s, tmp, len); } - + return 0; } diff --git a/menu/menu_setting.c b/menu/menu_setting.c index 1e2f8989be..d01071fe2e 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -277,7 +277,7 @@ struct string_options_entry (*a)[b->index - 1].enum_idx = c #define MENU_SETTINGS_LIST_CURRENT_ADD_ENUM_VALUE_IDX(a, b, c) \ - (*a)[b->index - 1].enum_value_idx = c + (*a)[b->index - 1].enum_value_idx = c static void menu_input_st_uint_cb(void *userdata, const char *str) { @@ -631,7 +631,7 @@ int setting_uint_action_left_default( overflowed = step > *setting->value.target.unsigned_integer; if (!overflowed) - *setting->value.target.unsigned_integer = + *setting->value.target.unsigned_integer = *setting->value.target.unsigned_integer - step; if (setting->enforce_minrange) @@ -662,7 +662,7 @@ int setting_uint_action_right_default( return -1; max = setting->max; - step = + step = recalc_step_based_on_length_of_action(setting); *setting->value.target.unsigned_integer = @@ -4182,7 +4182,7 @@ static void setting_get_string_representation_uint_xmb_menu_color_theme( msg_hash_to_str( MENU_ENUM_LABEL_VALUE_XMB_MENU_COLOR_THEME_SUNBEAM), len); - break; + break; } } #endif @@ -4355,7 +4355,7 @@ static void setting_get_string_representation_crt_switch_resolution_super( else if (*setting->value.target.unsigned_integer == 1) strlcpy(s, "DYNAMIC", len); else - snprintf(s, len, "%d", *setting->value.target.unsigned_integer); + snprintf(s, len, "%d", *setting->value.target.unsigned_integer); } static void setting_get_string_representation_uint_playlist_sublabel_runtime_type( @@ -5885,7 +5885,7 @@ rarch_setting_t *menu_setting_find_enum(enum msg_hash_enums enum_idx) return NULL; for (; setting_get_type(setting) != ST_NONE; (*list = *list + 1)) { - if ( setting->enum_idx == enum_idx && + if ( setting->enum_idx == enum_idx && setting_get_type(setting) <= ST_GROUP) { const char *short_description = setting->short_description; @@ -6558,7 +6558,7 @@ void general_write_handler(rarch_setting_t *setting) video_driver_set_rotation( (*setting->value.target.unsigned_integer + system->rotation) % 4); - + /* Update Custom Aspect Ratio values */ video_driver_get_viewport_info(&vp); custom->x = 0; @@ -7095,7 +7095,7 @@ static bool setting_append_list_input_player_options( "%s %u", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_SPLIT_JOYCON), user + 1); snprintf(label[user], sizeof(label[user]), - "%s", + "%s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_DEVICE_INDEX)); snprintf(label_type[user], sizeof(label_type[user]), "%s", @@ -7107,13 +7107,13 @@ static bool setting_append_list_input_player_options( "%s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_BIND_ALL)); snprintf(label_bind_defaults[user], sizeof(label_bind_defaults[user]), - "%s", + "%s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_BIND_DEFAULT_ALL)); snprintf(label_bind_all_save_autoconfig[user], sizeof(label_bind_all_save_autoconfig[user]), - "%s", + "%s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_SAVE_AUTOCONFIG)); snprintf(label_mouse_index[user], sizeof(label_mouse_index[user]), - "%s", + "%s", msg_hash_to_str(MENU_ENUM_LABEL_VALUE_INPUT_MOUSE_INDEX)); CONFIG_UINT_ALT( @@ -7690,7 +7690,11 @@ static bool setting_append_list( CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_QUIT_RETROARCH, +#ifdef HAVE_LAKKA + MENU_ENUM_LABEL_VALUE_RESTART_RETROARCH, +#else MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, +#endif &group_info, &subgroup_info, parent_group); @@ -7728,7 +7732,11 @@ static bool setting_append_list( CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_REBOOT, +#ifdef HAVE_LAKKA_SWITCH + MENU_ENUM_LABEL_VALUE_REBOOT_RCM, +#else MENU_ENUM_LABEL_VALUE_REBOOT, +#endif &group_info, &subgroup_info, parent_group); @@ -10153,7 +10161,7 @@ static bool setting_append_list( (*list)[list_info->index - 1].action_ok = setting_bool_action_left_with_refresh; (*list)[list_info->index - 1].action_left = setting_bool_action_left_with_refresh; (*list)[list_info->index - 1].action_right = setting_bool_action_right_with_refresh; - + CONFIG_UINT( list, list_info, &settings->uints.video_hard_sync_frames, @@ -13220,7 +13228,11 @@ static bool setting_append_list( list, list_info, &settings->bools.menu_show_quit_retroarch, MENU_ENUM_LABEL_MENU_SHOW_QUIT_RETROARCH, +#ifdef HAVE_LAKKA + MENU_ENUM_LABEL_VALUE_MENU_SHOW_RESTART_RETROARCH, +#else MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, +#endif menu_show_quit_retroarch, MENU_ENUM_LABEL_VALUE_OFF, MENU_ENUM_LABEL_VALUE_ON, @@ -13625,7 +13637,7 @@ static bool setting_append_list( general_read_handler); (*list)[list_info->index - 1].action_ok = &setting_action_ok_uint; menu_settings_list_current_add_range(list, list_info, 0.0, 1.0, 0.010, true, true); - (*list)[list_info->index - 1].ui_type + (*list)[list_info->index - 1].ui_type = ST_UI_TYPE_FLOAT_SLIDER_AND_SPINBOX; */ } diff --git a/msg_hash.h b/msg_hash.h index 075a7e0d55..f527b457b7 100644 --- a/msg_hash.h +++ b/msg_hash.h @@ -785,6 +785,7 @@ enum msg_hash_enums MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY, + MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_BSV_RECORD_TOGGLE, diff --git a/retroarch.c b/retroarch.c index d331514cc1..d8321c996d 100644 --- a/retroarch.c +++ b/retroarch.c @@ -2414,7 +2414,7 @@ bool menu_input_dialog_start_search(void) sizeof(menu_input_dialog_keyboard_label)); input_keyboard_ctl(RARCH_INPUT_KEYBOARD_CTL_LINE_FREE, NULL); - + if (is_accessibility_enabled()) { accessibility_speak((char*) msg_hash_to_str(MENU_ENUM_LABEL_VALUE_SEARCH)); @@ -3166,7 +3166,11 @@ const struct input_bind_map input_config_bind_map[RARCH_BIND_LIST_END_NULL] = { DECLARE_META_BIND(1, load_state, RARCH_LOAD_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_LOAD_STATE_KEY), DECLARE_META_BIND(1, save_state, RARCH_SAVE_STATE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_SAVE_STATE_KEY), DECLARE_META_BIND(2, toggle_fullscreen, RARCH_FULLSCREEN_TOGGLE_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_FULLSCREEN_TOGGLE_KEY), +#ifdef HAVE_LAKKA + DECLARE_META_BIND(2, exit_emulator, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_RESTART_KEY), +#else DECLARE_META_BIND(2, exit_emulator, RARCH_QUIT_KEY, MENU_ENUM_LABEL_VALUE_INPUT_META_QUIT_KEY), +#endif DECLARE_META_BIND(2, state_slot_increase, RARCH_STATE_SLOT_PLUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_PLUS), DECLARE_META_BIND(2, state_slot_decrease, RARCH_STATE_SLOT_MINUS, MENU_ENUM_LABEL_VALUE_INPUT_META_STATE_SLOT_MINUS), DECLARE_META_BIND(1, rewind, RARCH_REWIND, MENU_ENUM_LABEL_VALUE_INPUT_META_REWIND), @@ -4172,7 +4176,7 @@ static void handle_translation_cb( } i++; } - + if (string_is_equal(error_string, "No text found.")) { RARCH_LOG("No text found...\n"); @@ -4180,14 +4184,14 @@ static void handle_translation_cb( { text_string = (char*)malloc(15); } - + strlcpy(text_string, error_string, 15); #ifdef HAVE_MENU_WIDGETS if (menu_widgets_paused) { /* In this case we have to unpause and then repause for a frame */ menu_widgets_ai_service_overlay_set_state(2); - command_event(CMD_EVENT_UNPAUSE, NULL); + command_event(CMD_EVENT_UNPAUSE, NULL); } #endif } @@ -4220,7 +4224,7 @@ static void handle_translation_cb( { image_type = IMAGE_TYPE_BMP; } - else if (raw_image_file_data[1] == 'P' && + else if (raw_image_file_data[1] == 'P' && raw_image_file_data[2] == 'N' && raw_image_file_data[3] == 'G') image_type = IMAGE_TYPE_PNG; @@ -4229,11 +4233,11 @@ static void handle_translation_cb( RARCH_LOG("Invalid image type returned from server.\n"); goto finish; } - + ai_res = menu_widgets_ai_service_overlay_load( - raw_image_file_data, (unsigned) new_image_size, + raw_image_file_data, (unsigned) new_image_size, image_type); - + if (!ai_res) { RARCH_LOG("Video driver not supported for AI Service."); @@ -4250,7 +4254,7 @@ static void handle_translation_cb( command_event(CMD_EVENT_UNPAUSE, NULL); } } - else + else #endif /* Can't use menu widget overlays, so try writing to video buffer */ { @@ -4271,8 +4275,8 @@ static void handle_translation_cb( ((uint32_t) ((uint8_t)raw_image_file_data[23]) << 8) + ((uint32_t) ((uint8_t)raw_image_file_data[22]) << 0); raw_image_data = (void*)malloc(image_width*image_height*3*sizeof(uint8_t)); - memcpy(raw_image_data, - raw_image_file_data+54*sizeof(uint8_t), + memcpy(raw_image_data, + raw_image_file_data+54*sizeof(uint8_t), image_width*image_height*3*sizeof(uint8_t)); } else if (raw_image_file_data[1] == 'P' && raw_image_file_data[2] == 'N' && @@ -4291,7 +4295,7 @@ static void handle_translation_cb( ((uint32_t) ((uint8_t)raw_image_file_data[22])<<8)+ ((uint32_t) ((uint8_t)raw_image_file_data[23])<<0); rpng = rpng_alloc(); - + if (!rpng) { error = "Can't allocate memory."; @@ -4332,7 +4336,7 @@ static void handle_translation_cb( { RARCH_LOG("Output from URL not a valid file type, or is not supported.\n"); goto finish; - } + } scaler = (struct scaler_ctx*)calloc(1, sizeof(struct scaler_ctx)); if (!scaler) @@ -4387,7 +4391,7 @@ static void handle_translation_cb( video_driver_frame(raw_output_data, image_width, image_height, pitch); } } - + #ifdef HAVE_AUDIOMIXER if (raw_sound_data) { @@ -4638,9 +4642,9 @@ static const char *ai_service_get_str(enum translation_lang id) To make your own server, it must listen for a POST request, which will consist of a JSON body, with the "image" field as a base64 - encoded string of a 24bit-BMP/PNG that the will be translated. - The server must output the translated image in the form of a - JSON body, with the "image" field also as a base64 encoded + encoded string of a 24bit-BMP/PNG that the will be translated. + The server must output the translated image in the form of a + JSON body, with the "image" field also as a base64 encoded 24bit-BMP, or as an alpha channel png. */ static bool run_translation_service(void) @@ -4675,7 +4679,7 @@ static bool run_translation_service(void) char* system_label = NULL; core_info_t *core_info = NULL; -#ifdef HAVE_MENU_WIDGETS +#ifdef HAVE_MENU_WIDGETS if (menu_widgets_ai_service_overlay_get_state() != 0) { /* For the case when ai service pause is disabled. */ @@ -4702,7 +4706,7 @@ static bool run_translation_service(void) { const char *system_id = core_info->system_id ? core_info->system_id : "core"; - + const struct playlist_entry *entry = NULL; playlist_t *current_playlist = playlist_get_cached(); @@ -4863,7 +4867,7 @@ static bool run_translation_service(void) memcpy(json_buffer, (const void*)rf1, 11*sizeof(uint8_t)); memcpy(json_buffer+11, bmp64_buffer, (out_length)*sizeof(uint8_t)); if (rf3) - memcpy(json_buffer+11+out_length, (const void*)rf3, (16+strlen(system_label))*sizeof(uint8_t)); + memcpy(json_buffer+11+out_length, (const void*)rf3, (16+strlen(system_label))*sizeof(uint8_t)); else memcpy(json_buffer+11+out_length, (const void*)rf2, 3*sizeof(uint8_t)); RARCH_LOG("Request size: %d\n", out_length); @@ -4942,7 +4946,7 @@ static bool run_translation_service(void) if (use_overlay) mode_chr = "image,png,png-a,sound,wav"; else - mode_chr = "image,png,sound,wav"; + mode_chr = "image,png,sound,wav"; break; default: break; @@ -6357,13 +6361,13 @@ bool command_event(enum event_command cmd, void *data) accessibility_speak((char*) msg_hash_to_str(MSG_UNPAUSED)); command_event(CMD_EVENT_UNPAUSE, NULL); } - } + } else { /* Don't pause - useful for Text-To-Speech since * the audio can't currently play while paused. * Also useful for cases when users don't want the - * core's sound to stop while translating. */ + * core's sound to stop while translating. */ command_event(CMD_EVENT_AI_SERVICE_CALL, NULL); } #endif @@ -7749,7 +7753,7 @@ static void global_free(void) void main_exit(void *args) { settings_t *settings = configuration_settings; - + if (cached_video_driver[0]) { strcpy(settings->arrays.video_driver, cached_video_driver); @@ -9491,7 +9495,7 @@ static bool rarch_environment_cb(unsigned cmd, void *data) system->disk_control_cb = *(const struct retro_disk_control_callback*)data; break; - + case RETRO_ENVIRONMENT_GET_PREFERRED_HW_RENDER: { unsigned *cb = (unsigned*)data; @@ -9521,7 +9525,7 @@ static bool rarch_environment_cb(unsigned cmd, void *data) video_driver_get_hw_context_internal(); RARCH_LOG("[Environ]: SET_HW_RENDER.\n"); - + if (!dynamic_request_hw_context( cb->context_type, cb->version_minor, cb->version_major)) return false; @@ -10020,7 +10024,7 @@ static bool rarch_environment_cb(unsigned cmd, void *data) case RETRO_ENVIRONMENT_SET_HW_SHARED_CONTEXT: #ifdef HAVE_LIBNX - /* TODO/FIXME - Force this off for now for Switch + /* TODO/FIXME - Force this off for now for Switch * until shared HW context can work there */ return false; #else @@ -13681,7 +13685,7 @@ static int16_t input_state_device( */ if (settings->uints.input_turbo_mode == 1) { - /* Pressing turbo button toggles turbo mode on or off. + /* Pressing turbo button toggles turbo mode on or off. * Holding the button will * pass through, else the pressed state will be modulated by a * periodic pulse defined by the configured duty cycle. @@ -13724,7 +13728,7 @@ static int16_t input_state_device( { uint16_t enable_new; input_driver_turbo_btns.turbo_pressed[port] |= 1 << id; - /* Toggle turbo for pressed button but make + /* Toggle turbo for pressed button but make * sure at least one button has turbo */ enable_new = input_driver_turbo_btns.enable[port] ^ (1 << id); if (enable_new) @@ -16568,7 +16572,7 @@ void input_keyboard_event(bool down, unsigned code, { static bool deferred_wait_keys; #ifdef HAVE_MENU - if (menu_input_dialog_get_display_kb() + if (menu_input_dialog_get_display_kb() && down && is_accessibility_enabled()) { if (code != 303 && code != 0) @@ -19863,7 +19867,7 @@ static bool hw_render_context_is_gl(enum retro_hw_context_type type) bool *video_driver_get_threaded(void) { -#if defined(__MACH__) && defined(__APPLE__) +#if defined(__MACH__) && defined(__APPLE__) /* TODO/FIXME - force threaded video to disabled on Apple for now * until NSWindow/UIWindow concurrency issues are taken care of */ video_driver_threaded = false; @@ -19873,7 +19877,7 @@ bool *video_driver_get_threaded(void) void video_driver_set_threaded(bool val) { -#if defined(__MACH__) && defined(__APPLE__) +#if defined(__MACH__) && defined(__APPLE__) /* TODO/FIXME - force threaded video to disabled on Apple for now * until NSWindow/UIWindow concurrency issues are taken care of */ video_driver_threaded = false; @@ -21392,7 +21396,7 @@ void video_viewport_get_scaled_integer(struct video_viewport *vp, /* Use system reported sizes as these define the * geometry for the "normal" case. */ unsigned base_height; - + if (retroarch_get_rotation() % 2) base_height = video_driver_av_info.geometry.base_width; else @@ -21483,7 +21487,7 @@ static void video_driver_frame(const void *data, unsigned width, frame_cache_width = width; frame_cache_height = height; frame_cache_pitch = pitch; - + if ( video_driver_scaler_ptr && data @@ -29084,7 +29088,7 @@ bool is_accessibility_enabled(void) } bool is_input_keyboard_display_on(void) -{ +{ #ifdef HAVE_MENU return menu_input_dialog_get_display_kb(); #else @@ -29098,7 +29102,7 @@ bool accessibility_speak(const char* speak_text) } -#if defined(__MACH__) && defined(__APPLE__) +#if defined(__MACH__) && defined(__APPLE__) #include #if TARGET_OS_OSX && !defined(EMSCRIPTEN) #define _IS_OSX @@ -29166,7 +29170,7 @@ static char* accessibility_mac_language_code(const char* language) return "Yuna"; else if (string_is_equal(language,"pl")) return "Zosia"; - else if (string_is_equal(language,"cs")) + else if (string_is_equal(language,"cs")) return "Zuzana"; else return ""; @@ -29218,16 +29222,16 @@ static bool accessibility_speak_macos( /* parent process */ speak_pid = pid; - /* Tell the system that we'll ignore the exit status of the child + /* Tell the system that we'll ignore the exit status of the child * process. This prevents zombie processes. */ signal(SIGCHLD,SIG_IGN); } else - { - /* child process: replace process with the say command */ + { + /* child process: replace process with the say command */ if (strlen(language_speaker)> 0) { - char* cmd[] = {"say", "-v", NULL, + char* cmd[] = {"say", "-v", NULL, NULL, "-r", NULL, NULL}; cmd[2] = language_speaker; cmd[3] = (char *) speak_text; @@ -29308,7 +29312,7 @@ static const char *accessibility_win_language_code(const char* language) return "Microsoft Heami Desktop"; else if (string_is_equal(language,"pl")) return "Microsoft Adam Desktop"; - else if (string_is_equal(language,"cs")) + else if (string_is_equal(language,"cs")) return "Microsoft Jakub Desktop"; else return ""; @@ -29376,18 +29380,18 @@ static bool accessibility_speak_windows( return true; } - if (strlen(language) > 0) + if (strlen(language) > 0) snprintf(cmd, sizeof(cmd), - "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", language, speeds[speed-1], (char*) speak_text); + "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.SelectVoice(\\\"%s\\\"); $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", language, speeds[speed-1], (char*) speak_text); else snprintf(cmd, sizeof(cmd), - "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", speeds[speed-1], (char*) speak_text); + "powershell.exe -NoProfile -WindowStyle Hidden -Command \"Add-Type -AssemblyName System.Speech; $synth = New-Object System.Speech.Synthesis.SpeechSynthesizer; $synth.Rate = %s; $synth.Speak(\\\"%s\\\");\"", speeds[speed-1], (char*) speak_text); if (pi_set) terminate_win32_process(pi); res = create_win32_process(cmd); if (!res) { - RARCH_LOG("Create subprocess failed. Error: %d\n", GetLastError()); + RARCH_LOG("Create subprocess failed. Error: %d\n", GetLastError()); } return true; } @@ -29450,13 +29454,13 @@ bool accessibility_speak_linux( /* parent process */ speak_pid = pid; - /* Tell the system that we'll ignore the exit status of the child + /* Tell the system that we'll ignore the exit status of the child * process. This prevents zombie processes. */ signal(SIGCHLD,SIG_IGN); } else - { - /* child process: replace process with the espeak command */ + { + /* child process: replace process with the espeak command */ char* cmd[] = { (char*) "espeak", NULL, NULL, NULL, NULL}; cmd[1] = voice_out; cmd[2] = speed_out; @@ -29486,11 +29490,11 @@ bool accessibility_speak_priority(const char* speak_text, int priority) RARCH_LOG("Platform not supported for accessibility.\n"); /* The following method is a fallback for other platforms to use the AI Service url to do the TTS. However, since the playback is done - via the audio mixer, which only processes the audio while the + via the audio mixer, which only processes the audio while the core is running, this playback method won't work. When the audio - mixer can handle playing streams while the core is paused, then + mixer can handle playing streams while the core is paused, then we can use this. */ - /* + /* #if defined(HAVE_NETWORKING) return accessibility_speak_ai_service(speak_text, voice, priority); #endif @@ -29519,8 +29523,8 @@ bool is_narrator_running(void) bool accessibility_speak_ai_service(const char* speak_text, const char* language, int priority) { #if defined(HAVE_NETWORKING) && defined(HAVE_TRANSLATE) - /* Call the AI service listed to do espeak for us. */ - /* NOTE: This call works, but the audio mixer will not + /* Call the AI service listed to do espeak for us. */ + /* NOTE: This call works, but the audio mixer will not * play sound files while the core is paused, so it's * not practical at the moment. */ unsigned i; @@ -29530,22 +29534,22 @@ bool accessibility_speak_ai_service(const char* speak_text, const char* language char separator = '?'; settings_t *settings = configuration_settings; - strlcpy(new_ai_service_url, settings->arrays.ai_service_url, + strlcpy(new_ai_service_url, settings->arrays.ai_service_url, sizeof(new_ai_service_url)); if (strrchr(new_ai_service_url, '?') != NULL) separator = '&'; snprintf(temp_string, sizeof(temp_string), - "%csource_lang=%s&target_lang=%s&output=espeak", + "%csource_lang=%s&target_lang=%s&output=espeak", separator, language, language); strlcat(new_ai_service_url, temp_string, sizeof(new_ai_service_url)); - + strlcpy(temp_string, speak_text, sizeof(temp_string)); for (i = 0; i < strlen(temp_string);i++) { if (temp_string[i]=='\"') temp_string[i] = ' '; - } + } snprintf(json_buffer, sizeof(json_buffer), "{\"text\": \"%s\"}", speak_text); RARCH_LOG("SENDING accessibilty request... %s\n", new_ai_service_url); @@ -29560,7 +29564,7 @@ bool accessibility_speak_ai_service(const char* speak_text, const char* language bool accessibility_startup_message(void) { - /* State that the narrator is on, and also include the first menu + /* State that the narrator is on, and also include the first menu item we're on at startup. */ accessibility_speak("RetroArch accessibility on. Main Menu Load Core."); return true; From a3806194ee84fe95e8b6f869eb09a7261f0a52d1 Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Wed, 25 Sep 2019 17:08:14 +0300 Subject: [PATCH 3/7] Handle exceptions in intl/template.py --- intl/template.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/intl/template.py b/intl/template.py index 9201f694ab..0ce68017b4 100755 --- a/intl/template.py +++ b/intl/template.py @@ -9,7 +9,7 @@ import sys try: lc = sys.argv[1] -except: +except IndexError: print("Usage: ./template.py ") sys.exit(1) @@ -54,9 +54,12 @@ def update(translation, template): with open('msg_hash_us.h', 'r') as template_file: template = template_file.read() - with open('msg_hash_' + lc + '.h', 'r+') as translation_file: - translation = translation_file.read() - new_translation = update(translation, template) - translation_file.seek(0) - translation_file.write(new_translation) - translation_file.truncate() + try: + with open('msg_hash_' + lc + '.h', 'r+') as translation_file: + translation = translation_file.read() + new_translation = update(translation, template) + translation_file.seek(0) + translation_file.write(new_translation) + translation_file.truncate() + except EnvironmentError: + print('Cannot read/write "msg_hash_' + lc + '.h"') From 148e15bca0b75afefa78c36155373ef43884b04c Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Wed, 25 Sep 2019 17:20:17 +0300 Subject: [PATCH 4/7] Detect duplicated keys --- intl/template.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/intl/template.py b/intl/template.py index 0ce68017b4..520e8e0b83 100755 --- a/intl/template.py +++ b/intl/template.py @@ -35,11 +35,17 @@ def parse_message(message): def messages(text): result = p.findall(text) + seen = set() msg_list = [] for msg in result: key, val = parse_message(msg) item = {'key': key, 'val': val, 'msg': msg} msg_list.append(item) + if key not in seen: + seen.add(key) + else: + print("Duplicate key: " + key) + return msg_list def update(translation, template): From 34792a3493953c358a1e50193b07440196ed4d75 Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Tue, 22 Oct 2019 17:59:16 +0300 Subject: [PATCH 5/7] Fix C98 build error --- menu/menu_setting.c | 49 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 12 deletions(-) diff --git a/menu/menu_setting.c b/menu/menu_setting.c index d01071fe2e..a63af56dbf 100644 --- a/menu/menu_setting.c +++ b/menu/menu_setting.c @@ -7687,17 +7687,23 @@ static bool setting_append_list( #endif #if !defined(IOS) /* Apple rejects iOS apps that let you forcibly quit them. */ +#ifdef HAVE_LAKKA CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_QUIT_RETROARCH, -#ifdef HAVE_LAKKA MENU_ENUM_LABEL_VALUE_RESTART_RETROARCH, -#else - MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, -#endif &group_info, &subgroup_info, parent_group); +#else + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_QUIT_RETROARCH, + MENU_ENUM_LABEL_VALUE_QUIT_RETROARCH, + &group_info, + &subgroup_info, + parent_group); +#endif menu_settings_list_current_add_cmd(list, list_info, CMD_EVENT_QUIT); #endif @@ -7729,17 +7735,23 @@ static bool setting_append_list( &subgroup_info, parent_group); #endif +#ifdef HAVE_LAKKA_SWITCH CONFIG_ACTION( list, list_info, MENU_ENUM_LABEL_REBOOT, -#ifdef HAVE_LAKKA_SWITCH MENU_ENUM_LABEL_VALUE_REBOOT_RCM, -#else - MENU_ENUM_LABEL_VALUE_REBOOT, -#endif &group_info, &subgroup_info, parent_group); +#else + CONFIG_ACTION( + list, list_info, + MENU_ENUM_LABEL_REBOOT, + MENU_ENUM_LABEL_VALUE_REBOOT, + &group_info, + &subgroup_info, + parent_group); +#endif menu_settings_list_current_add_cmd(list, list_info, CMD_EVENT_REBOOT); CONFIG_ACTION( @@ -13224,15 +13236,12 @@ static bool setting_append_list( general_read_handler, SD_FLAG_LAKKA_ADVANCED); +#ifdef HAVE_LAKKA CONFIG_BOOL( list, list_info, &settings->bools.menu_show_quit_retroarch, MENU_ENUM_LABEL_MENU_SHOW_QUIT_RETROARCH, -#ifdef HAVE_LAKKA MENU_ENUM_LABEL_VALUE_MENU_SHOW_RESTART_RETROARCH, -#else - MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, -#endif menu_show_quit_retroarch, MENU_ENUM_LABEL_VALUE_OFF, MENU_ENUM_LABEL_VALUE_ON, @@ -13242,6 +13251,22 @@ static bool setting_append_list( general_write_handler, general_read_handler, SD_FLAG_NONE); +#else + CONFIG_BOOL( + list, list_info, + &settings->bools.menu_show_quit_retroarch, + MENU_ENUM_LABEL_MENU_SHOW_QUIT_RETROARCH, + MENU_ENUM_LABEL_VALUE_MENU_SHOW_QUIT_RETROARCH, + menu_show_quit_retroarch, + MENU_ENUM_LABEL_VALUE_OFF, + MENU_ENUM_LABEL_VALUE_ON, + &group_info, + &subgroup_info, + parent_group, + general_write_handler, + general_read_handler, + SD_FLAG_NONE); +#endif #ifdef HAVE_LAKKA CONFIG_BOOL( From 85814137eac403ae1841718911cc3921daef6bdd Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Wed, 27 Nov 2019 17:01:47 +0200 Subject: [PATCH 6/7] Use JSON as format to connect with translation tools --- intl/.gitignore | 2 ++ intl/README.md | 17 ++++++++++ intl/h2json.py | 55 +++++++++++++++++++++++++++++++++ intl/{template.py => json2h.py} | 44 +++++++++++++++----------- intl/msg_hash_us.h | 1 - 5 files changed, 100 insertions(+), 19 deletions(-) create mode 100644 intl/.gitignore create mode 100644 intl/README.md create mode 100755 intl/h2json.py rename intl/{template.py => json2h.py} (58%) diff --git a/intl/.gitignore b/intl/.gitignore new file mode 100644 index 0000000000..d69d1a4252 --- /dev/null +++ b/intl/.gitignore @@ -0,0 +1,2 @@ +*.json + diff --git a/intl/README.md b/intl/README.md new file mode 100644 index 0000000000..6398376d25 --- /dev/null +++ b/intl/README.md @@ -0,0 +1,17 @@ +# Internationalization Workflow (Draft) + +## Steps + +- Developers update strings in `msg_hash_us.h`. +- Developers (can set a cron job) run `./h2json.py msg_hash_us.h` to generate `msg_hash_us.json`. It is just a convenient format that is supported by Weblate/Crowdin/Transifex and doesn't need to be inversion control. +- Developers (can set a cron job) upload `msg_hash_us.json` to Weblate/Crowdin/Transifex. +- Translators translate strings on Weblate/Crowdin/Transifex. +- Developers (can set a cron job) download `msg_hash_xx.json` files. +- Developers (can set a cron job) run `./json2h.py msg_hash_xx.json` to generate `msg_hash_xx.h`. + +## Pros + +- No new dependencies. +- No performance impact. +- Don't require translators to know how to use Git, how to read C code and how to create Pull Request. +- Translators will be informed whenever a source string changes. diff --git a/intl/h2json.py b/intl/h2json.py new file mode 100755 index 0000000000..eb986cb063 --- /dev/null +++ b/intl/h2json.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python3 + +# Convert *.h to *.json +# Usage: ./h2json.py msg_has_us.h + +import re +import sys +import json + +try: + h_filename = sys.argv[1] + json_filename = h_filename.replace('.h', '.json') +except IndexError: + print("Usage: ./h2json.py msg_has_us.h") + sys.exit(1) + +p = re.compile('MSG_HASH\(\s*[A-Z0-9_]+,\s*\".*\"\s*\)') + +header = """#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +#if (_MSC_VER >= 1700) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#endif +#pragma warning(disable:4566) +#endif +""" + + +def parse_message(message): + key_start = message.find('(') + 1 + key_end = message.find(',') + key = message[key_start:key_end].strip() + value_start = message.find('"') + 1 + value_end = message.rfind('"') + value = message[value_start:value_end].strip() + return key, value + + +try: + with open(h_filename, 'r+') as h_file: + text = h_file.read() + result = p.findall(text) + seen = set() + messages = {} + for msg in result: + key, val = parse_message(msg) + messages[key] = val + if key not in seen: + seen.add(key) + else: + print("Duplicate key: " + key) + with open(json_filename, 'w') as json_file: + json.dump(messages, json_file, indent=2) +except EnvironmentError: + print('Cannot read/write ' + h_filename) diff --git a/intl/template.py b/intl/json2h.py similarity index 58% rename from intl/template.py rename to intl/json2h.py index 520e8e0b83..511e1c37b0 100755 --- a/intl/template.py +++ b/intl/json2h.py @@ -1,14 +1,15 @@ #!/usr/bin/env python3 -# Apply template (us) updates to translations (fr, ja, chs, etc.) -# Usage: ./template.py xx -# xx is the language code postfix of translation files +# Convert *.json to *.h +# Usage: ./json2h.py msg_hash_fr.json import re import sys +import json try: - lc = sys.argv[1] + json_filename = sys.argv[1] + h_filename = json_filename.replace('.json', '.h') except IndexError: print("Usage: ./template.py ") sys.exit(1) @@ -24,6 +25,7 @@ header = """#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MS #endif """ + def parse_message(message): key_start = message.find('(') + 1 key_end = message.find(',') @@ -33,7 +35,8 @@ def parse_message(message): value = message[value_start:value_end].strip() return key, value -def messages(text): + +def parse_messages(text): result = p.findall(text) seen = set() msg_list = [] @@ -48,24 +51,29 @@ def messages(text): return msg_list -def update(translation, template): + +def update(messages, template): new_translation = header + template - template_messages = messages(template) - translation_messages = messages(translation) + template_messages = parse_messages(template) for tp_msg in template_messages: - for ts_msg in translation_messages: - if tp_msg['key'] == ts_msg['key']: - new_translation = new_translation.replace(tp_msg['msg'], ts_msg['msg']) + if tp_msg['key'] in messages: + tp_msg_val = tp_msg['val'] + tl_msg_val = messages[tp_msg['key']] + old_msg = tp_msg['msg'] + new_msg = old_msg.replace(tp_msg_val, tl_msg_val) + new_translation = new_translation.replace(old_msg, new_msg) return new_translation + with open('msg_hash_us.h', 'r') as template_file: template = template_file.read() try: - with open('msg_hash_' + lc + '.h', 'r+') as translation_file: - translation = translation_file.read() - new_translation = update(translation, template) - translation_file.seek(0) - translation_file.write(new_translation) - translation_file.truncate() + with open(json_filename, 'r+') as json_file: + messages = json.load(json_file) + new_translation = update(messages, template) + with open(h_filename, 'w') as h_file: + h_file.seek(0) + h_file.write(new_translation) + h_file.truncate() except EnvironmentError: - print('Cannot read/write "msg_hash_' + lc + '.h"') + print('Cannot read/write ' + json_filename) diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 5dbcfe6a49..8d3a823808 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -2500,7 +2500,6 @@ MSG_HASH( MENU_ENUM_LABEL_VALUE_RESTART_RETROARCH, "Restart RetroArch" ) -#endif MSG_HASH( MENU_ENUM_LABEL_VALUE_RDB_ENTRY_DETAIL, "Database Entry" From 0c2fbd178d6c31f2382c69cb740baa28a700072b Mon Sep 17 00:00:00 2001 From: Guo Yunhe Date: Sat, 4 Jan 2020 21:56:06 +0200 Subject: [PATCH 7/7] Synchronize translations --- intl/.gitignore | 3 ++- intl/README.md | 6 ++++++ intl/crowdin_sync.sh | 27 +++++++++++++++++++++++++++ intl/json2h.py | 15 +++++---------- intl/msg_hash_us.h | 8 ++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) create mode 100755 intl/crowdin_sync.sh diff --git a/intl/.gitignore b/intl/.gitignore index d69d1a4252..1d6146afdd 100644 --- a/intl/.gitignore +++ b/intl/.gitignore @@ -1,2 +1,3 @@ *.json - +crowdin.yaml +crowdin.yml diff --git a/intl/README.md b/intl/README.md index 6398376d25..13721d9ad7 100644 --- a/intl/README.md +++ b/intl/README.md @@ -15,3 +15,9 @@ - No performance impact. - Don't require translators to know how to use Git, how to read C code and how to create Pull Request. - Translators will be informed whenever a source string changes. + +## Cron job setup + +1. [Install crowdin cli tool](https://support.crowdin.com/cli-tool/) +2. Get the `intl/crowdin.yaml` file from someone who have crowdin admin accounts +3. Add cron job `cd path/to/retroarch/intl && ./crowin_sync.sh` diff --git a/intl/crowdin_sync.sh b/intl/crowdin_sync.sh new file mode 100755 index 0000000000..084834bb3a --- /dev/null +++ b/intl/crowdin_sync.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Update source code +git pull + +# Convert source *.h to *.json +./h2json.py msg_hash_us.h + +# Upload source file +crowdin upload sources + +# Crowdin need some time to process data +sleep 1m + +# Download translation files +crowdin download translations + +# Convert translation *.json to *.h +for f in *.json +do + ./json2h.py $f +done + +# Commit new translations +git add . +git commit -m "Synchronize translations" +git push diff --git a/intl/json2h.py b/intl/json2h.py index 511e1c37b0..1b4ea13372 100755 --- a/intl/json2h.py +++ b/intl/json2h.py @@ -14,16 +14,11 @@ except IndexError: print("Usage: ./template.py ") sys.exit(1) -p = re.compile('MSG_HASH\(\s*[A-Z0-9_]+,\s*\".*\"\s*\)') +if json_filename == 'msg_hash_us.json': + print("Skip") + sys.exit(0) -header = """#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900) -#if (_MSC_VER >= 1700) -/* https://support.microsoft.com/en-us/kb/980263 */ -#pragma execution_character_set("utf-8") -#endif -#pragma warning(disable:4566) -#endif -""" +p = re.compile('MSG_HASH\(\s*[A-Z0-9_]+,\s*\".*\"\s*\)') def parse_message(message): @@ -53,7 +48,7 @@ def parse_messages(text): def update(messages, template): - new_translation = header + template + new_translation = template template_messages = parse_messages(template) for tp_msg in template_messages: if tp_msg['key'] in messages: diff --git a/intl/msg_hash_us.h b/intl/msg_hash_us.h index 8d3a823808..8587804495 100644 --- a/intl/msg_hash_us.h +++ b/intl/msg_hash_us.h @@ -1,3 +1,11 @@ +#if defined(_MSC_VER) && !defined(_XBOX) && (_MSC_VER >= 1500 && _MSC_VER < 1900) +#if (_MSC_VER >= 1700) +/* https://support.microsoft.com/en-us/kb/980263 */ +#pragma execution_character_set("utf-8") +#endif +#pragma warning(disable:4566) +#endif + #ifdef HAVE_LAKKA_SWITCH MSG_HASH( MENU_ENUM_LABEL_VALUE_SWITCH_GPU_PROFILE,