mirror of
https://github.com/pepaskopec-blip/Graduately.git
synced 2026-10-27 15:14:35 +00:00
made vocabulary exercise just a bit more tolerant
This commit is contained in:
parent
a6094bc780
commit
a57c4923fe
6 changed files with 75 additions and 11 deletions
|
|
@ -155,6 +155,39 @@ gboolean answer_accepts(const char *sel_norm, const char *ans) {
|
|||
return ok;
|
||||
}
|
||||
|
||||
/* True when the typed answer matches the beginning of an accepted answer but
|
||||
* is missing one or more trailing words (e.g. "jmenovat" for "jmenovat se").
|
||||
* Used to warn about an incomplete answer before counting it wrong. */
|
||||
gboolean answer_is_incomplete(const char *sel_norm, const char *ans) {
|
||||
gchar **parts = g_strsplit(ans, "|", -1);
|
||||
gboolean incomplete = FALSE;
|
||||
|
||||
for (int i = 0; parts[i] != NULL && !incomplete; i++) {
|
||||
gchar *a = normalize_answer(parts[i]);
|
||||
gchar **sel_w = g_strsplit(sel_norm, " ", -1);
|
||||
gchar **ans_w = g_strsplit(a, " ", -1);
|
||||
int nsel = g_strv_length(sel_w);
|
||||
int nans = g_strv_length(ans_w);
|
||||
int k;
|
||||
|
||||
if (sel_norm[0] && nsel > 0 && nsel < nans) {
|
||||
incomplete = TRUE;
|
||||
for (k = 0; k < nsel; k++) {
|
||||
if (g_strcmp0(sel_w[k], ans_w[k]) != 0) {
|
||||
incomplete = FALSE;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_strfreev(sel_w);
|
||||
g_strfreev(ans_w);
|
||||
g_free(a);
|
||||
}
|
||||
|
||||
g_strfreev(parts);
|
||||
return incomplete;
|
||||
}
|
||||
|
||||
void combo_list_check(GtkButton *button, gpointer data) {
|
||||
ComboListCtx *ctx = data;
|
||||
guint total = ctx->combos->len;
|
||||
|
|
|
|||
|
|
@ -1062,7 +1062,7 @@ const TypedQ trans_p13[] = {
|
|||
{"das Alter", "věk|age", "věk"},
|
||||
{"auch", "také|taky|also|too", "také"},
|
||||
{"aus", "z|ze|from", "z"},
|
||||
{"die Band, -s", "skupina|kapela|band", "(hudební) skupina"},
|
||||
{"die Band, -s", "skupina|hudební skupina|kapela|hudební kapela|band", "(hudební) skupina"},
|
||||
{"chatten (Infinitiv)", "chatovat|chatuje|to chat", "chatovat"},
|
||||
{"das Computerspiel, -e", "počítačová hra|computer game", "počítačová hra"},
|
||||
{"die/das E-Mail, -s", "e-mail|email", "e-mail"},
|
||||
|
|
@ -1079,7 +1079,7 @@ const TypedQ trans_p13[] = {
|
|||
"Je fotbalový fanatik."},
|
||||
{"gehen (Infinitiv)", "jít|jde|chodit|to go", "jít"},
|
||||
{"Ich gehe gern auf Konzerte.",
|
||||
"rád chodím na koncerty|ráda chodím na koncerty|i like going to concerts",
|
||||
"rád chodím na koncerty|ráda chodím na koncerty|chodím rád na koncerty|chodím ráda na koncerty|i like going to concerts",
|
||||
"Rád(a) chodím na koncerty."},
|
||||
{"gern", "rád|ráda|gladly", "rád/a"},
|
||||
{"die Gitarre, -n", "kytara|guitar", "kytara"},
|
||||
|
|
@ -1096,7 +1096,7 @@ const TypedQ trans_p13[] = {
|
|||
{"kein/keine/kein", "žádný|žádná|žádné|no|none", "žádný/žádná/žádné"},
|
||||
{"das Klavier, -e", "klavír|piano", "klavír"},
|
||||
{"kommen (Infinitiv, aus)",
|
||||
"přicházet|přijít|přichází|pochází|to come|to come from",
|
||||
"přicházet|přijít|přichází|pocházet|pochází|to come|to come from",
|
||||
"přicházet, pocházet (z)"},
|
||||
{"Er kommt aus Wien.",
|
||||
"pochází z vídně|he comes from vienna|he is from vienna",
|
||||
|
|
@ -1119,30 +1119,30 @@ const TypedQ trans_p13[] = {
|
|||
};
|
||||
|
||||
const TypedQ trans_p14[] = {
|
||||
{"falsch", "nesprávně|chybně|špatně|wrong|incorrect", "nesprávně, chybně"},
|
||||
{"falsch", "nesprávně|chybně|špatně|nepravda|wrong|incorrect", "nesprávně, chybně"},
|
||||
{"die Flöte, -n", "flétna|flute", "flétna"},
|
||||
{"haben (Infinitiv)", "mít|má|to have", "mít"},
|
||||
{"das Instrument, -e", "nástroj|instrument", "(hudební) nástroj"},
|
||||
{"das Instrument, -e", "nástroj|hudební nástroj|instrument", "(hudební) nástroj"},
|
||||
{"jetzt", "teď|nyní|now", "teď"},
|
||||
{"machen (Infinitiv)", "dělat|dělá|to do|to make", "dělat"},
|
||||
{"richtig", "správně|správný|correct|right", "správně"},
|
||||
{"der Sport", "sport", "sport"},
|
||||
{"Sport treiben (Infinitiv)",
|
||||
"sportovat|sportuje|provozovat sport|to do sport", "sportovat"},
|
||||
"sportovat|sportuje|provozovat sport|dělat sport|to do sport", "sportovat"},
|
||||
{"das Squash", "squash", "squash"},
|
||||
{"das/der Yoga", "jóga|yoga", "jóga"},
|
||||
};
|
||||
|
||||
const TypedQ trans_p15[] = {
|
||||
{"bald", "brzy|soon", "brzy"},
|
||||
{"Bis bald!", "brzy na shledanou|brzy se uvidíme|see you soon",
|
||||
{"bald", "brzy|brzo|soon", "brzy"},
|
||||
{"Bis bald!", "brzy na shledanou|brzo na shledanou|brzy se uvidíme|brzo se uvidíme|see you soon",
|
||||
"Brzy na shledanou!"},
|
||||
{"bis", "do|až|until", "do"},
|
||||
{"Bis zum nächsten Mal!", "do příštího setkání|do příště|until next time",
|
||||
"Do příštího setkání!"},
|
||||
{"die Frau, -en", "paní|žena|woman|mrs", "paní"},
|
||||
{"die Frau, -en", "paní|žena|manželka|woman|mrs|wife", "paní"},
|
||||
{"der Herr, -en", "pan|muž|man|mr", "pan"},
|
||||
{"Mach's gut!", "měj se hezky|měj se|take care", "Měj se hezky!"},
|
||||
{"Mach's gut!", "měj se hezky|měj se|mějte se hezky|mějte se|take care", "Měj se hezky!"},
|
||||
{"das Museum, Museen", "muzeum|museum", "muzeum"},
|
||||
{"Sie gehen ins Museum.",
|
||||
"jdou do muzea|they are going to the museum|they go to the museum",
|
||||
|
|
@ -1186,6 +1186,7 @@ typedef struct {
|
|||
int done;
|
||||
int *order; /* queue of word indices, order[0] = current */
|
||||
int qn; /* words still in the queue */
|
||||
gboolean warned; /* current word already got an "incomplete" warning */
|
||||
} TransCtx;
|
||||
|
||||
/* Canonical translation of a word, in the current UI language. */
|
||||
|
|
@ -1203,11 +1204,13 @@ static void trans_update_progress(TransCtx *ctx) {
|
|||
}
|
||||
|
||||
static void trans_show(TransCtx *ctx) {
|
||||
ctx->warned = FALSE;
|
||||
|
||||
if (ctx->qn <= 0) {
|
||||
gtk_label_set_text(GTK_LABEL(ctx->header), "");
|
||||
gtk_label_set_text(GTK_LABEL(ctx->prompt), tr("trans_done"));
|
||||
gtk_widget_set_visible(ctx->entry, FALSE);
|
||||
gtk_widget_set_sensitive(ctx->check, FALSE);
|
||||
gtk_widget_set_visible(ctx->check, FALSE);
|
||||
set_feedback(ctx->feedback, TRUE, tr("feedback_ok"));
|
||||
trans_update_progress(ctx);
|
||||
mark_done(ctx->unit, ctx->ex_num);
|
||||
|
|
@ -1255,6 +1258,16 @@ static void translate_check(GtkButton *button, gpointer data) {
|
|||
txt = gtk_editable_get_text(GTK_EDITABLE(ctx->entry));
|
||||
norm = normalize_answer(txt ? txt : "");
|
||||
good = txt && txt[0] && answer_accepts(norm, q->answers);
|
||||
|
||||
/* Nearly there (e.g. "jmenovat" for "jmenovat se"): warn in yellow and let
|
||||
* the student finish the answer. A second Check on the same word counts. */
|
||||
if (!good && !ctx->warned && txt && txt[0] &&
|
||||
answer_is_incomplete(norm, q->answers)) {
|
||||
g_free(norm);
|
||||
ctx->warned = TRUE;
|
||||
set_feedback_warn(ctx->feedback, tr("trans_incomplete"));
|
||||
return;
|
||||
}
|
||||
g_free(norm);
|
||||
|
||||
/* Pop the current word off the front of the queue. */
|
||||
|
|
|
|||
1
i18n.c
1
i18n.c
|
|
@ -80,6 +80,7 @@ const TrEntry tr_ui[] = {
|
|||
"Přeložte slovo. Špatná odpověď se vrátí na konec.",
|
||||
"Translate the word. Wrong answers come back at the end."},
|
||||
{"trans_wrong", "Špatně. Správně: %s", "Wrong. Correct: %s"},
|
||||
{"trans_incomplete", "Nepřesné – něco chybí.", "Almost – something is missing."},
|
||||
{"trans_progress", "Hotovo: %d / %d", "Done: %d / %d"},
|
||||
{"trans_done", "Hotovo! Zvládl(a) jsi všechna slova.",
|
||||
"Done! You mastered all the words."},
|
||||
|
|
|
|||
|
|
@ -743,6 +743,7 @@ void shuffle_indices(int *arr, int n);
|
|||
void flow_clear(GtkFlowBox *fb);
|
||||
void answer_mark(GtkWidget *widget, gboolean ok);
|
||||
void set_feedback(GtkWidget *label, gboolean ok, const char *text);
|
||||
void set_feedback_warn(GtkWidget *label, const char *text);
|
||||
gboolean text_has_german_umlaut(const char *s);
|
||||
void add_umlaut_note(GtkWidget *body);
|
||||
void unit_save_progress(UnitCtx *u);
|
||||
|
|
@ -852,6 +853,7 @@ void combo_list_add(ComboListCtx *ctx, GtkWidget *combo, const char *ans);
|
|||
void combo_list_add_trans(ComboListCtx *ctx, GtkWidget *label);
|
||||
GtkWidget *make_word_combo(const char **words, int n);
|
||||
gboolean answer_accepts(const char *sel_norm, const char *ans);
|
||||
gboolean answer_is_incomplete(const char *sel_norm, const char *ans);
|
||||
void combo_list_check(GtkButton *button, gpointer data);
|
||||
void sent_rebuild(SentBuilder *sb);
|
||||
GdkContentProvider *asm_drag_prepare(GtkDragSource *source,
|
||||
|
|
|
|||
|
|
@ -610,6 +610,11 @@ entry.answer-wrong {
|
|||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.feedback-warn {
|
||||
color: @warning;
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
}
|
||||
.stats-page {
|
||||
padding: 8px 28px 24px 28px;
|
||||
}
|
||||
|
|
|
|||
10
util.c
10
util.c
|
|
@ -92,10 +92,20 @@ void answer_mark(GtkWidget *widget, gboolean ok) {
|
|||
void set_feedback(GtkWidget *label, gboolean ok, const char *text) {
|
||||
gtk_widget_remove_css_class(label, "feedback-ok");
|
||||
gtk_widget_remove_css_class(label, "feedback-err");
|
||||
gtk_widget_remove_css_class(label, "feedback-warn");
|
||||
gtk_widget_add_css_class(label, ok ? "feedback-ok" : "feedback-err");
|
||||
gtk_label_set_text(GTK_LABEL(label), text);
|
||||
}
|
||||
|
||||
/* Yellow "almost there" note: close to a correct answer but incomplete. */
|
||||
void set_feedback_warn(GtkWidget *label, const char *text) {
|
||||
gtk_widget_remove_css_class(label, "feedback-ok");
|
||||
gtk_widget_remove_css_class(label, "feedback-err");
|
||||
gtk_widget_remove_css_class(label, "feedback-warn");
|
||||
gtk_widget_add_css_class(label, "feedback-warn");
|
||||
gtk_label_set_text(GTK_LABEL(label), text);
|
||||
}
|
||||
|
||||
/* True if `s` contains any German special letter (ä, ö, ü or ß). */
|
||||
gboolean text_has_german_umlaut(const char *s) {
|
||||
const char *p = s ? s : "";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue