GitHub user mike-jumper opened a pull request: https://github.com/apache/guacamole-client/pull/356
GUACAMOLE-699: Add helper script for verifying translations. This change adds a utility script, `guacamole/util/check-translation.py`, which performs automated comparisons between the JSON files that define Guacamole's translated strings. By default, strings which are present in English but absent in the translation will be listed (these will need to be added), as well as strings which are present in the translation but not in English (these are likely incorrect and should be removed). These checks can be disabled with command line options, if desired. An additional duplicate string check can be enabled to test whether strings were incorrectly copied verbatim from English without being translated at all. This check includes some obvious exclusions (empty strings, simple numbers, protocol names, "Apache Guacamole"), but will likely produce false positives. It is disabled by default unless requested on the command line. Usage ----- ``` usage: check-translation.py [-h] [--no-missing] [--no-unused] [--check-copied] [ORIGINAL] TRANSLATED Compares two JSON translation files, as used by the Apache Guacamole web application, listing the strings which appear to be missing or incorrect. positional arguments: ORIGINAL The JSON file which should be used as the basis for comparison. This should be JSON which can be expected to contain every string used by the web application and no others. Typically, this will be the primary, original language of the web application. In the case of Apache Guacamole, this should be English. If omitted, the file "en.json" within the same directory as TRANSLATED will be used by default. TRANSLATED The JSON file which should be compared against ORIGINAL. This should be the JSON which has been translated from ORIGINAL, and thus should contain the same set of strings if the translation is complete. optional arguments: -h, --help show this help message and exit --no-missing Disables checking for strings which are present in ORIGINAL but are missing from TRANSLATED. Assuming ORIGINAL represents the set of strings actually used by the web application, these strings are those which are missing and need to be defined for the translation to be complete. By default, the comparison will check for missing translations. --no-unused Disables checking for strings which are present in TRANSLATED but not in ORIGINAL. Assuming ORIGINAL represents the set of strings actually used by the web application, these strings are those which are defined by the translation but unused. By default, the comparison will check for unused translations. --check-copied Enables checking for strings defined in TRANSLATED which are identical to the corresponding strings in ORIGINAL. Such strings may have been incorrectly copied verbatim from the original without being translated at all. It is also possible that both languages simply use the same text for that string, and the string is correct. As this test can produce false positives, it is disabled by default. ``` Example output ------------- ``` [mjumper@dev-mjumper guacamole]$ ./util/check-translation.py --check-copied src/main/webapp/translations/de.json Original language: en (English) Translation language: de (Deutsch) The following strings are missing from the translation and should be added: APP.ACTION_DOWNLOAD APP.ACTION_MANAGE_USER_GROUPS APP.ACTION_SHARE APP.ERROR_PAGE_UNAVAILABLE APP.TEXT_ANONYMOUS_USER CLIENT.ACTION_SHARE CLIENT.ERROR_CLIENT_207 CLIENT.ERROR_CLIENT_208 CLIENT.ERROR_CLIENT_209 CLIENT.ERROR_CLIENT_20A CLIENT.ERROR_CLIENT_20B CLIENT.ERROR_TUNNEL_207 CLIENT.ERROR_TUNNEL_208 CLIENT.HELP_SHARE_LINK CLIENT.INFO_CONNECTION_SHARED CLIENT.TEXT_CLIENT_STATUS_UNSTABLE LIST.TEXT_ANONYMOUS_USER MANAGE_CONNECTION.TABLE_HEADER_HISTORY_REMOTEHOST MANAGE_CONNECTION_GROUP.ACTION_CLONE MANAGE_SHARING_PROFILE.DIALOG_HEADER_CONFIRM_DELETE MANAGE_SHARING_PROFILE.FIELD_HEADER_PRIMARY_CONNECTION MANAGE_SHARING_PROFILE.SECTION_HEADER_EDIT_SHARING_PROFILE MANAGE_SHARING_PROFILE.TEXT_CONFIRM_DELETE MANAGE_USER.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES MANAGE_USER.FIELD_HEADER_CREATE_NEW_USER_GROUPS MANAGE_USER.HELP_NO_USER_GROUPS MANAGE_USER.INFO_NO_USER_GROUPS_AVAILABLE MANAGE_USER.SECTION_HEADER_ALL_CONNECTIONS MANAGE_USER.SECTION_HEADER_CURRENT_CONNECTIONS MANAGE_USER.SECTION_HEADER_USER_GROUPS MANAGE_USER_GROUP.ACTION_ACKNOWLEDGE MANAGE_USER_GROUP.ACTION_CANCEL MANAGE_USER_GROUP.ACTION_CLONE MANAGE_USER_GROUP.ACTION_DELETE MANAGE_USER_GROUP.ACTION_SAVE MANAGE_USER_GROUP.DIALOG_HEADER_CONFIRM_DELETE MANAGE_USER_GROUP.DIALOG_HEADER_ERROR MANAGE_USER_GROUP.FIELD_HEADER_ADMINISTER_SYSTEM MANAGE_USER_GROUP.FIELD_HEADER_CHANGE_OWN_PASSWORD MANAGE_USER_GROUP.FIELD_HEADER_CREATE_NEW_CONNECTIONS MANAGE_USER_GROUP.FIELD_HEADER_CREATE_NEW_CONNECTION_GROUPS MANAGE_USER_GROUP.FIELD_HEADER_CREATE_NEW_SHARING_PROFILES MANAGE_USER_GROUP.FIELD_HEADER_CREATE_NEW_USERS MANAGE_USER_GROUP.FIELD_HEADER_CREATE_NEW_USER_GROUPS MANAGE_USER_GROUP.FIELD_HEADER_USER_GROUP_NAME MANAGE_USER_GROUP.FIELD_PLACEHOLDER_FILTER MANAGE_USER_GROUP.HELP_NO_MEMBER_USERS MANAGE_USER_GROUP.HELP_NO_MEMBER_USER_GROUPS MANAGE_USER_GROUP.HELP_NO_USER_GROUPS MANAGE_USER_GROUP.INFO_NO_USERS_AVAILABLE MANAGE_USER_GROUP.INFO_NO_USER_GROUPS_AVAILABLE MANAGE_USER_GROUP.INFO_READ_ONLY MANAGE_USER_GROUP.SECTION_HEADER_ALL_CONNECTIONS MANAGE_USER_GROUP.SECTION_HEADER_CONNECTIONS MANAGE_USER_GROUP.SECTION_HEADER_CURRENT_CONNECTIONS MANAGE_USER_GROUP.SECTION_HEADER_EDIT_USER_GROUP MANAGE_USER_GROUP.SECTION_HEADER_MEMBER_USERS MANAGE_USER_GROUP.SECTION_HEADER_MEMBER_USER_GROUPS MANAGE_USER_GROUP.SECTION_HEADER_PERMISSIONS MANAGE_USER_GROUP.SECTION_HEADER_USER_GROUPS MANAGE_USER_GROUP.TEXT_CONFIRM_DELETE PROTOCOL_RDP.FIELD_HEADER_CREATE_RECORDING_PATH PROTOCOL_RDP.FIELD_HEADER_DISABLE_BITMAP_CACHING PROTOCOL_RDP.FIELD_HEADER_DISABLE_GLYPH_CACHING PROTOCOL_RDP.FIELD_HEADER_DISABLE_OFFSCREEN_CACHING PROTOCOL_RDP.FIELD_HEADER_DRIVE_NAME PROTOCOL_RDP.FIELD_HEADER_ENABLE_AUDIO_INPUT PROTOCOL_RDP.FIELD_HEADER_LOAD_BALANCE_INFO PROTOCOL_RDP.FIELD_HEADER_PRECONNECTION_BLOB PROTOCOL_RDP.FIELD_HEADER_PRECONNECTION_ID PROTOCOL_RDP.FIELD_HEADER_PRINTER_NAME PROTOCOL_RDP.FIELD_HEADER_RECORDING_EXCLUDE_MOUSE PROTOCOL_RDP.FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT PROTOCOL_RDP.FIELD_HEADER_RECORDING_INCLUDE_KEYS PROTOCOL_RDP.FIELD_HEADER_RECORDING_NAME PROTOCOL_RDP.FIELD_HEADER_RECORDING_PATH PROTOCOL_RDP.FIELD_HEADER_RESIZE_METHOD PROTOCOL_RDP.FIELD_HEADER_SFTP_HOST_KEY PROTOCOL_RDP.FIELD_HEADER_SFTP_ROOT_DIRECTORY PROTOCOL_RDP.FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL PROTOCOL_RDP.FIELD_OPTION_RESIZE_METHOD_DISPLAY_UPDATE PROTOCOL_RDP.FIELD_OPTION_RESIZE_METHOD_EMPTY PROTOCOL_RDP.FIELD_OPTION_RESIZE_METHOD_RECONNECT PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_EN_GB_QWERTY PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_ES_ES_QWERTY PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_FR_CH_QWERTZ PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_JA_JP_QWERTY PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_PT_BR_QWERTY PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_TR_TR_QWERTY PROTOCOL_RDP.SECTION_HEADER_GATEWAY PROTOCOL_RDP.SECTION_HEADER_LOAD_BALANCING PROTOCOL_RDP.SECTION_HEADER_PRECONNECTION_PDU PROTOCOL_RDP.SECTION_HEADER_RECORDING PROTOCOL_SSH.FIELD_HEADER_BACKSPACE PROTOCOL_SSH.FIELD_HEADER_CREATE_RECORDING_PATH PROTOCOL_SSH.FIELD_HEADER_CREATE_TYPESCRIPT_PATH PROTOCOL_SSH.FIELD_HEADER_HOST_KEY PROTOCOL_SSH.FIELD_HEADER_RECORDING_EXCLUDE_MOUSE PROTOCOL_SSH.FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT PROTOCOL_SSH.FIELD_HEADER_RECORDING_INCLUDE_KEYS PROTOCOL_SSH.FIELD_HEADER_RECORDING_NAME PROTOCOL_SSH.FIELD_HEADER_RECORDING_PATH PROTOCOL_SSH.FIELD_HEADER_SERVER_ALIVE_INTERVAL PROTOCOL_SSH.FIELD_HEADER_SFTP_ROOT_DIRECTORY PROTOCOL_SSH.FIELD_HEADER_TERMINAL_TYPE PROTOCOL_SSH.FIELD_HEADER_TYPESCRIPT_NAME PROTOCOL_SSH.FIELD_HEADER_TYPESCRIPT_PATH PROTOCOL_SSH.FIELD_OPTION_BACKSPACE_127 PROTOCOL_SSH.FIELD_OPTION_BACKSPACE_8 PROTOCOL_SSH.FIELD_OPTION_BACKSPACE_EMPTY PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_ANSI PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_EMPTY PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_LINUX PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_VT100 PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_VT220 PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_XTERM PROTOCOL_SSH.FIELD_OPTION_TERMINAL_TYPE_XTERM_256COLOR PROTOCOL_SSH.SECTION_HEADER_BEHAVIOR PROTOCOL_SSH.SECTION_HEADER_RECORDING PROTOCOL_SSH.SECTION_HEADER_TYPESCRIPT PROTOCOL_TELNET.FIELD_HEADER_BACKSPACE PROTOCOL_TELNET.FIELD_HEADER_CREATE_RECORDING_PATH PROTOCOL_TELNET.FIELD_HEADER_CREATE_TYPESCRIPT_PATH PROTOCOL_TELNET.FIELD_HEADER_LOGIN_FAILURE_REGEX PROTOCOL_TELNET.FIELD_HEADER_LOGIN_SUCCESS_REGEX PROTOCOL_TELNET.FIELD_HEADER_RECORDING_EXCLUDE_MOUSE PROTOCOL_TELNET.FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT PROTOCOL_TELNET.FIELD_HEADER_RECORDING_INCLUDE_KEYS PROTOCOL_TELNET.FIELD_HEADER_RECORDING_NAME PROTOCOL_TELNET.FIELD_HEADER_RECORDING_PATH PROTOCOL_TELNET.FIELD_HEADER_TERMINAL_TYPE PROTOCOL_TELNET.FIELD_HEADER_TYPESCRIPT_NAME PROTOCOL_TELNET.FIELD_HEADER_TYPESCRIPT_PATH PROTOCOL_TELNET.FIELD_HEADER_USERNAME_REGEX PROTOCOL_TELNET.FIELD_OPTION_BACKSPACE_127 PROTOCOL_TELNET.FIELD_OPTION_BACKSPACE_8 PROTOCOL_TELNET.FIELD_OPTION_BACKSPACE_EMPTY PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_ANSI PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_EMPTY PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_LINUX PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_VT100 PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_VT220 PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_XTERM PROTOCOL_TELNET.FIELD_OPTION_TERMINAL_TYPE_XTERM_256COLOR PROTOCOL_TELNET.SECTION_HEADER_BEHAVIOR PROTOCOL_TELNET.SECTION_HEADER_RECORDING PROTOCOL_TELNET.SECTION_HEADER_TYPESCRIPT PROTOCOL_VNC.FIELD_HEADER_CREATE_RECORDING_PATH PROTOCOL_VNC.FIELD_HEADER_RECORDING_EXCLUDE_MOUSE PROTOCOL_VNC.FIELD_HEADER_RECORDING_EXCLUDE_OUTPUT PROTOCOL_VNC.FIELD_HEADER_RECORDING_INCLUDE_KEYS PROTOCOL_VNC.FIELD_HEADER_RECORDING_NAME PROTOCOL_VNC.FIELD_HEADER_RECORDING_PATH PROTOCOL_VNC.FIELD_HEADER_SFTP_HOST_KEY PROTOCOL_VNC.FIELD_HEADER_SFTP_ROOT_DIRECTORY PROTOCOL_VNC.FIELD_HEADER_SFTP_SERVER_ALIVE_INTERVAL PROTOCOL_VNC.SECTION_HEADER_RECORDING SETTINGS_CONNECTIONS.ACTION_NEW_SHARING_PROFILE SETTINGS_CONNECTION_HISTORY.ACTION_DOWNLOAD SETTINGS_CONNECTION_HISTORY.FILENAME_HISTORY_CSV SETTINGS_CONNECTION_HISTORY.TABLE_HEADER_SESSION_REMOTEHOST SETTINGS_USERS.TABLE_HEADER_FULL_NAME SETTINGS_USERS.TABLE_HEADER_LAST_ACTIVE SETTINGS_USERS.TABLE_HEADER_ORGANIZATION SETTINGS_USER_GROUPS.ACTION_ACKNOWLEDGE SETTINGS_USER_GROUPS.ACTION_NEW_USER_GROUP SETTINGS_USER_GROUPS.DIALOG_HEADER_ERROR SETTINGS_USER_GROUPS.FIELD_PLACEHOLDER_FILTER SETTINGS_USER_GROUPS.FORMAT_DATE SETTINGS_USER_GROUPS.HELP_USER_GROUPS SETTINGS_USER_GROUPS.SECTION_HEADER_USER_GROUPS SETTINGS_USER_GROUPS.TABLE_HEADER_USER_GROUP_NAME USER_ATTRIBUTES.FIELD_HEADER_GUAC_EMAIL_ADDRESS USER_ATTRIBUTES.FIELD_HEADER_GUAC_FULL_NAME USER_ATTRIBUTES.FIELD_HEADER_GUAC_ORGANIZATION USER_ATTRIBUTES.FIELD_HEADER_GUAC_ORGANIZATIONAL_ROLE USER_MENU.ACTION_MANAGE_USER_GROUPS USER_MENU.ACTION_VIEW_HISTORY The following strings are either NOT defined for the original language or are expected to be inherited from the original language and should be removed: CLIENT.ERROR_CLIENT_205 The following strings are identical to the original language and MIGHT be untranslated: APP.ACTION_ACKNOWLEDGE APP.FIELD_PLACEHOLDER_FILTER CLIENT.NAME_KEY_ALT CLIENT.NAME_KEY_ESC CLIENT.NAME_KEY_TAB CLIENT.NAME_MOUSE_MODE_ABSOLUTE CLIENT.NAME_MOUSE_MODE_RELATIVE CLIENT.TEXT_FILE_TRANSFER_PROGRESS FORM.FIELD_PLACEHOLDER_DATE FORM.FIELD_PLACEHOLDER_TIME MANAGE_CONNECTION.FIELD_HEADER_NAME MANAGE_CONNECTION.INFO_CONNECTION_DURATION_UNKNOWN MANAGE_CONNECTION_GROUP.FIELD_HEADER_NAME MANAGE_CONNECTION_GROUP.NAME_TYPE_BALANCING MANAGE_SHARING_PROFILE.FIELD_HEADER_NAME PROTOCOL_RDP.FIELD_HEADER_GATEWAY_HOSTNAME PROTOCOL_RDP.FIELD_HEADER_GATEWAY_PORT PROTOCOL_RDP.FIELD_HEADER_HOSTNAME PROTOCOL_RDP.FIELD_HEADER_PORT PROTOCOL_RDP.FIELD_HEADER_SFTP_HOSTNAME PROTOCOL_RDP.FIELD_HEADER_SFTP_PASSPHRASE PROTOCOL_RDP.FIELD_HEADER_SFTP_PORT PROTOCOL_RDP.FIELD_OPTION_SERVER_LAYOUT_FAILSAFE PROTOCOL_SSH.FIELD_HEADER_HOSTNAME PROTOCOL_SSH.FIELD_HEADER_PASSPHRASE PROTOCOL_SSH.FIELD_HEADER_PORT PROTOCOL_TELNET.FIELD_HEADER_HOSTNAME PROTOCOL_TELNET.FIELD_HEADER_PORT PROTOCOL_VNC.FIELD_HEADER_CURSOR PROTOCOL_VNC.FIELD_HEADER_HOSTNAME PROTOCOL_VNC.FIELD_HEADER_PORT PROTOCOL_VNC.FIELD_HEADER_SFTP_HOSTNAME PROTOCOL_VNC.FIELD_HEADER_SFTP_PASSPHRASE PROTOCOL_VNC.FIELD_HEADER_SFTP_PORT PROTOCOL_VNC.FIELD_OPTION_CLIPBOARD_ENCODING_CP1252 PROTOCOL_VNC.FIELD_OPTION_CLIPBOARD_ENCODING_ISO8859_1 PROTOCOL_VNC.FIELD_OPTION_CLIPBOARD_ENCODING_UTF_16 PROTOCOL_VNC.FIELD_OPTION_CLIPBOARD_ENCODING_UTF_8 PROTOCOL_VNC.SECTION_HEADER_AUDIO PROTOCOL_VNC.SECTION_HEADER_REPEATER SETTINGS_CONNECTION_HISTORY.INFO_CONNECTION_DURATION_UNKNOWN 220 error(s) total. [mjumper@dev-mjumper guacamole]$ ``` You can merge this pull request into a Git repository by running: $ git pull https://github.com/mike-jumper/guacamole-client translator-utils Alternatively you can review and apply these changes as the patch at: https://github.com/apache/guacamole-client/pull/356.patch To close this pull request, make a commit to your master/trunk branch with (at least) the following in the commit message: This closes #356 ---- commit 28d5a4603d0786ce88e591218ee800f5ebc3480d Author: Michael Jumper <mjumper@...> Date: 2019-01-12T09:09:28Z GUACAMOLE-699: Add helper script for verifying translations. ---- ---