package ONO::FW::Apps::Core::Lang;
################################################################################
# COPYRIGHT / LICENSE #
################################################################################
#
# This file is part of the ONO Software Project.
#
# Copyright (C) 2000-2025 Jos KIRPS [ www.kirps.com | jos_AT_kirps_DOT_com ]
# and The Joopita Project [ www.joopita.org | contact_AT_joopita_DOT_com ]
#
# This file, as well as other parts of the ONO Software Project or related
# elements, are FREE SOFTWARE available under the ARTISTIC LICENSE 2.0.
#
# 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.
#
# For the full license, see /ono/osr/license/LICENSE.txt, or write to
# jos_AT_kirps_DOT_com or contact_AT_joopita_DOT_com.
#
################################################################################
# END OF COPYRIGHT / LICENSE, HERE COMES THE CODE ... #
################################################################################
use strict;
use ONO::IO;
use ONO::DB;
use ONO::Lib::Basic;
use ONO::FW::Apps::Core;
use ONO::FW::Apps::Core::Lang;
use ONO::FW::Apps::ToolBox;
use ONO::Ext::Morzino::Apps::Manager::ToolBox;
use ONO::FW::Media::Manager::ToolBox;
use ONO::Lib::Audio::Speech;
use ONO::Lib::Lang::ToolBox;
use ONO::Lib::Web::Domain;
###################################################################
#
###################################################################
#: This module offers functions used by language-related ONO apps.
sub total_pix {
#: Return upper limit for Morzino Picture Dictionary ID access.
return 2000;
}
sub lessons {
#: Generate and return custom lessons array in 4 languages.
return (
"1:Basics :Elementar :Elementar :Elementaire",
"2:Numbers :Zahlen :Zuelen :Nombres",
"3:Colors :Farben :Faarwen :Couleurs",
"13:Verbs :Verben :Verben :Verbes",
"14:Adjectives :Adjektive :Adjektiver :Adjectifs",
"6:Body & Health :Körper & Gesundheit :Kierper & Gesondheet :Corps & Santé",
"20:Sports :Sport :Sport :Sport",
"7:Clothing & Shopping :Kleidung & Shopping :Kleedung & Shopping :Vêtements & Shopping",
"11:Home & Living :Heim & Wohnen :Heem & Wunnen :Maison & Vie",
"5:Family & Friends :Familie & Freunde :Famill & Frënn :Famille & Amis",
"4:Calendar, Time & Weather:Kalender, Zeit & Wetter:Kalenner, Zäit & Wieder:Calendrier & Temps",
"10:Environment & Traveling:Umwelt & Reisen :Ëmwelt & Reesen :Environnement & Voyages",
"9:Food & Eating :Essen & Trinken :Iessen & Drénken :Nourriture",
"8:Animals :Tiere :Déieren :Animaux",
"15:Nature :Natur :Natur :Nature",
"12:School & Education :Schule & Bildung :Schoul & Educatioun :Ecole & Education",
"16:World & Cultures :Welt & Kulturen :Welt & Kulturen :Monde & Cultures",
"17:Art & Entertainment :Kunst & Unterhaltung :Konscht & Ënnerhalung :Art & Divertissement",
"18:Office & Work :Büro & Arbeit :Büro & Aarbecht :Bureau & Travail",
"19:Our world :Unsere Welt :Ons Welt :Notre Monde",
"21:Feelings :Gefühle :Gefiller :Sentiments",
"22:Tools :Werkzeuge :Schaffgeschir :Outils",
"23:Toys & Fun :Spielzeug & Spaß :Spillsaachen & Spaass :Jeux & Amusement",
"24:Things & Objects :Dinge & Objekte :Saachen & Objeten :Objets",
"25:Fiction :Fiktion :Fiktioun :Fiction",
);
my $community = ONO::IO->community();
if ($community =~ /^(morzino|school)$/) {
if (int(rand(1000)) == 500 || !ONO::IO->exists("var/community/$community/apps/cache/lang_lessons_list.txt")) {
my $DATA;
foreach my $line (&lessons()) {
$DATA .= "$line\n";
}
ONO::IO->mkpath("var/community/$community/apps/cache");
ONO::IO->store("var/community/$community/apps/cache/lang_lessons_list.txt",$DATA);
}
}
}
sub lessons_num {
#: Return custom lessons array number.
return 25;
}
sub lessons_lang {
#: Return custom lessons array in 1 language.
my @list;
foreach my $line (&lessons) {
my @lp = split(/:/,$line);
my $use = $lp[1];
if ($_[1] eq "de") {
$use = $lp[2];
}
if ($_[1] eq "lu") {
$use = $lp[3];
}
if ($_[1] eq "fr") {
$use = $lp[4];
}
$list[$lp[0]] = $use;
}
return @list;
}
sub lang_tenses {
#: Return verbs tenses, see ONO_Lib_Lang_ToolBox.
my ($codes_ref,$tenses_ref,$pres_ref,$past_ref,$fut_ref) = ONO::Lib::Lang::ToolBox->lang_tenses($_[1]);
return ($codes_ref,$tenses_ref,$pres_ref,$past_ref,$fut_ref);
}
sub lang_verbs_default {
#: Return default / fallback verbs.
my $verbs = "be:be;have:have";
if ($_[1] eq "de") {
$verbs = "haben:haben;sein:sein";
}
if ($_[1] eq "fr") {
$verbs = "avoir:avoir;etre:être";
}
return $verbs;
}
sub lang_verbs_add {
my (
$self,
$db,
$community,
$option,
$lang,
$BLK_ref,
$vars_ref,
) = @_;
#: Add a verb in an ONO app.
my %vars = %$vars_ref;
my %BLK = %$BLK_ref;
$option =~ s~english~en~;
$option =~ s~(german|deutsch)~de~;
$option =~ s~(french|francais)~fr~;
my @txt = (
"Add one verb",
"Add a group of verbs",
"Teachers may create their own custom verb lists in their Virtual Classrooms.",
"You need to be logged in to use this feature.",
"Manage your own verb lists:",
"You are not listed as a teacher in any classrooms.",
);
if ($lang eq "de") {
@txt = (
"Ein Verb hinzufügen",
"Gruppe hinzufügen",
"Lehrer können ihre eigenen benutzerdefinierten Verbenlisten in ihren Virtuellen Klassen erstellen.",
"Du musst angemeldet sein, um diese Funktion nutzen zu können.",
"Meine Verbenlisten verwalten:",
"Du bist in keinem Klassenzimmer als Lehrer aufgeführt.",
);
}
if ($lang eq "lu") {
@txt = (
"Ee Verb bäimaachen",
"Grupp bäimaachen",
"Enseignante kënnen hir eege personnaliséiert Verbelëschten an hire Virtuelle Klassen erstellen.",
"Du muss ageloggt sinn fir dës Funktioun ze benotzen.",
"Meng Verbelëschte geréieren:",
"Du bass net als Enseignant an engem Klassesäll opgelëscht.",
);
}
if ($lang eq "fr") {
@txt = (
"Ajouter un verb",
"Groupe de verbes",
"Les enseignants peuvent créer leurs propres listes de verbes personnalisées dans leurs salles de classe virtuelles.",
"Vous devez être connecté pour utiliser cette fonctionnalité.",
"Gérer mes listes de verbes:",
"Vous n'êtes enrégistré comme enseignant dans aucune salle de classe.",
);
}
my ($ADD,$SEL,$GROUPS,$CUSTOM,@groups,@custom);
for (my $c = 10; $c < 101; $c = $c + 10) {
@custom = (@custom,"custom_top_$c:Top $c");
}
if ($option eq "en") {
}
if ($option eq "de") {
}
if ($option eq "fr") {
@groups = ('group_er:-er','group_re:-re','group_ir:-ir','group_oir:-oir','-','group_prono:pronominaux','-','group_er_basic:-er (basic)','group_ir_basic:-ir (basic)');
}
my @vlist = ONO::IO->list("ono-data/school/verbs_$option/structure/list.txt");
if ($option eq "fr") {
@vlist = (@vlist,"----",ONO::IO->list("ono-data/school/verbs_$option/structure/list_prono.txt"));
}
foreach my $line (@vlist) {
$line =~ s~(\n|\r|\t)~~gi;
if ($option eq "en") {
if ($line =~ /[a-z]/) {
$SEL .= qq~<option value="$line:$line">to $line</option>~;
}
} else {
if ($line =~ /-/) {
if ($line eq "----") {
$SEL .= qq~<option value="">----</option>~;
} else {
my @lp = split(/-/,$line);
$SEL .= qq~<option value="$lp[1]:$lp[0]">$lp[0]</option>~;
}
}
}
}
foreach my $opt (@groups) {
if ($opt eq "-") {
$GROUPS .= qq~</div><div class="inline w100">~;
} else {
if ($opt =~ /^(.*?):(.*?)$/) {
$GROUPS .= qq~<input type="submit" name="output_add_group_$1" value="$2" class="button_green button_small mb10">~;
}
}
}
# custom: add teacher's virclass lists
my ($MANAGE_LISTS,$CUSTOM) = ONO::FW::Apps::ToolBox->get_custom_lists($db,$community,$vars{'username'},"verbs",$option);
# custom: add predefined lists
foreach my $opt (@custom) {
if ($opt eq "-") {
$CUSTOM .= qq~<option value="">----</option>~;
} else {
if ($opt =~ /^(.*?):(.*?)$/) {
$CUSTOM .= qq~<option value="$1:$2">$2</option>~;
}
}
}
$ADD = qq~<div class="inline w100 mb10">
<div class="mb10">$txt[0]:</div>
<table class="default_table">
<tr>
<td><div class="select"><select name="output_add_verb"><option value="">$BLK{'select'}...</option><option value="">----</option>$SEL</select></div></td>
<td>$BLK{'submit_add'}</td>
</tr>
</table>
</div>
~;
if ($GROUPS) {
$ADD .= qq~<div class="mb10 bb"></div>
<div class="mb10">$txt[1]:</div>
<div class="inline w90 ml20">
<div class="inline w100">$GROUPS</div>
</div>
~;
}
$CUSTOM = qq~<table class="default_table">
<tr>
<td>
<div class="select"><select name="output_add_custom">
<option value="">$BLK{'select'}...</option>
<option value="">----</option>
<option value="custom_ALL:$BLK{'all'} ($BLK{'random'})">$BLK{'all'} ($BLK{'random'})</option>
<option value="">----</option>
$CUSTOM
</select></div>
</td>
<td>$BLK{'submit_add_2'}</td>
</tr>
</table>
~;
my $MANAGE = qq~<div class="lightred mb5">$txt[3]</div>
<div class="inline w100"><a href="javascript:void(0);" onclick="onojs_hide('apps_manage_lists');" class="button_yellow">$BLK{'close'}</a></div>
~;
if ($vars{'username'}) {
$MANAGE = qq~<div>$txt[4]</div>
<div class="block" style="max-height:200px;overflow-y:scroll">
<div class="inline">$MANAGE_LISTS</div>
</div>
~;
if (!$MANAGE_LISTS) {
$MANAGE = qq~<div class="lightred mb5">$txt[5]</div>
<div class="inline w100"><a href="javascript:void(0);" onclick="onojs_hide('apps_manage_lists');" class="button_yellow">$BLK{'close'}</a></div>
~;
}
}
$ADD .= qq~<div id="apps_manage_lists" class="box_yellow w100 abs hide" style="top:30px;left:30px">
<a href="javascript:void(0);" onclick="onojs_hide('apps_manage_lists');"><img class="close" alt="close button"></a>
<h4 class="yellow">$BLK{'Manage_lists'}</h4>
<div class="box_paper">
<div class="mb5">$txt[2]</div>
$MANAGE
</div>
</div>
<div class="mb10 bb"></div>
<div class="inline w100 mb10">
<div class="fr"><a href="javascript:void(0);" onclick="onojs_block('apps_manage_lists');" class="button_yellow button_small">$BLK{'manage_lists'}</a></div>
<div class="mb10">$BLK{'Add_a_list'}:</div>
$CUSTOM
</div>
~;
return $ADD;
}
sub lang_verbs_persons {
#: Return verbs persons, see ONO_Lib_Lang_ToolBox.
return ONO::Lib::Lang::ToolBox->lang_verbs_persons($_[1]);
}
sub lang_verbs_persons_list {
#: Return verbs persons as an array, see ONO_Lib_Lang_ToolBox.
return ONO::Lib::Lang::ToolBox->lang_verbs_persons_list($_[1]);
}
sub lang_load_wordlist_pass {
my (
$self,
$level,
$word1,
$word2,
$lang1,
$lang2,
$audio,
) = @_;
#: Load a language word list.
my $pass = 1;
my $max_length = 15;
if ($level == 1) {
$max_length = 12;
}
if ($level < 3) {
if (length $word1 > $max_length || $word1 =~ /(ß|ê)/) {
$pass = 0;
}
if (length $word2 > $max_length || $word2 =~ /(ß|ê)/) {
$pass = 0;
}
}
if ($audio) {
# may look weird, but we'll need to check lang1 with word2:
if ($lang1 && $lang1 ne "img" && $word2) {
if (!ONO::IO->exists(ONO::Lib::Audio::Speech->speech_file($lang1,$word2))) {
$pass = 0;
}
}
}
return $pass;
}
sub lang_load_wordlist {
my (
$self,
$db,
$community,
$vars_ref,
) = @_;
my %vars = %$vars_ref;
#: Load a language word list.
#:
#: wordlist_require_images -> 0, or 1
#: wordlist_require_audio -> 0, or 1
#:
#: note that $max_words is approximative only in some/most cases...
my $max_words = $vars{'wordlist_max_words'};
if (!$max_words) {
$max_words = 999;
}
# worksheet maker may need to set default variables
if ($vars{'output_worksheet_maker'}) {
$vars{'app_input_use_group_1'} = "Basic";
$vars{'app_input_use_group_2'} = "Numbers";
$vars{'app_input_use_group_3'} = "Colors";
$vars{'app_info_use_group_1'} = 1;
$vars{'app_info_use_group_2'} = 1;
$vars{'app_info_use_group_3'} = 1;
$vars{'app_input_difficulty_status'} = 3;
$vars{'app_input_langs_lang_1'} = "fr";
$vars{'app_input_langs_lang_2'} = "de";
$vars{'app_input_multiple_choice_displayed'} = 1;
$vars_ref = \%vars;
}
# start...
my (
$WEB,$DEBUG,@WORDLIST,@TRANS,@IMGS,
$USE_GROUPS,$USE_GROUPS_ALT,$USE_GROUPS_LU,@USE_GROUPS_CUSTOM,
$CACHE_WORDS,$CACHE_TRANS,$CACHE_IMGS,
$use_language1,$use_language2,
$use_images,$use_images_reverse,$use_images_forced,$use_custom_groups,$use_lu,
$get_words,$words_found,$multi_frame_custom_width,$hide_flags,$single_lang_mode,
);
$DEBUG .= "SchoolAppsCoreLang/LangLoadWordlist: ";
# in exercise mode we need to translate "app_input" to "app_info", otherwise DB entries won't load
if ($vars{'exercise'} && $vars{'exercise_id'}) {
foreach my $key (keys %vars) {
if ($key =~ /^app_input_use_group_(.*)$/) {
my $grp = $1;
if (!$vars{"app_info_use_group_$grp"}) {
$vars{"app_info_use_group_$grp"} = $vars{$key};
}
}
}
}
foreach my $key (keys %vars) {
if ($key =~ /^output_add_group_/) {
$vars{'output_add'}++;
}
if ($key =~ /^app_info_use_group_(.*?)$/) {
my $alt = $1;
if ($alt !~ /^custom_/) {
$USE_GROUPS .= "'$alt',";
$USE_GROUPS_ALT .= " OR cat_alt LIKE '%:$alt:%'";
$get_words++;
}
}
if ($key =~ /^app_info_use_group_custom_(.*?)_(.*?)$/) {
my @ins = ($1,$2);
foreach my $i (0,1) {
if (length $ins[$i] < 2) {
$ins[$i] = "0$ins[$i]";
}
}
$USE_GROUPS_LU .= "'1$ins[0]$ins[1]',";
$get_words++;
} else {
if ($key =~ /^app_info_use_group_custom_(.*?)$/) {
@USE_GROUPS_CUSTOM = (@USE_GROUPS_CUSTOM,$1);
$get_words++;
}
}
}
if ($vars{'app_input_langs_lang_1'} || $vars{'app_input_langs_lang_2'}) {
($use_language1,$use_language2) = ONO::FW::Apps::Core->lang_check($vars{'app_input_langs_lang_1'},$vars{'app_input_langs_lang_2'});
($use_language1,$use_language2) = ONO::FW::Apps::Core->lang_check_unique($use_language1,$use_language2,"en");
$vars{'app_input_langs_lang_1'} = $use_language1;
$vars{'app_input_langs_lang_2'} = $use_language2;
}
# this is required to make single lang mode work:
if (!$use_language2) {
$use_language2 = $use_language1;
$vars{'app_input_langs_lang_2'} = $use_language1;
$single_lang_mode = 1;
}
$DEBUG .= "\[$use_language1,$use_language2\] ";
# get words from cache
if ($vars{'output_web_cache_words'}) {
$CACHE_WORDS = $vars{'output_web_cache_words'};
$CACHE_TRANS = $vars{'output_web_cache_trans'};
$CACHE_IMGS = $vars{'output_web_cache_imgs'};
foreach my $word (split(/;;/,$vars{'output_web_cache_words'})) {
my @wp = split(/;/,$word);
@WORDLIST = (@WORDLIST,"$wp[0]^$wp[1]");
$words_found++;
}
foreach my $word (split(/;;/,$vars{'output_web_cache_trans'})) {
@TRANS = (@TRANS,$word);
}
@IMGS = split(/;;/,$CACHE_IMGS);
$get_words = 0;
$DEBUG .= "Loading $words_found words from cache...";
}
# new page (in 20 per page mode) requires rescramble, caches flushing, caches rebuild
if ($vars{'output_web_continue'}) {
foreach my $key (keys %vars) {
if ($key =~ /^output_web_(correct|answer|hints)_/) {
$vars{$key} = "";
}
}
$CACHE_WORDS = "";
$CACHE_TRANS = "";
@WORDLIST = ONO::Lib::Basic->randomize_array(@WORDLIST);
foreach my $word (@WORDLIST) {
$word =~ s~(\n|\r|\t)~~g;
my @wp = split(/\^/,$word);
$CACHE_WORDS .= "$wp[0];$wp[1];;";
$CACHE_TRANS .= "$wp[0];";
}
}
# detect langs + modes
if ($use_language1 eq "img" || $use_language2 eq "img") {
$multi_frame_custom_width = "w20";
$use_images++;
if ($use_language2 eq "img") {
$use_images_reverse++;
}
}
if ($use_language1 eq "lu" || $use_language2 eq "lu") {
$use_lu++;
}
if ($vars{'wordlist_require_images'}) {
$use_images++;
}
# if we need to get words from the DB first (caches empty)
my $exercise_started;
if ($vars{'output_web'} && $vars{'exercise'} && $vars{'exercise_id'}) {
$exercise_started = 1;
}
if ($vars{'wordlist_force_loading'}) {
$DEBUG .= "(forced loading)";
}
if (($get_words && ($vars{'app_input_multiple_choice_displayed'} || $vars{'wordlist_force_loading'})) || $exercise_started) {
my $LIMIT = 256;
if ($max_words < $LIMIT) {
$LIMIT = $max_words;
}
my @ORDER = ("RAND()","RAND()","RAND()");
if ($vars{'output_print_vocs'}) {
@ORDER = ("","","");
}
if (!$vars{'app_input_difficulty_status'}) {
$vars{'app_input_difficulty_status'} = 3;
}
my $DIFF = $vars{'app_input_difficulty_status'};
$DIFF =~ s~[^0-9]~~g;
$DIFF = " AND (difficulty IS NULL OR difficulty < $DIFF)";
if ($vars{'app_input_difficulty_status'} == 4) {
$DIFF = " AND (difficulty > 1 AND difficulty < 4)";
}
if ($vars{'app_input_difficulty_status'} == 5) {
$DIFF = " AND difficulty > 3";
}
$DEBUG .= "Loading from SQL DB [";
if ($vars{'output_print_vocs'}) {
$LIMIT = 512;
if ($use_language1 =~ /(en|de|fr)/) {
@ORDER = ("voc_$use_language1","voc_$use_language1","voc_$use_language1");
}
if ($use_language1 eq "lu") {
@ORDER = ("voc_$use_language1","voc_$use_language1","voc_de");
}
}
$USE_GROUPS =~ s~,$~~;
$USE_GROUPS =~ s~[^0-9\'\,]~~g;
$USE_GROUPS_LU =~ s~,$~~;
$USE_GROUPS_LU =~ s~[^0-9\'\,]~~g;
my $CATS;
if ($USE_GROUPS) {
$CATS = "cat IN ($USE_GROUPS)";
if ($USE_GROUPS_ALT) {
$CATS = "$USE_GROUPS_ALT";
$CATS =~ s~ OR ~~;
}
}
$DEBUG .= $CATS;
my %used;
# DB 1 of 4: we ALWAYS use the image DB first, UNLESS we're using custom groups
if (!$use_custom_groups && $CATS) {
my $use_source = "id_src";
my $use_voc = $use_language2;
if ($use_language1 ne "img") {
$use_source = "voc_$use_language1";
}
if ($use_language2 eq "img") {
$use_source = "id_src";
$use_voc = $use_language1;
}
$DEBUG .= " IMG ";
foreach my $line (ONO::DB->select($db,"school_school_db_vocabulary_pix:id,cat,length,$use_source,voc_$use_voc","$CATS$DIFF",$ORDER[0],"LIMIT $LIMIT")) {
my @word = ONO::DB->readcols($line);
if (!$used{$word[3]} && &lang_load_wordlist_prefer_check("",$word[3],$word[4],$vars{'app_input_langs_prefer'})) {
$used{$word[3]}++;
if (&lang_load_wordlist_pass("",$vars{'app_input_difficulty_status'},$word[3],$word[4],$use_language1,$use_language2,$vars{'wordlist_require_audio'})) {
$words_found++;
@WORDLIST = (@WORDLIST,"$word[3]^$word[4]");
$CACHE_WORDS .= qq~$word[3];$word[4];;~;
$CACHE_IMGS .= qq~$word[3];$word[4];;~;
@IMGS = (@IMGS,"$word[3];$word[4]");
if ($vars{'app_input_multiple_choice'}) {
@TRANS = (@TRANS,$word[4]);
$CACHE_TRANS .= "$word[4];";
}
}
}
}
}
# DB 2 of 4: we ALWAYS use the standard DB, UNLESS we're in image mode OR we're using custom groups
if (!$use_images && !$use_custom_groups && $CATS && $words_found < $max_words) {
$DEBUG .= " VOC ";
foreach my $line (ONO::DB->select($db,"school_school_db_vocabulary:id,cat,length,voc_$use_language1,voc_$use_language2","$CATS$DIFF",$ORDER[1],"LIMIT $LIMIT")) {
my @word = ONO::DB->readcols($line);
if (!$used{$word[3]} && &lang_load_wordlist_prefer_check("",$word[3],$word[4],$vars{'app_input_langs_prefer'})) {
$used{$word[3]}++;
if (&lang_load_wordlist_pass("",$vars{'app_input_difficulty_status'},$word[3],$word[4],$use_language1,$use_language2,$vars{'wordlist_require_audio'})) {
$words_found++;
@WORDLIST = (@WORDLIST,"$word[3]^$word[4]");
$CACHE_WORDS .= qq~$word[3];$word[4];;~;
if ($vars{'app_input_multiple_choice'}) {
@TRANS = (@TRANS,$word[4]);
$CACHE_TRANS .= "$word[4];";
}
}
}
}
}
# DB 3 of 4: we ALWAYS use the LUX db, UNLESS we're using LU language (paradox, but true), OR img, OR we're using custom groups
if (!$use_images && !$use_custom_groups && !$use_lu && $USE_GROUPS_LU && $words_found < $max_words) {
$DEBUG .= " LU[cat IN ($USE_GROUPS_LU)] ";
foreach my $line (ONO::DB->select($db,"school_school_db_vocabulary_lu:id,cat,length,voc_$use_language1,voc_$use_language2","cat IN ($USE_GROUPS_LU)$DIFF",$ORDER[2],"LIMIT $LIMIT")) {
my @word = ONO::DB->readcols($line);
if (!$used{$word[3]} && &lang_load_wordlist_prefer_check("",$word[3],$word[4],$vars{'app_input_langs_prefer'})) {
$used{$word[3]}++;
if (&lang_load_wordlist_pass("",$vars{'app_input_difficulty_status'},$word[3],$word[4],$use_language1,$use_language2,$vars{'wordlist_require_audio'})) {
$words_found++;
@WORDLIST = (@WORDLIST,"$word[3]^$word[4]");
$CACHE_WORDS .= qq~$word[3];$word[4];;~;
if ($vars{'app_input_multiple_choice'}) {
@TRANS = (@TRANS,$word[4]);
$CACHE_TRANS .= "$word[4];";
}
}
}
}
}
# DB 4 of 4: we ONLY use custom groups if selected
foreach my $CUSTOM (@USE_GROUPS_CUSTOM) {
if (length $CUSTOM == 10) {
$DEBUG .= "custom = $CUSTOM, ";
my $APPCONF = "var/community/$community/apps/lists/".ONO::IO->deepdir($CUSTOM)."/$CUSTOM.appconf";
if (ONO::IO->devstation) {
$WEB .= qq~LOADING FROM: $APPCONF<br>~;
}
my (@CUSTOM_WORDS,@CUSTOM_WORDS_ALT,@BACKUP_WORDS,@custom_langs,$custom_source,$custom_source_dir,$custom_type,$override_no_image);
# first run: detect type and langs
foreach my $line (ONO::IO->list($APPCONF)) {
if ($line =~ /^AppConfType "(.*?)"/) {
$custom_type = $1;
$DEBUG .= "custom type = '$1', ";
}
if ($line =~ /^AppConfLang "(.*?)"/) {
$custom_langs[0] = $1;
$DEBUG .= "custom lang[0] = '$1', ";
}
if ($line =~ /^AppConfLang(.*?) "(.*?)"/) {
$custom_langs[$1] = $2;
$DEBUG .= "custom lang[$1] = '$2', ";
}
}
# second run: detect custom, and run...
foreach my $line (ONO::IO->list($APPCONF)) {
if ($line =~ /^AppConfSourceFile "(.*?)"/) {
$custom_source = $1;
if ($vars{'username'}) {
if (ONO::IO->exists("media/users/".ONO::IO->deepdir($vars{'username'})."/$vars{'username'}/documents/lists/images/uploaded/$custom_langs[1]")) {
$custom_source_dir = "media/users/".ONO::IO->deepdir($vars{'username'})."/$vars{'username'}/documents/lists/images/uploaded/$custom_langs[1]";
}
}
if ($custom_source =~ m~media/virclass/(.*?)/class/lists~) {
if (ONO::IO->exists("media/virclass/$1/class/lists/images/uploaded/$custom_langs[1]")) {
$custom_source_dir = "media/virclass/$1/class/lists/images/uploaded/$custom_langs[1]";
}
}
$DEBUG .= "custom img dir = '$custom_source_dir', ";
}
if ($line =~ /^"(.*)"/) {
my @data = split(/\",\"/,$1);
$used{$data[0]}++;
$used{$data[1]}++;
# $data[1] should be updated to support custom uploaded images
if (&lang_load_wordlist_prefer_check("",$data[0],$data[1],$vars{'app_input_langs_prefer'})) {
if ($use_images) {
my $custom_img;
if ($custom_source_dir) {
my $custom_file;
if ($custom_type eq "words" || $custom_type eq "vocs") {
$custom_file = ONO::Ext::Morzino::Apps::Manager::ToolBox->img_detect_custom_file($data[0]);
}
$DEBUG .= "custom img file = '$custom_file', ";
if (ONO::IO->exists("$custom_source_dir/$custom_file")) {
$custom_img = "$custom_source_dir/$custom_file";
}
}
if ($custom_type eq "words") {
if ($data[1] || $custom_img) {
$words_found++;
@CUSTOM_WORDS = (@CUSTOM_WORDS, "$custom_img$data[1]^$data[0]");
} else {
$override_no_image++;
@BACKUP_WORDS = (@BACKUP_WORDS, "$data[1]^$data[0]");
}
}
if ($custom_type eq "vocs") {
my $word = $data[1];
$DEBUG .= "word switch on custom lang select (selected = '$vars{'app_input_langs_lang_1'}', first in list = '$custom_langs[1]'), ";
if ($vars{'app_input_langs_lang_1'} eq $custom_langs[1]) {
$word = $data[0];
}
if ($data[2] || $custom_img) {
$words_found++;
@CUSTOM_WORDS = (@CUSTOM_WORDS, "$custom_img$data[2]^$word");
} else {
$override_no_image++;
@BACKUP_WORDS = (@BACKUP_WORDS, "$data[0]^$word");
}
}
} else {
if ($custom_type eq "words" && $data[0] && $data[1]) {
$use_images_forced++;
$words_found++;
@CUSTOM_WORDS = (@CUSTOM_WORDS, "$data[1]^$data[0]");
}
if ($custom_type eq "vocs" && $data[0] && $data[1]) {
# still missing: select the correct lang here
$words_found++;
@CUSTOM_WORDS = (@CUSTOM_WORDS, "$data[0]^$data[1]");
@CUSTOM_WORDS_ALT = (@CUSTOM_WORDS_ALT, "$data[1]^$data[0]");
}
}
}
}
@CUSTOM_WORDS = ONO::Lib::Basic->array_randomize(@CUSTOM_WORDS);
@CUSTOM_WORDS_ALT = ONO::Lib::Basic->array_randomize(@CUSTOM_WORDS_ALT);
}
# exercise won't run if images are selected but no images are available, so we'll need to use the backup list
if ($use_images && (!$words_found || $override_no_image)) {
@CUSTOM_WORDS = (@CUSTOM_WORDS,@BACKUP_WORDS);
$words_found = @CUSTOM_WORDS;
# $use_images = 0;
$use_images_forced = 0;
$DEBUG .= "no image problem resolved";
}
if (!$use_images && ($vars{'app_input_langs_lang_1'} eq $custom_langs[2] || $vars{'app_input_langs_lang_2'} eq $custom_langs[1])) {
@CUSTOM_WORDS = @CUSTOM_WORDS_ALT;
}
if (
($vars{'app_input_langs_lang_1'} ne $custom_langs[1] && $vars{'app_input_langs_lang_1'} ne $custom_langs[2]) ||
($vars{'app_input_langs_lang_2'} ne $custom_langs[1] && $vars{'app_input_langs_lang_2'} ne $custom_langs[2])
) {
$hide_flags++;
}
foreach my $WORD (@CUSTOM_WORDS) {
@WORDLIST = (@WORDLIST,$WORD);
my @wp = split(/\^/,$WORD);
$CACHE_WORDS .= qq~$wp[0];$wp[1];;~;
}
}
}
if (ONO::IO->devstation) {
my $debug_num = @WORDLIST;
$DEBUG .= ", $debug_num in wordlist",
}
$DEBUG .= "], loaded $words_found words from the SQL DB, langs = $use_language1,$use_language2...";
}
if (ONO::IO->devstation) {
if ($words_found) {
$WEB .= "LangLoadWordList:<br>$DEBUG";
} else {
$WEB .= "<br><br><br><br><br><br><br><br><br><br><br><br><br><br><br>LangLoadWordList:<br>$DEBUG";
}
ONO::IO->store("0_LangLoadWordList.txt",$DEBUG);
}
return (
$WEB,\@WORDLIST,\@TRANS,\@IMGS,
$USE_GROUPS,$USE_GROUPS_ALT,$USE_GROUPS_LU,\@USE_GROUPS_CUSTOM,
$CACHE_WORDS,$CACHE_TRANS,$CACHE_IMGS,
$use_language1,$use_language2,
$use_images,$use_images_reverse,$use_images_forced,$use_custom_groups,$use_lu,
$get_words,$words_found,$multi_frame_custom_width,$hide_flags,$single_lang_mode,
);
}
sub lang_load_wordlist_prefer_check {
my (
$self,
$word1,
$word2,
$prefer,
) = @_;
#: Check if word or verb includes helper (article / person).
my $res = 0;
if ($prefer eq "") {
$res = 1;
}
if ($prefer eq "nouns") {
if ($word1 =~ /^(the|der|die|das|le|la|les|de|den) /) {
$res = 1;
} else {
if ($word2 =~ /^(the|der|die|das|le|la|les|de|den) /) {
$res = 1;
} else {
if ($word1 =~ /^(l|d)\'/) {
$res = 1;
} else {
if ($word2 =~ /^(l|d)\'/) {
$res = 1;
}
}
}
}
}
if ($prefer eq "verbs") {
if ($word1 =~ /^(I|ich|je|ech) /) {
$res = 1;
} else {
if ($word2 =~ /^(I|ich|je|ech) /) {
$res = 1;
} else {
if ($word1 =~ /^j\' /) {
$res = 1;
} else {
if ($word2 =~ /^j\' /) {
$res = 1;
}
}
}
}
}
return $res;
}
sub lang_default_setup_config {
my (
$self,
$vars_ref,
$switches,
) = @_;
#: Default language setup.
#:
#: Switches:
#:
#: -i prefer images if possible
my %vars = %$vars_ref;
my $LU;
my @default = ('en','de');
if (ONO::Lib::Web::Domain->variation eq "oli" || ONO::Lib::Web::Domain->variation eq "education" || ONO::IO->devstation) {
$LU = ":lu";
@default = ('de','fr');
}
if ($ENV{'SERVER_NAME'} =~ /morzino\.(de|at|ch)$/) {
@default = ('de','en');
}
if ($ENV{'SERVER_NAME'} =~ /morzino\.(fr)$/) {
@default = ('fr','en');
}
if ($switches =~ /i/) {
if ($ENV{'SERVER_NAME'} =~ /morzino/ || ONO::IO->devstation) {
@default = ('img','en');
if ($ENV{'SERVER_NAME'} =~ /morzino\.(de|at|ch)$/) {
@default = ('img','de');
}
if ($ENV{'SERVER_NAME'} =~ /morzino\.(fr)$/) {
@default = ('img','fr');
}
}
}
if ($vars{'app_input_langs_lang_1'}) {
$default[0] = $vars{'app_input_langs_lang_1'};
}
if ($vars{'app_input_langs_lang_2'}) {
$default[1] = $vars{'app_input_langs_lang_2'};
}
my @lessons = ONO::FW::Apps::Core::Lang->lessons();
my ($default_lessons,$all_lessons,$custom_lessons);
my $default_lang_id = 1;
if ($vars{'app_lang'} eq "de") {
$default_lang_id = 2;
}
if ($vars{'app_lang'} eq "lu") {
$default_lang_id = 3;
}
if ($vars{'app_lang'} eq "fr") {
$default_lang_id = 4;
}
my $max_cats = 3;
if ($vars{'lang_default_setup_config_max_cats'}) {
$max_cats = $vars{'lang_default_setup_config_max_cats'};
}
for (my $l = 0; $l < ONO::FW::Apps::Core::Lang->lessons_num()+1; $l++) {
my @lp = split(/:/,$lessons[$l]);
$all_lessons .= "$lp[0]:$lp[$default_lang_id];";
if ($l < $max_cats) {
$default_lessons .= "$lp[0]:$lp[$default_lang_id];";
}
}
if (ONO::Lib::Web::Domain->variation eq "oli" || ONO::Lib::Web::Domain->variation eq "education" || ONO::IO->devstation) {
my @cylces = ('2.2','3.1','3.2','4.1','4.2');
my $min = 3;
if ($ENV{'SERVER_NAME'} =~ /oli\.lu/ || ONO::IO->devstation) {
$min = 2;
}
for (my $l = $min; $l < 7; $l++) {
foreach my $line (ONO::IO->list("ono-data/school/vocs/lessons_$l/titles.txt")) {
$line =~ s~(\n|\r|\t)~~g;
my @lp = split(/:/,$line);
if ($lp[0] && $lp[1]) {
$custom_lessons .= qq~custom_${l}_$lp[0]:C$cylces[$l-2] - $lp[0]) $lp[1];~;
}
}
$custom_lessons .= "-;";
}
$custom_lessons =~ s~-;$~~;
}
return (\@default,$default_lessons,$all_lessons,$custom_lessons,$LU);
}
sub lang_image_validator {
#: Validate/check a language image.
if ($_[1] !~ /[A-Za-z]/ && $_[1] =~ /[0-9]/) {
return 1;
} else {
if ($_[1] =~ m~^media/~) {
return 1;
} else {
return 0;
}
}
}
sub lang_image_url_generator {
my (
$self,
$IMG,
$username,
$community,
) = @_;
#: Generate a valid Morzino Picture Dictionary URL.
my $URL = "images/pix/preview/rgb/$IMG.jpg";
if ($IMG =~ m~^media~) {
my %vars;
$vars{'username'} = $username;
$URL = ONO::FW::Media::Manager::ToolBox->protected_image("/$IMG",\%vars,$community);
$URL =~ s~^/cgi-bin~cgi-bin~;
}
return $URL;
}
###################################################################
# THAT'S IT :-D
###################################################################
1;
__END__