Skip to content

Commit

Permalink
Implement #130 (Add option
Browse files Browse the repository at this point in the history
for selecting language)
  • Loading branch information
kovzol committed Feb 27, 2020
1 parent 4c6bc2d commit c19ff3f
Show file tree
Hide file tree
Showing 7 changed files with 204 additions and 45 deletions.
61 changes: 51 additions & 10 deletions i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,29 @@ XaoS has transitioned from gettext to Qt for i18n.
The .ts files can be edited using Qt Linguist. See the
[Qt Linguist manual](https://doc.qt.io/qt-5/qtlinguist-index.html) for more information.

Run the following command from the XaoS top level directory to update the
## New or modified texts in XaoS

In case there is a new or updated text in the set of translated texts,
please run the following command from the XaoS top level directory to update the
.ts files with changes from the source code:

```
lupdate -tr-function-alias QT_TRANSLATE_NOOP=TR XaoS.pro
```

The function alias is very important so XaoS can find the TR macros used
The function alias is very important so Qt can find the TR macros used
by XaoS.

To add a new translation, edit i18n.pri and add a file named
$$PWD/XaoS_LL.ts to the TRANSLATIONS list. Replace LL with the two
## Adding a new language

To add a new translation, do the following:

1. Edit i18n.pri and add a file named $$PWD/XaoS_LL.ts to the TRANSLATIONS list.
Replace LL with the two
letter ISO 639-1 code for the new language. Save the pri file and
rerun the lupdate command above to create the .ts file.

The i18n.pri contains commands to automatically generate the binary .qm
2. The i18n.pri contains commands to automatically generate the binary .qm
files from the .ts files each time XaoS is built, but the .qm files can
also be manually updated by running the following command from the top
level directory:
Expand All @@ -29,16 +36,50 @@ level directory:
lrelease XaoS.pro
```

Finally, add an entry for the new language in the file XaoS.qrc
3. Also, add an entry for the new language in the file XaoS.qrc
in the top level directory. This makes sure that the .qm file will be
used by the executable (actually, the .qm file is linked against
the executable).

After compilation you can try out a translation by setting the shell variable LANGUAGE
to the two letter language code. For example, on a Linux system
4. There are some further changes required in src/include/ui_helper.h
by defining the new language with a line

```
#define UIH_LANG_LL XX
```

where LL is the two letter ISO 639-1 code for the new language and XX
is the next number in the list.

5. In src/ui/main.cpp increase the number of internationalized menu items at line

```
#define MAX_MENUITEMS_I18N 30
```

If you compile XaoS in debug mode, you can find the required minimal number here
by checking the message "Filled ... ui menu items out of ...".

6. Also, add the LL code in src/ui/main.cpp to the list languages1 and an appropriate
[ICU](http://userguide.icu-project.org/locale) code to languages2.

7. Finally, extend the file src/ui/main.cpp with a menu entry like

```
MENUINTRB_I("setlang", NULL, "Swedish", "sv", UI, uih_setlanguage, UIH_LANG_SV, ui_languageselected);
```

Here the English name "Swedish" and the two letter code "sv" must be changed, and also
UIH_LANG_SV to UIH_LANG_LL, accordingly (as defined in step 4).

## Testing your changes

After compilation you can try a translation out by setting the shell variable LANG
to the two letter language code before startup. For example, on a Linux system

```
qmake && make && LANGUAGE=sv bin/xaos
qmake && make && LANG=sv bin/xaos
```

will start the application in Swedish.
will start the application in Swedish. Alternatively, the newest versions of XaoS
give you the opportunity to do the same in the View menu as well.
4 changes: 4 additions & 0 deletions src/include/i18n.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
#define I18N_H

const char *qt_gettext(const char *context, const char *text);
void setLanguage(const char *lang);
const char *getLanguage();
const char *lang1(int i);
const char *lang2(int i);

#define TR(context, text) qt_gettext(context, text)
#endif
12 changes: 12 additions & 0 deletions src/include/ui_helper.h
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,18 @@ typedef struct uih_context uih_context;
#define UIH_TEXTCENTER 1
#define UIH_TEXTRIGHT 2

#define UIH_LANG_EN 0
#define UIH_LANG_CS 1
#define UIH_LANG_DE 2
#define UIH_LANG_ES 3
#define UIH_LANG_FR 4
#define UIH_LANG_HU 5
#define UIH_LANG_IT 6
#define UIH_LANG_PT 7
#define UIH_LANG_RO 8
#define UIH_LANG_RU 9
#define UIH_LANG_SV 10

#define RANDOM_PALETTE_SIZE 1
#define FULLSCREEN 2
#define UPDATE_AFTER_PALETTE 4
Expand Down
1 change: 0 additions & 1 deletion src/include/xmenu.h
Original file line number Diff line number Diff line change
Expand Up @@ -468,5 +468,4 @@ const char *menu_fillparam(struct uih_context *uih, tokenfunc f,
int menu_processargs(int n, int argc, char **argv);
void menu_forall(struct uih_context *c,
void (*callback)(struct uih_context *c, const menuitem *item));

#endif
12 changes: 12 additions & 0 deletions src/ui-hlp/menu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,17 @@ void uih_registermenudialogs_i18n(void)
NULL_I();
#endif

if (no_menudialogs_i18n > MAX_MENUDIALOGS_I18N) {
fprintf(stderr, "MAX_MENUDIALOGS_I18N is set to an insufficiently low number, please increase it to %d\n", no_menudialogs_i18n);
fflush(stderr);
exit(1);
}
#ifdef DEBUG
printf("Filled %d widgets out of %d.\n", no_menudialogs_i18n,
MAX_MENUDIALOGS_I18N);
fflush(stdout);
#endif

}

#undef Register
Expand Down Expand Up @@ -1192,6 +1199,11 @@ void uih_registermenus_i18n(void)
TUTOR_I("otherf", TR("Menu", "Sierpinski Gasket, S.Carpet, Koch Snowflake"),
"classic.xaf");
TUTOR_I("new", TR("Menu", "What's new in 3.0?"), "new30.xaf");
if (no_menuitems_i18n > MAX_MENUITEMS_I18N) {
fprintf(stderr, "MAX_MENUITEMS_I18N is set to an insufficiently low number, please increase it to %d\n", no_menuitems_i18n);
fflush(stderr);
exit(1);
}
#ifdef DEBUG
printf("Filled %d menu items out of %d.\n", no_menuitems_i18n,
MAX_MENUITEMS_I18N);
Expand Down
156 changes: 123 additions & 33 deletions src/ui/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -329,6 +329,24 @@ static void ui_font(struct uih_context *uih)
window->chooseFont();
}

void uih_setlanguage(uih_context *c, int l)
{
const char* menu = lang1(l);
uih_updatemenus(c, menu);
setLanguage(lang2(l));

QSettings settings;
settings.setValue("MainWindow/language", lang2(l));
QMessageBox msgBox;
msgBox.setText(TR("Message", "XaoS must restart to change the language."));
msgBox.setInformativeText(TR("Message", "Do you want to quit now?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::No);
int ret = msgBox.exec();
if (ret == QMessageBox::Yes) {
exit(0);
}
}

#ifndef Q_OS_MAC
static void ui_fullscreensw(struct uih_context *uih)
{
Expand All @@ -351,14 +369,72 @@ static int ui_fullscreenselected(struct uih_context *uih)
}
#endif

#define MAX_MENUITEMS_I18N 20
/* WARNING: Increase this number in case there are new menu items added. */
#define MAX_MENUITEMS_I18N 30
/* These variables must be global: */
static menuitem *menuitems;
static menuitem menuitems_i18n[MAX_MENUITEMS_I18N];
int ui_no_menuitems_i18n = 0;

#define UI (MENUFLAG_NOPLAY | MENUFLAG_NOOPTION)

static void ui_unregistermenus(void)
{
menu_delete(menuitems, NITEMS(menuitems));
menu_delete(menuitems_i18n, ui_no_menuitems_i18n);
}

QTranslator qtTranslator;
QTranslator xaosTranslator;
char languageSetting[6] = "";
const char *languages1[] = {
"en", "cs", "de", "es", "fr", "hu", "it", "pt", "ro", "ru", "sv"
// "English", "Czech", "German", "French", "Hungarian", "Italian", "Portuguese", "Romanian", "Russian", "Spanish", "Swedish"
};
const char *languages2[] = {
"en_US", "cs_CZ", "de_DE", "es_ES", "fr_FR", "hu_HU", "it_IT", "pt_PT", "ro_RO", "ru_RU", "sv_SV"
};

const char *lang1(int i) {
return languages1[i];
}
const char *lang2(int i) {
return languages2[i];
}
const char* getLanguage() {
return languageSetting;
}

static int ui_languageselected(uih_context *c, int p)
{
if (c == NULL)
return 0;
return (strcmp(languageSetting, languages2[p]) == 0);
}

void setLanguage(const char *lang) {
qApp->removeTranslator(&qtTranslator);
qApp->removeTranslator(&xaosTranslator);
QString language = QString(lang);
if (language.isNull()) {
language = QLocale::system().name();
}
strcpy(languageSetting, language.toStdString().c_str());
qtTranslator.load("qt_" + language,
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
qApp->installTranslator(&qtTranslator);
xaosTranslator.load("XaoS_" + language, ":/i18n");
qApp->installTranslator(&xaosTranslator);

/* Without this some locales (e.g. the Hungarian) replaces "." to ","
in numerical format and this will cause an automatic truncation
at each parameter at certain places, e.g. drawing a new fractal. */
QLocale::system().setNumberOptions(QLocale::DefaultNumberOptions);
setlocale(LC_NUMERIC, "C");
// printf("Language set to %s\n", languageSetting);
// fflush(stdout);
}

static void ui_registermenus_i18n(void)
{
int no_menuitems_i18n =
Expand All @@ -367,10 +443,31 @@ static void ui_registermenus_i18n(void)
MENUFLAG_INTERRUPT | MENUFLAG_ATSTARTUP, ui_quit, UI);

MENUNOP_I("ui", NULL, TR("Menu", "Message Font..."), "font", UI, ui_font);
MENUSEPARATOR_I("ui");

MENUSEPARATOR_I("uia");
MENUNOP_I("uia", NULL, TR("Menu", "Message Font..."), "font", UI, ui_font);

SUBMENU_I("ui", NULL, TR("Menu", "Set Language"), "setlang");
MENUINTRB_I("setlang", NULL, "Czech", "cs", UI, uih_setlanguage,
UIH_LANG_CS, ui_languageselected);
MENUINTRB_I("setlang", NULL, "English", "en", UI, uih_setlanguage,
UIH_LANG_EN, ui_languageselected);
MENUINTRB_I("setlang", NULL, "French", "fr", UI, uih_setlanguage,
UIH_LANG_FR, ui_languageselected);
MENUINTRB_I("setlang", NULL, "German", "de", UI, uih_setlanguage,
UIH_LANG_DE, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Hungarian", "hu", UI, uih_setlanguage,
UIH_LANG_HU, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Portuguese", "pt", UI, uih_setlanguage,
UIH_LANG_PT, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Romanian", "ro", UI, uih_setlanguage,
UIH_LANG_RO, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Russian", "ru", UI, uih_setlanguage,
UIH_LANG_RU, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Spanish", "es", UI, uih_setlanguage,
UIH_LANG_ES, ui_languageselected);
MENUINTRB_I("setlang", NULL, "Swedish", "sv", UI, uih_setlanguage,
UIH_LANG_SV, ui_languageselected);

MENUSEPARATOR_I("ui");
MENUSEPARATOR_I("uia");
#ifndef Q_OS_MACOS
MENUNOPCB_I("ui", NULL, TR("Menu", "Fullscreen"), "fullscreen", UI,
Expand All @@ -393,12 +490,15 @@ static void ui_registermenus_i18n(void)
no_menuitems_i18n -= ui_no_menuitems_i18n;
menu_add(&(menuitems_i18n[ui_no_menuitems_i18n]), no_menuitems_i18n);
ui_no_menuitems_i18n += no_menuitems_i18n;
}

static void ui_unregistermenus(void)
{
menu_delete(menuitems, NITEMS(menuitems));
menu_delete(menuitems_i18n, ui_no_menuitems_i18n);
if (ui_no_menuitems_i18n > MAX_MENUITEMS_I18N) {
fprintf(stderr, "MAX_MENUITEMS_I18N is set to an insufficiently low number, please increase it to %d\n", ui_no_menuitems_i18n);
fflush(stderr);
exit(1);
}
#ifdef DEBUG
printf("Filled %d ui menu items out of %d.\n", ui_no_menuitems_i18n,
MAX_MENUITEMS_I18N);
#endif
}

int main(int argc, char *argv[])
Expand All @@ -422,19 +522,18 @@ int main(int argc, char *argv[])
signal(SIGFPE, SIG_IGN);
srand(time(NULL));

QTranslator qtTranslator;
qtTranslator.load("qt_" + QLocale::system().name(),
QLibraryInfo::location(QLibraryInfo::TranslationsPath));
app.installTranslator(&qtTranslator);
QTranslator xaosTranslator;
xaosTranslator.load("XaoS_" + QLocale::system().name(), ":/i18n");
app.installTranslator(&xaosTranslator);
QSettings settings;
if (defthreads == 0) {
// defthreads will be 0 if -threads command line option was not given
// so load it from saved settings instead
defthreads = settings.value("MainWindow/threadCount", 1).toInt();
} else {
// -threads command line option was given, so save it to settings
settings.setValue("MainWindow/threadCount", defthreads);
}
xth_init(defthreads);

/* Without this some locales (e.g. the Hungarian) replaces "." to ","
in numerical format and this will cause an automatic truncation
at each parameter at certain places, e.g. drawing a new fractal. */
QLocale::system().setNumberOptions(QLocale::DefaultNumberOptions);
setlocale(LC_NUMERIC, "C");
setLanguage(settings.value("MainWindow/language").toString().toStdString().c_str());

params_register(global_params);
uih_registermenudialogs_i18n(); /* Internationalized dialogs. */
Expand All @@ -450,17 +549,6 @@ int main(int argc, char *argv[])
exit(-1);
}

QSettings settings;
if (defthreads == 0) {
// defthreads will be 0 if -threads command line option was not given
// so load it from saved settings instead
defthreads = settings.value("MainWindow/threadCount", 1).toInt();
} else {
// -threads command line option was given, so save it to settings
settings.setValue("MainWindow/threadCount", defthreads);
}
xth_init(defthreads);

int i = ui_render();
if (i) {
ui_unregistermenus();
Expand All @@ -474,3 +562,5 @@ int main(int argc, char *argv[])

return 0;
}


3 changes: 2 additions & 1 deletion src/ui/mainwindow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,8 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent)
// Try to load a catalog for the current language and if it doesn't exist,
// default to english. Fixes "No catalog loaded" messages on tutorials
// when using a language XaoS doesn't support
if (!uih_loadcatalog(uih, QLocale::system().name().left(2).toUtf8()))
//if (!uih_loadcatalog(uih, QLocale::system().name().left(2).toUtf8()))
if (!uih_loadcatalog(uih, QString(getLanguage()).left(2).toUtf8()))
uih_loadcatalog(uih, "en");

tl_update_time();
Expand Down

0 comments on commit c19ff3f

Please sign in to comment.