From 5025bc258cdeabbd498919d5c6b4554accdad842 Mon Sep 17 00:00:00 2001 From: Michal Labedzki Date: Sat, 21 Dec 2013 17:55:43 +0100 Subject: [PATCH] Qt: Dynamic languages - get language as soon as possible (before creating any Qt objects) to make all translations working - dynamic list of supported languages - runtime change of GUI language (no need to restart application) - add flags icons support - search for *.qm languages in buildin resources, then data dir called "languages" (main directory in sources or /usr/share/wireshark/languages), then user directory (UNIX: ~/.wireshark/languages); "languages" directory should contains files wireshark_xx.qm where xx is language code (en, en_GB, etc.), and optional xx.svg for flag icon - try to fix some untranslated manually-created UI items (need manual reset text of those components) Change-Id: I62ca8a8cddce47cec9dbcad6b0bd68b6cfd92229 Reviewed-on: https://code.wireshark.org/review/5041 Tested-by: Michal Labedzki Reviewed-by: Michal Labedzki --- doc/README.qt | 29 +++-- epan/prefs.c | 18 ---- image/languages/de.svg | 9 ++ image/languages/en.svg | 10 ++ image/languages/fr.svg | 6 ++ image/languages/it.svg | 6 ++ image/languages/ja_JP.svg | 6 ++ image/languages/languages.qrc | 11 ++ image/languages/pl.svg | 1 + image/languages/zh_CN.svg | 15 +++ ui/CMakeLists.txt | 1 + ui/Makefile.common | 2 + ui/language.c | 135 ++++++++++++++++++++++++ ui/language.h | 53 ++++++++++ ui/qt/CMakeLists.txt | 1 + ui/qt/Makefile.common | 1 + ui/qt/Wireshark.pro | 1 + ui/qt/capture_interfaces_dialog.cpp | 16 +++ ui/qt/capture_interfaces_dialog.h | 1 + ui/qt/display_filter_edit.cpp | 22 ++++ ui/qt/display_filter_edit.h | 2 + ui/qt/main.cpp | 48 ++------- ui/qt/main_welcome.cpp | 16 +++ ui/qt/main_welcome.h | 1 + ui/qt/main_window.cpp | 13 ++- ui/qt/main_window.h | 2 +- ui/qt/main_window.ui | 30 ++++++ ui/qt/main_window_preferences_frame.cpp | 58 +++++++++- ui/qt/main_window_preferences_frame.h | 1 - ui/qt/main_window_preferences_frame.ui | 48 +-------- ui/qt/packet_list.cpp | 59 +++++++---- ui/qt/preferences_dialog.cpp | 6 +- ui/qt/proto_tree.cpp | 40 ++++--- ui/qt/proto_tree.h | 6 +- ui/qt/search_frame.cpp | 16 +++ ui/qt/search_frame.h | 4 +- ui/qt/summary_dialog.cpp | 16 +++ ui/qt/summary_dialog.h | 1 + ui/qt/wireshark_application.cpp | 40 +++++++ ui/qt/wireshark_application.h | 6 ++ 40 files changed, 603 insertions(+), 154 deletions(-) create mode 100644 image/languages/de.svg create mode 100644 image/languages/en.svg create mode 100644 image/languages/fr.svg create mode 100644 image/languages/it.svg create mode 100644 image/languages/ja_JP.svg create mode 100644 image/languages/languages.qrc create mode 100644 image/languages/pl.svg create mode 100644 image/languages/zh_CN.svg create mode 100644 ui/language.c create mode 100644 ui/language.h diff --git a/doc/README.qt b/doc/README.qt index 93d646bdca..d3dac582dd 100644 --- a/doc/README.qt +++ b/doc/README.qt @@ -216,16 +216,33 @@ http://www.parashift.com/c++-faq/mixing-c-and-cpp.html 3. Translations (i18n) +3.1 Make translation + Qt makes translating the Wireshark UI into different languages easy. - Add your translation (ui/qt/wireshark_XX.ts) in ui/qt/Wireshark.pro, ui/qt/i18n.qrc, - ui/qt/Makefile.common and ui/qt/CMakeLists.txt . -- Add also in epan/prefs.c, in ui/qt/main.cpp add case in switch(prefs_p->gui_qt_language) - and in ui/qt/main_window_preferences_frame.ui (use qt-design) to add in prefs. -- Run "lupdate Wireshark.pro" to generate/update your translation file. -- Translate with Qt Linguist. -- Run "lrelease Wireshark.pro" to create/update wireshark_xx.qm file. + ui/qt/Makefile.common and ui/qt/CMakeLists.txt +- Please add flag (image) for your language in images/languages/XX.svg and image/languages/languages.qrc +- Run "lupdate ui/qt/Wireshark.pro" to generate/update your translation file. + or "lupdate ui/qt -ts ui/qt/wireshark_XX.ts" for specified translation +- Translate with Qt Linguist (in console: "linguist ui/qt/wireshark_XX.ts") +- Run "lrelease Wireshark.pro" or + "lrelease ui/qt/wireshark_XX.ts -qm ui/qt/wireshark_XX.qm" to create/update wireshark_XX.qm file. - Push your translation to Gerrit for review ("git push"). +Alternatively you can only put your QM and flag files in "languages" directory in +Wireshark user configuration directory (~/.wireshark/languages/ on unix) + More information about Qt Linguist http://qt-project.org/doc/qt-4.8/linguist-manual.html + +3.2 Developing + +- Please avoid tr() in code, try add any strings in *.ui files; tr() on manually + created object (like QMenu) are not automatically retranslated, so you must + add a couple of code to manually translate them + NOTE: if your object life is short, so any time when your component needs to be shown + it is (re)created then it is ok to have tr() in code +- For creating submenu in context menu please follow solution from ui/qt/proto_tree.cpp +- Some new windows need also to override changeEvent() and do retranslateUi() like + summary_dialog.[ch] does \ No newline at end of file diff --git a/epan/prefs.c b/epan/prefs.c index 0c59897e1c..0c302168ea 100644 --- a/epan/prefs.c +++ b/epan/prefs.c @@ -1261,18 +1261,6 @@ static const enum_val_t print_dest_vals[] = { { NULL, NULL, 0 } }; -static const enum_val_t gui_qt_language[] = { - {"Auto-Detect", "auto", 0}, - {"English", "en", 1}, - {"French", "fr", 2}, - {"German", "de", 3}, - {"Chinese", "zh_CN", 4}, - {"Polish", "pl", 5}, - {"Japanese", "ja_JP", 6}, - {"Italian", "it", 7}, - {NULL, NULL, -1} -}; - static const enum_val_t st_sort_col_vals[] = { { "name", "Node name (topic/item)", ST_SORT_COL_NAME }, { "count", "Item count", ST_SORT_COL_COUNT }, @@ -2332,11 +2320,6 @@ prefs_register_modules(void) 10, &prefs.gui_auto_scroll_percentage); - prefs_register_enum_preference(gui_module, "qt_language", - "Qt Language", - "Qt Language", - &prefs.gui_qt_language, gui_qt_language, FALSE); - /* User Interface : Layout */ gui_layout_module = prefs_register_subtree(gui_module, "Layout", "Layout", gui_layout_callback); @@ -2970,7 +2953,6 @@ pre_init_prefs(void) prefs.gui_layout_content_1 = layout_pane_content_plist; prefs.gui_layout_content_2 = layout_pane_content_pdetails; prefs.gui_layout_content_3 = layout_pane_content_pbytes; - prefs.gui_qt_language = 0; /* (Auto-Detect) */ prefs.gui_packet_editor = FALSE; prefs.col_list = NULL; diff --git a/image/languages/de.svg b/image/languages/de.svg new file mode 100644 index 0000000000..21d26c1057 --- /dev/null +++ b/image/languages/de.svg @@ -0,0 +1,9 @@ + + + + Flag of Germany + + + + diff --git a/image/languages/en.svg b/image/languages/en.svg new file mode 100644 index 0000000000..36c9889743 --- /dev/null +++ b/image/languages/en.svg @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/image/languages/fr.svg b/image/languages/fr.svg new file mode 100644 index 0000000000..a4bdfc9314 --- /dev/null +++ b/image/languages/fr.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/image/languages/it.svg b/image/languages/it.svg new file mode 100644 index 0000000000..3ac830a963 --- /dev/null +++ b/image/languages/it.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/image/languages/ja_JP.svg b/image/languages/ja_JP.svg new file mode 100644 index 0000000000..31a29c7fdb --- /dev/null +++ b/image/languages/ja_JP.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/image/languages/languages.qrc b/image/languages/languages.qrc new file mode 100644 index 0000000000..3ceca22450 --- /dev/null +++ b/image/languages/languages.qrc @@ -0,0 +1,11 @@ + + + de.svg + en.svg + fr.svg + it.svg + ja_JP.svg + pl.svg + zh_CN.svg + + diff --git a/image/languages/pl.svg b/image/languages/pl.svg new file mode 100644 index 0000000000..909cb7230f --- /dev/null +++ b/image/languages/pl.svg @@ -0,0 +1 @@ + diff --git a/image/languages/zh_CN.svg b/image/languages/zh_CN.svg new file mode 100644 index 0000000000..287704c693 --- /dev/null +++ b/image/languages/zh_CN.svg @@ -0,0 +1,15 @@ + + + +Flag of the People's Republic of China + + + + + + + + + + diff --git a/ui/CMakeLists.txt b/ui/CMakeLists.txt index f964c3a3cc..12deadfed4 100644 --- a/ui/CMakeLists.txt +++ b/ui/CMakeLists.txt @@ -36,6 +36,7 @@ set(COMMON_UI_SRC help_url.c iface_lists.c io_graph_item.c + language.c packet_list_utils.c persfilepath_opt.c preference_utils.c diff --git a/ui/Makefile.common b/ui/Makefile.common index 9da5fb17ba..1be7dc1c59 100644 --- a/ui/Makefile.common +++ b/ui/Makefile.common @@ -56,6 +56,7 @@ WIRESHARK_UI_SRC = \ follow.c \ iface_lists.c \ io_graph_item.c \ + language.c \ help_url.c \ packet_list_utils.c \ persfilepath_opt.c \ @@ -92,6 +93,7 @@ noinst_HEADERS = \ packet_list_utils.h \ iface_lists.h \ io_graph_item.h \ + language.h \ main_statusbar.h \ persfilepath_opt.h \ preference_utils.h \ diff --git a/ui/language.c b/ui/language.c new file mode 100644 index 0000000000..9e6af04f72 --- /dev/null +++ b/ui/language.c @@ -0,0 +1,135 @@ +/* language.c + * Language "preference" handling routines + * Copyright 2014, Michal Labedzki for Tieto Corporation + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#include "config.h" + +#include +#include + +#include +#include +#include + +#include +#include + +#include "ui/language.h" +#include "ui/simple_dialog.h" + +#define LANGUAGE_FILE_NAME "language" +#define LANGUAGE_PREF_LANGUAGE "language" + +char *language = NULL; + +/* set one user's recent common file key/value pair */ +static prefs_set_pref_e +read_language_pref(gchar *key, const gchar *value, + void *private_data _U_, gboolean return_range_errors _U_) +{ + if (strcmp(key, LANGUAGE_PREF_LANGUAGE) == 0) { + if (language) + g_free(language); + if (!value || (value && !*value)) + language = g_strdup("auto"); + else + language = g_strdup(value); + } + + return PREFS_SET_OK; +} + +void +read_language_prefs(void) +{ + char *rf_path; + FILE *rf; + + rf_path = get_persconffile_path(LANGUAGE_FILE_NAME, FALSE); + + if ((rf = ws_fopen(rf_path, "r")) != NULL) { + read_prefs_file(rf_path, rf, read_language_pref, NULL); + + fclose(rf); + } + + g_free(rf_path); +} + +gboolean +write_language_prefs(void) +{ + char *pf_dir_path; + char *rf_path; + FILE *rf; + + /* To do: + * - Split output lines longer than MAX_VAL_LEN + * - Create a function for the preference directory check/creation + * so that duplication can be avoided with filter.c + */ + + /* Create the directory that holds personal configuration files, if + necessary. */ + if (create_persconffile_dir(&pf_dir_path) == -1) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't create directory\n\"%s\"\nfor language file: %s.", pf_dir_path, + g_strerror(errno)); + g_free(pf_dir_path); + return FALSE; + } + + rf_path = get_persconffile_path(LANGUAGE_FILE_NAME, FALSE); + if ((rf = ws_fopen(rf_path, "w")) == NULL) { + simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, + "Can't open recent file\n\"%s\": %s.", rf_path, + g_strerror(errno)); + g_free(rf_path); + return FALSE; + } + g_free(rf_path); + + fputs("# Language settings file for Wireshark " VERSION ".\n" + "#\n" + "# This file is regenerated each time Wireshark is quit.\n" + "# So be careful, if you want to make manual changes here.\n" + "\n", rf); + + fprintf(rf, LANGUAGE_PREF_LANGUAGE ": %s\n", language); + + fclose(rf); + + return TRUE; +} + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/language.h b/ui/language.h new file mode 100644 index 0000000000..8b94d23f69 --- /dev/null +++ b/ui/language.h @@ -0,0 +1,53 @@ +/* language.h + * + * Copyright 2014, Michal Labedzki for Tieto Corporation + * + * Wireshark - Network traffic analyzer + * By Gerald Combs + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef __LANGUAGE_H__ +#define __LANGUAGE_H__ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +extern char *language; + +extern void read_language_prefs(void); +extern int write_language_prefs(void); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + +#endif /* language.h */ + +/* + * Editor modelines - http://www.wireshark.org/tools/modelines.html + * + * Local variables: + * c-basic-offset: 4 + * tab-width: 8 + * indent-tabs-mode: nil + * End: + * + * vi: set shiftwidth=4 tabstop=8 expandtab: + * :indentSize=4:tabSize=8:noTabs=true: + */ diff --git a/ui/qt/CMakeLists.txt b/ui/qt/CMakeLists.txt index 0192d47feb..dc01df4f08 100644 --- a/ui/qt/CMakeLists.txt +++ b/ui/qt/CMakeLists.txt @@ -280,6 +280,7 @@ endif() set(WIRESHARK_QT_QRC ../../image/about.qrc ../../image/display_filter.qrc + ../../image/languages/languages.qrc ../../image/layout.qrc ../../image/status.qrc ../../image/toolbar.qrc diff --git a/ui/qt/Makefile.common b/ui/qt/Makefile.common index 250aac0ae2..a32ca8bc1c 100644 --- a/ui/qt/Makefile.common +++ b/ui/qt/Makefile.common @@ -261,6 +261,7 @@ MOC_SRC = $(MOC_HDRS:.h=.moc.cpp) QRC_FILES = \ ../../image/about.qrc \ ../../image/display_filter.qrc \ + ../../image/languages/languages.qrc \ ../../image/layout.qrc \ ../../image/status.qrc \ ../../image/toolbar.qrc \ diff --git a/ui/qt/Wireshark.pro b/ui/qt/Wireshark.pro index 9a6f775b93..4c3e4db3a8 100644 --- a/ui/qt/Wireshark.pro +++ b/ui/qt/Wireshark.pro @@ -496,6 +496,7 @@ win32 { RESOURCES += \ ../../image/about.qrc \ ../../image/display_filter.qrc \ + ../../image/languages/languages.qrc \ ../../image/layout.qrc \ ../../image/status.qrc \ ../../image/toolbar.qrc \ diff --git a/ui/qt/capture_interfaces_dialog.cpp b/ui/qt/capture_interfaces_dialog.cpp index 3da8e79a05..34f305174d 100644 --- a/ui/qt/capture_interfaces_dialog.cpp +++ b/ui/qt/capture_interfaces_dialog.cpp @@ -851,6 +851,22 @@ void CaptureInterfacesDialog::on_manage_clicked() } } +void CaptureInterfacesDialog::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + // // InterfaceTreeItem // diff --git a/ui/qt/capture_interfaces_dialog.h b/ui/qt/capture_interfaces_dialog.h index f46842d0d3..5ec54d7f6a 100644 --- a/ui/qt/capture_interfaces_dialog.h +++ b/ui/qt/capture_interfaces_dialog.h @@ -99,6 +99,7 @@ private slots: void refreshInterfaceList(); void updateLocalInterfaces(); void on_browseButton_clicked(); + void changeEvent(QEvent* event); signals: void startCapture(); diff --git a/ui/qt/display_filter_edit.cpp b/ui/qt/display_filter_edit.cpp index ea01dcca14..7d80f76697 100644 --- a/ui/qt/display_filter_edit.cpp +++ b/ui/qt/display_filter_edit.cpp @@ -327,6 +327,28 @@ void DisplayFilterEdit::displayFilterSuccess(bool success) apply_button_->setEnabled(!success); } +void DisplayFilterEdit::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + if (plain_) { + empty_filter_message_ = QString(tr("Enter a display filter %1")). + arg(UTF8_HORIZONTAL_ELLIPSIS); + } else { + empty_filter_message_ = QString(tr("Apply a display filter %1 <%2/>")) + .arg(UTF8_HORIZONTAL_ELLIPSIS).arg(DEFAULT_MODIFIER); + } + break; + default: + break; + } + } + SyntaxLineEdit::changeEvent(event); +} + /* * Editor modelines * diff --git a/ui/qt/display_filter_edit.h b/ui/qt/display_filter_edit.h index 72cde11aa7..896b9da09d 100644 --- a/ui/qt/display_filter_edit.h +++ b/ui/qt/display_filter_edit.h @@ -22,6 +22,7 @@ #ifndef DISPLAYFILTEREDIT_H #define DISPLAYFILTEREDIT_H +#include #include #include "syntax_line_edit.h" @@ -45,6 +46,7 @@ private slots: void checkFilter(const QString &text); void bookmarkClicked(); void clearFilter(); + void changeEvent(QEvent* event); private: bool plain_; diff --git a/ui/qt/main.cpp b/ui/qt/main.cpp index 0db57b1899..d1256f642c 100644 --- a/ui/qt/main.cpp +++ b/ui/qt/main.cpp @@ -101,6 +101,7 @@ #endif #include "ui/console.h" #include "ui/iface_lists.h" +#include "ui/language.h" #include "ui/main_statusbar.h" #include "ui/persfilepath_opt.h" #include "ui/recent.h" @@ -141,7 +142,6 @@ #if (QT_VERSION < QT_VERSION_CHECK(5, 0, 0)) #include #endif -#include #include "conversation_dialog.h" #include "endpoint_dialog.h" @@ -783,6 +783,13 @@ int main(int argc, char *argv[]) optind = optind_initial; opterr = 1; + // Initialize our language + read_language_prefs(); + locale = QString(language); + wsApp->loadLanguage(locale); + + g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Translator %s", locale.toStdString().c_str()); + // Init the main window (and splash) main_w = new(MainWindow); main_w->show(); @@ -884,46 +891,9 @@ int main(int argc, char *argv[]) } splash_update(RA_PREFERENCES, NULL, NULL); + prefs_p = ws_app.readConfigurationFiles (&gdp_path, &dp_path); - // Initialize our language - - /*TODO: Enhance... may be get the locale from the enum gui_qt_language */ - switch(prefs_p->gui_qt_language){ - case 1: /* English */ - locale = "en"; - break; - case 2: /* French */ - locale = "fr"; - break; - case 3: /* German */ - locale = "de"; - break; - case 4: /* Chinese */ - locale = "zh_CN"; - break; - case 5: /* Polish */ - locale = "pl"; - break; - case 6: /* Japanese */ - locale = "ja_JP"; - break; - case 7: /* Italian */ - locale = "it"; - break; - default: /* Auto-Detect */ - locale = QLocale::system().name(); - break; - } - g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Translator %s", locale.toStdString().c_str()); - QTranslator translator; - translator.load(QString(":/i18n/wireshark_") + locale); - wsApp->installTranslator(&translator); - - QTranslator qtTranslator; - qtTranslator.load("qt_" + locale, QLibraryInfo::location(QLibraryInfo::TranslationsPath)); - wsApp->installTranslator(&qtTranslator); - /* Now get our args */ while ((opt = getopt_long(argc, argv, optstring, long_options, NULL)) != -1) { switch (opt) { diff --git a/ui/qt/main_welcome.cpp b/ui/qt/main_welcome.cpp index 7c28bcde57..91c6954106 100644 --- a/ui/qt/main_welcome.cpp +++ b/ui/qt/main_welcome.cpp @@ -248,6 +248,22 @@ void MainWelcome::resizeEvent(QResizeEvent *event) QFrame::resizeEvent(event); } +void MainWelcome::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + welcome_ui_->retranslateUi(this); + break; + default: + break; + } + } + QFrame::changeEvent(event); +} + /* * Editor modelines * diff --git a/ui/qt/main_welcome.h b/ui/qt/main_welcome.h index 4f987eac44..3092c7d5c6 100644 --- a/ui/qt/main_welcome.h +++ b/ui/qt/main_welcome.h @@ -67,6 +67,7 @@ private slots: void interfaceDoubleClicked(QTreeWidgetItem *item, int column); void updateRecentFiles(); void openRecentItem(QListWidgetItem *item); + void changeEvent(QEvent* event); }; #endif // MAIN_WELCOME_H diff --git a/ui/qt/main_window.cpp b/ui/qt/main_window.cpp index ccc04a5e32..886b8395a4 100644 --- a/ui/qt/main_window.cpp +++ b/ui/qt/main_window.cpp @@ -1765,19 +1765,24 @@ void MainWindow::updateForUnsavedChanges() { void MainWindow::changeEvent(QEvent* event) { - if(0 != event) + if (0 != event) { - switch(event->type()) + switch (event->type()) { - // this event is send if a translator is loaded case QEvent::LanguageChange: main_ui_->retranslateUi(this); break; + case QEvent::LocaleChange:{ + QString locale = QLocale::system().name(); + locale.truncate(locale.lastIndexOf('_')); + wsApp->loadLanguage(locale); + } + break; default: break; } } - QMainWindow::changeEvent(event); + QMainWindow::changeEvent(event); } /* Update main window items based on whether there's a capture in progress. */ diff --git a/ui/qt/main_window.h b/ui/qt/main_window.h index 64426a8adf..5014dd51c8 100644 --- a/ui/qt/main_window.h +++ b/ui/qt/main_window.h @@ -87,7 +87,7 @@ public: protected: bool eventFilter(QObject *obj, QEvent *event); void keyPressEvent(QKeyEvent *event); - void closeEvent (QCloseEvent *event); + void closeEvent(QCloseEvent *event); private: // XXX Move to FilterUtils diff --git a/ui/qt/main_window.ui b/ui/qt/main_window.ui index 59d7c912c6..72f46fba4a 100644 --- a/ui/qt/main_window.ui +++ b/ui/qt/main_window.ui @@ -2103,6 +2103,36 @@ Show or hide the packet bytes + + + Follow... + + + + + SCTP + + + + + Apply as Filter + + + + + Prepare a Filter + + + + + Copy + + + + + Bytes + + diff --git a/ui/qt/main_window_preferences_frame.cpp b/ui/qt/main_window_preferences_frame.cpp index fca0479835..db4f5cabad 100644 --- a/ui/qt/main_window_preferences_frame.cpp +++ b/ui/qt/main_window_preferences_frame.cpp @@ -19,10 +19,15 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "wireshark_application.h" #include "main_window_preferences_frame.h" +#include "qt_ui_utils.h" + #include "ui_main_window_preferences_frame.h" +#include "ui/language.h" #include +#include #include #include @@ -45,7 +50,6 @@ MainWindowPreferencesFrame::MainWindowPreferencesFrame(QWidget *parent) : pref_auto_scroll_percentage_ = prefFromPrefPtr(&prefs.gui_auto_scroll_percentage); pref_toolbar_main_style_ = prefFromPrefPtr(&prefs.gui_toolbar_main_style); pref_toolbar_filter_style_ = prefFromPrefPtr(&prefs.gui_toolbar_filter_style); - pref_qt_language_ = prefFromPrefPtr(&prefs.gui_qt_language); QStyleOption style_opt; QString indent_ss = QString( @@ -64,6 +68,44 @@ MainWindowPreferencesFrame::MainWindowPreferencesFrame(QWidget *parent) : ui->maxFilterLineEdit->setMaximumWidth(num_entry_width); ui->maxRecentLineEdit->setMaximumWidth(num_entry_width); ui->autoScrollPercentageLineEdit->setMaximumWidth(num_entry_width); + + QString globalLanguagesPath(QString(get_datafile_dir()) + "/languages/"); + QString userLanguagesPath(gchar_free_to_qstring(get_persconffile_path("languages/", FALSE))); + + + + QStringList filenames = QDir(":/i18n/").entryList(QStringList("wireshark_*.qm")); + filenames += QDir(globalLanguagesPath).entryList(QStringList("wireshark_*.qm")); + filenames += QDir(userLanguagesPath).entryList(QStringList("wireshark_*.qm")); + + for (int i = 0; i < filenames.size(); i += 1) { + QString locale; + locale = filenames[i]; + locale.truncate(locale.lastIndexOf('.')); + locale.remove(0, locale.indexOf('_') + 1); + + QString lang = QLocale::languageToString(QLocale(locale).language()); + QIcon ico = QIcon(); + if (QFile::exists(QString(":/languages/%1.svg").arg(locale))) + ico.addFile(QString(":/languages/%1.svg").arg(locale)); + if (QFile::exists(globalLanguagesPath + locale + ".svg")) + ico.addFile(globalLanguagesPath + locale + ".svg"); + if (QFile::exists(userLanguagesPath + locale + ".svg")) + ico.addFile(userLanguagesPath + locale + ".svg"); + + ui->languageComboBox->addItem(ico, lang, locale); + } + + ui->languageComboBox->setItemData(0, "auto"); + ui->languageComboBox->model()->sort(0); + + for (int i = 0; i < ui->languageComboBox->count(); i += 1) { + if (QString(language) == ui->languageComboBox->itemData(i).toString()) { + ui->languageComboBox->setCurrentIndex(i); + break; + } + } + } MainWindowPreferencesFrame::~MainWindowPreferencesFrame() @@ -102,7 +144,14 @@ void MainWindowPreferencesFrame::updateWidgets() ui->mainToolbarComboBox->setCurrentIndex(pref_toolbar_main_style_->stashed_val.enumval); ui->filterToolbarComboBox->setCurrentIndex(pref_toolbar_filter_style_->stashed_val.enumval); - ui->languageComboBox->setCurrentIndex(pref_qt_language_->stashed_val.enumval); + + QStringList filenames = QDir(":/i18n/").entryList(QStringList("wireshark_*.qm")); + for (int i = 0; i < ui->languageComboBox->count(); i += 1) { + if (QString(language) == ui->languageComboBox->itemData(i).toString()) { + ui->languageComboBox->setCurrentIndex(i); + break; + } + } } void MainWindowPreferencesFrame::on_geometryCheckBox_toggled(bool checked) @@ -186,7 +235,10 @@ void MainWindowPreferencesFrame::on_filterToolbarComboBox_currentIndexChanged(in void MainWindowPreferencesFrame::on_languageComboBox_currentIndexChanged(int index) { - pref_qt_language_->stashed_val.enumval = index; + if (language) + delete language; + + language = strdup(ui->languageComboBox->itemData(index).toString().toStdString().c_str()); } /* diff --git a/ui/qt/main_window_preferences_frame.h b/ui/qt/main_window_preferences_frame.h index 178b5dd3ad..a29793dfc7 100644 --- a/ui/qt/main_window_preferences_frame.h +++ b/ui/qt/main_window_preferences_frame.h @@ -56,7 +56,6 @@ private: pref_t *pref_auto_scroll_percentage_; pref_t *pref_toolbar_main_style_; pref_t *pref_toolbar_filter_style_; - pref_t *pref_qt_language_; void updateWidgets(); private slots: diff --git a/ui/qt/main_window_preferences_frame.ui b/ui/qt/main_window_preferences_frame.ui index eeb1517425..043d9924c6 100644 --- a/ui/qt/main_window_preferences_frame.ui +++ b/ui/qt/main_window_preferences_frame.ui @@ -312,51 +312,9 @@ - Auto-Detect + Auto - - - English - - - - - Français - - - - - Deutsch - - - - - Chinese - - - - - Polski - - - - - Japanese - - - - - Italiano - - - - - - - - You need to restart Wireshark for this change to take effect - @@ -389,7 +347,9 @@ - + + + diff --git a/ui/qt/packet_list.cpp b/ui/qt/packet_list.cpp index ea7409aba9..685db9fd04 100644 --- a/ui/qt/packet_list.cpp +++ b/ui/qt/packet_list.cpp @@ -235,6 +235,7 @@ PacketList::PacketList(QWidget *parent) : ctx_column_(-1) { QMenu *submenu, *subsubmenu; + QAction *action; setItemsExpandable(false); setRootIsDecorated(false); @@ -256,26 +257,36 @@ PacketList::PacketList(QWidget *parent) : ctx_menu_.addAction(window()->findChild("actionEditPacketComment")); ctx_menu_.addSeparator(); - submenu = new QMenu(tr("Follow...")); - ctx_menu_.addMenu(submenu); + + action = window()->findChild("actionFollow"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionAnalyzeFollowTCPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowUDPStream")); submenu->addAction(window()->findChild("actionAnalyzeFollowSSLStream")); + filter_actions_ << submenu->actions(); - // " \n" - // " \n" - // " \n" - submenu = new QMenu(tr("SCTP")); + + action = window()->findChild("actionSCTP"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); ctx_menu_.addMenu(submenu); submenu->addAction(window()->findChild("actionSCTPAnalyseThisAssociation")); submenu->addAction(window()->findChild("actionSCTPShowAllAssociations")); submenu->addAction(window()->findChild("actionSCTPFilterThisAssociation")); filter_actions_ << submenu->actions(); + ctx_menu_.addSeparator(); + // " \n" - ctx_menu_.addSeparator(); - submenu = new QMenu(tr("Apply as Filter")); - ctx_menu_.addMenu(submenu); +// ctx_menu_.addSeparator(); + + action = window()->findChild("actionApply_as_Filter"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionAnalyzeAAFSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFAndSelected")); @@ -283,8 +294,11 @@ PacketList::PacketList(QWidget *parent) : submenu->addAction(window()->findChild("actionAnalyzeAAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFOrNotSelected")); filter_actions_ << submenu->actions(); - submenu = new QMenu(tr("Prepare a Filter")); - ctx_menu_.addMenu(submenu); + + action = window()->findChild("actionPrepare_a_Filter"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionAnalyzePAFSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFAndSelected")); @@ -292,7 +306,11 @@ PacketList::PacketList(QWidget *parent) : submenu->addAction(window()->findChild("actionAnalyzePAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFOrNotSelected")); filter_actions_ << submenu->actions(); - submenu = new QMenu(tr("Colorize with Filter")); + +// action = window()->findChild("actionColorize_with_Filter"); +// submenu = new QMenu(); +// action->setMenu(submenu); +// ctx_menu_.addAction(action); // " \n" // " \n" // " \n" @@ -367,20 +385,25 @@ PacketList::PacketList(QWidget *parent) : // " \n" // " \n" ctx_menu_.addSeparator(); -// " \n" - submenu = new QMenu(tr("Copy")); - ctx_menu_.addMenu(submenu); + + action = window()->findChild("actionCopy"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); // " \n" // " \n" submenu->addAction(window()->findChild("actionEditCopyAsFilter")); filter_actions_ << window()->findChild("actionEditCopyAsFilter"); submenu->addSeparator(); - subsubmenu = new QMenu(tr("Bytes")); - submenu->addMenu(subsubmenu); + + action = window()->findChild("actionBytes"); + subsubmenu = new QMenu(); + action->setMenu(subsubmenu); + submenu->addAction(action); // " \n" // " \n" // " \n" - ctx_menu_.addSeparator(); +// ctx_menu_.addSeparator(); // " \n" // " \n" ctx_menu_.addSeparator(); diff --git a/ui/qt/preferences_dialog.cpp b/ui/qt/preferences_dialog.cpp index 663b33b96c..4702c664f1 100644 --- a/ui/qt/preferences_dialog.cpp +++ b/ui/qt/preferences_dialog.cpp @@ -31,8 +31,9 @@ #endif /* HAVE_LIBPCAP */ #include - +#include #include +#include #include "module_preferences_scroll_area.h" #include "syntax_line_edit.h" @@ -797,6 +798,9 @@ void PreferencesDialog::on_buttonBox_accepted() prefs_main_write(); + write_language_prefs(); + wsApp->loadLanguage(QString(language)); + #ifdef HAVE_AIRPCAP /* * Load the Wireshark decryption keys (just set) and save diff --git a/ui/qt/proto_tree.cpp b/ui/qt/proto_tree.cpp index 36e597dc5a..ecef518e3b 100644 --- a/ui/qt/proto_tree.cpp +++ b/ui/qt/proto_tree.cpp @@ -159,6 +159,7 @@ ProtoTree::ProtoTree(QWidget *parent) : decode_as_(NULL) { QMenu *submenu, *subsubmenu; + QAction *action; setAccessibleName(tr("Packet details")); setUniformRowHeights(true); @@ -170,25 +171,32 @@ ProtoTree::ProtoTree(QWidget *parent) : ctx_menu_.addAction(window()->findChild("actionViewCollapseAll")); ctx_menu_.addSeparator(); // " \n" - ctx_menu_.addSeparator(); - submenu = new QMenu(tr("Apply as Filter")); - ctx_menu_.addMenu(submenu); + action = window()->findChild("actionApply_as_Filter"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionAnalyzeAAFSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFAndSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFOrSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzeAAFOrNotSelected")); - submenu = new QMenu(tr("Prepare a Filter")); - ctx_menu_.addMenu(submenu); + + action = window()->findChild("actionPrepare_a_Filter"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionAnalyzePAFSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFAndSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFOrSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFAndNotSelected")); submenu->addAction(window()->findChild("actionAnalyzePAFOrNotSelected")); - submenu = new QMenu(tr("Colorize with Filter")); - ctx_menu_.addMenu(submenu); + +// action = window()->findChild("actionColorize_with_Filter"); +// submenu = new QMenu(); +// action->setMenu(submenu); +// ctx_menu_.addAction(action); // " \n" // " \n" // " \n" @@ -205,15 +213,21 @@ ProtoTree::ProtoTree(QWidget *parent) : // " \n" // " \n" ctx_menu_.addSeparator(); - submenu = new QMenu(tr("Copy")); - ctx_menu_.addMenu(submenu); + + action = window()->findChild("actionCopy"); + submenu = new QMenu(); + action->setMenu(submenu); + ctx_menu_.addAction(action); submenu->addAction(window()->findChild("actionEditCopyDescription")); submenu->addAction(window()->findChild("actionEditCopyFieldName")); submenu->addAction(window()->findChild("actionEditCopyValue")); submenu->addSeparator(); submenu->addAction(window()->findChild("actionEditCopyAsFilter")); - subsubmenu = new QMenu(tr("Bytes")); - submenu->addMenu(subsubmenu); + + action = window()->findChild("actionBytes"); + subsubmenu = new QMenu(); + action->setMenu(subsubmenu); + submenu->addAction(action); subsubmenu->addSeparator(); // " \n" // " \n" @@ -225,12 +239,12 @@ ProtoTree::ProtoTree(QWidget *parent) : // " \n" // " \n" // " \n" - ctx_menu_.addSeparator(); +// ctx_menu_.addSeparator(); // " \n" // " \n" // " \n" // " \n" - ctx_menu_.addSeparator(); +// ctx_menu_.addSeparator(); decode_as_ = window()->findChild("actionAnalyzeDecodeAs"); ctx_menu_.addAction(decode_as_); // " \n" diff --git a/ui/qt/proto_tree.h b/ui/qt/proto_tree.h index 6f7bd0d678..54d081bd4d 100644 --- a/ui/qt/proto_tree.h +++ b/ui/qt/proto_tree.h @@ -44,9 +44,9 @@ protected: void contextMenuEvent(QContextMenuEvent *event); private: - QMenu ctx_menu_; - QAction *decode_as_; - QFont mono_font_; + QMenu ctx_menu_; + QAction *decode_as_; + QFont mono_font_; signals: void protoItemSelected(QString &); diff --git a/ui/qt/search_frame.cpp b/ui/qt/search_frame.cpp index ca37afb54d..68f22d76f9 100644 --- a/ui/qt/search_frame.cpp +++ b/ui/qt/search_frame.cpp @@ -340,6 +340,22 @@ void SearchFrame::on_cancelButton_clicked() animatedHide(); } +void SearchFrame::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + sf_ui_->retranslateUi(this); + break; + default: + break; + } + } + AccordionFrame::changeEvent(event); +} + /* * Editor modelines * diff --git a/ui/qt/search_frame.h b/ui/qt/search_frame.h index a45d055076..f8c91a31ba 100644 --- a/ui/qt/search_frame.h +++ b/ui/qt/search_frame.h @@ -61,12 +61,10 @@ private: private slots: void on_searchTypeComboBox_currentIndexChanged(int index); - void on_searchLineEdit_textChanged(const QString &search_string); - void on_findButton_clicked(); - void on_cancelButton_clicked(); + void changeEvent(QEvent* event); }; #endif // SEARCH_FRAME_H diff --git a/ui/qt/summary_dialog.cpp b/ui/qt/summary_dialog.cpp index bc6f73ff0e..36b82f4707 100644 --- a/ui/qt/summary_dialog.cpp +++ b/ui/qt/summary_dialog.cpp @@ -744,6 +744,22 @@ SummaryDialog::~SummaryDialog() delete ui; } +void SummaryDialog::changeEvent(QEvent* event) +{ + if (0 != event) + { + switch (event->type()) + { + case QEvent::LanguageChange: + ui->retranslateUi(this); + break; + default: + break; + } + } + QDialog::changeEvent(event); +} + /* * Editor modelines diff --git a/ui/qt/summary_dialog.h b/ui/qt/summary_dialog.h index 6c5f667c6d..5d13990d1a 100644 --- a/ui/qt/summary_dialog.h +++ b/ui/qt/summary_dialog.h @@ -73,6 +73,7 @@ protected slots: void HelpButton(); void CopyComment(); void on_tabWidget_currentChanged(int index); + void changeEvent(QEvent* event); private: diff --git a/ui/qt/wireshark_application.cpp b/ui/qt/wireshark_application.cpp index 1362ca7f6b..a4f3595438 100644 --- a/ui/qt/wireshark_application.cpp +++ b/ui/qt/wireshark_application.cpp @@ -61,6 +61,8 @@ #include #include #include +#include +#include #include #include #include @@ -570,6 +572,7 @@ WiresharkApplication::WiresharkApplication(int &argc, char **argv) : Q_INIT_RESOURCE(status); Q_INIT_RESOURCE(toolbar); Q_INIT_RESOURCE(wsicon); + Q_INIT_RESOURCE(languages); #ifdef Q_OS_WIN /* RichEd20.DLL is needed for native file dialog filter entries. */ @@ -866,6 +869,43 @@ void WiresharkApplication::addRecentItem(const QString &filename, qint64 size, b itemStatusFinished(filename, size, accessible); } +static void switchTranslator(QTranslator& myTranslator, const QString& filename, + const QString& searchPath) +{ + wsApp->removeTranslator(&myTranslator); + + if (myTranslator.load(filename, searchPath)) + wsApp->installTranslator(&myTranslator); +} + +void WiresharkApplication::loadLanguage(const QString& newLanguage) +{ + QLocale locale; + QString localeLanguage; + + if (newLanguage.isEmpty() || newLanguage == "auto") { + localeLanguage = QLocale::system().name(); + } else { + localeLanguage = newLanguage; + } + + locale = QLocale(localeLanguage); + QLocale::setDefault(locale); + switchTranslator(wsApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), QString(":/i18n/")); + if (QFile::exists(QString("%1/%2/wireshark_%3.qm") + .arg(get_datafile_dir()).arg("languages").arg(localeLanguage))) + switchTranslator(wsApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), QString(get_datafile_dir()) + QString("/languages")); + if (QFile::exists(QString("%1/wireshark_%3.qm") + .arg(gchar_free_to_qstring(get_persconffile_path("languages", FALSE))).arg(localeLanguage))) + switchTranslator(wsApp->translator, + QString("wireshark_%1.qm").arg(localeLanguage), gchar_free_to_qstring(get_persconffile_path("languages", FALSE))); + switchTranslator(wsApp->translatorQt, + QString("qt_%1.qm").arg(localeLanguage), + QLibraryInfo::location(QLibraryInfo::TranslationsPath)); +} + /* * Editor modelines * diff --git a/ui/qt/wireshark_application.h b/ui/qt/wireshark_application.h index a56d6c6c7f..a6764cf137 100644 --- a/ui/qt/wireshark_application.h +++ b/ui/qt/wireshark_application.h @@ -43,6 +43,8 @@ #include #include #include +#include + // Recent items: // - Read from prefs @@ -93,6 +95,10 @@ public: const QIcon &normalIcon() const { return normal_icon_; } const QIcon &captureIcon() const { return capture_icon_; } + QTranslator translator; + QTranslator translatorQt; + void loadLanguage(const QString& language); + private: bool initialized_; QFont mono_font_;