ILIAS  trunk Revision v11.0_alpha-3011-gc6b235a2e85
class.ilLanguage.php
Go to the documentation of this file.
1<?php
2
19declare(strict_types=1);
20
43{
44 public ILIAS $ilias;
45 public array $text = [];
46 public string $lang_default;
47 public string $lang_user;
48 public string $lang_path;
49 public string $lang_key;
50 public string $lang_name;
51 public string $separator = "#:#";
52 public string $comment_separator = "###";
53 public array $loaded_modules = array();
54 protected static array $used_topics = array();
55 protected static array $used_modules = array();
56 protected array $cached_modules = array();
57 protected array $map_modules_txt = array();
58 protected bool $usage_log_enabled = false;
59 protected static array $lng_log = array();
60 protected string $cust_lang_path;
61 protected ilLogger $log;
63
73 public function __construct(string $a_lang_key)
74 {
75 global $DIC;
76 $client_ini = $DIC->clientIni();
77
78 $this->log = $DIC->logger()->root();
79
80 $this->lang_key = $a_lang_key;
81
82 $this->usage_log_enabled = self::isUsageLogEnabled();
83
84 $this->lang_path = ILIAS_ABSOLUTE_PATH . "/lang";
85 $this->cust_lang_path = ILIAS_ABSOLUTE_PATH . "/lang/customizing";
86
87 $this->lang_default = $client_ini->readVariable("language", "default") ?? 'en';
88 $this->lang_user = $this->lang_default;
89
90 if ($DIC->offsetExists("ilSetting")) {
91 $ilSetting = $DIC->settings();
92 if ($ilSetting->get("language") != "") {
93 $this->lang_default = $ilSetting->get("language");
94 }
95 }
96 if ($DIC->offsetExists("ilUser")) {
97 $ilUser = $DIC->user();
98 $this->lang_user = $ilUser->getLanguage();
99 }
100
101 $langs = $this->getInstalledLanguages();
102
103 if (!in_array($this->lang_key, $langs, true)) {
104 $this->lang_key = $this->lang_default;
105 }
106
107 $this->global_cache = ilCachedLanguage::getInstance($this->lang_key);
108 if ($this->global_cache->isActive()) {
109 $this->cached_modules = $this->global_cache->getTranslations();
110 }
111 $this->loadLanguageModule("common");
112 }
113
117 public function getLangKey(): string
118 {
119 return $this->lang_key;
120 }
121
125 public function getDefaultLanguage(): string
126 {
127 return $this->lang_default ?? "en";
128 }
129
133 public function getTextDirection(): string
134 {
135 $rtl = array("ar", "fa", "ur", "he");
136 if (in_array($this->getContentLanguage(), $rtl)) {
137 return "rtl";
138 }
139 return "ltr";
140 }
141
145 public function getContentLanguage(): string
146 {
147 if ($this->getUserLanguage()) {
148 return $this->getUserLanguage();
149 }
150 return $this->getLangKey();
151 }
152
157 public function txtlng(string $a_module, string $a_topic, string $a_language): string
158 {
159 if (strcmp($a_language, $this->lang_key) === 0) {
160 return $this->txt($a_topic);
161 } else {
162 return self::_lookupEntry($a_language, $a_module, $a_topic);
163 }
164 }
165
170 public function txt(string $a_topic, string $a_default_lang_fallback_mod = ""): string
171 {
172 if (empty($a_topic)) {
173 return "";
174 }
175
176 // remember the used topics
177 self::$used_topics[$a_topic] = $a_topic;
178
179 $translation = $this->text[$a_topic] ?? "";
180
181 if ($translation === "" && $a_default_lang_fallback_mod !== "") {
182 // #13467 - try current language first (could be missing module)
183 if ($this->lang_key != $this->lang_default) {
184 $translation = self::_lookupEntry(
185 $this->lang_key,
186 $a_default_lang_fallback_mod,
187 $a_topic
188 );
189 }
190 // try default language last
191 if ($translation === "" || $translation === "-" . $a_topic . "-") {
192 $translation = self::_lookupEntry(
193 $this->lang_default,
194 $a_default_lang_fallback_mod,
195 $a_topic
196 );
197 }
198 }
199
200
201 if ($translation === "") {
202 if (ILIAS_LOG_ENABLED && is_object($this->log)) {
203 $this->log->debug("Language (" . $this->lang_key . "): topic -" . $a_topic . "- not present");
204 }
205 return "-" . $a_topic . "-";
206 }
207
208 if ($this->usage_log_enabled) {
209 self::logUsage($this->map_modules_txt[$a_topic] ?? "", $a_topic);
210 }
211
212 return $translation;
213 }
214
218 public function exists(string $a_topic): bool
219 {
220 return isset($this->text[$a_topic]);
221 }
222
226 public function loadLanguageModule(string $a_module): void
227 {
228 global $DIC;
229 $ilDB = $DIC->database();
230
231 if (in_array($a_module, $this->loaded_modules, true)) {
232 return;
233 }
234
235 $this->loaded_modules[] = $a_module;
236
237 // remember the used modules globally
238 self::$used_modules[$a_module] = $a_module;
239
241
242 if (empty($this->lang_key)) {
244 }
245
246 if (isset($this->cached_modules[$a_module]) && is_array($this->cached_modules[$a_module])) {
247 $this->text = array_merge($this->text, $this->cached_modules[$a_module]);
248
249 if ($this->usage_log_enabled) {
250 foreach (array_keys($this->cached_modules[$a_module]) as $key) {
251 $this->map_modules_txt[$key] = $a_module;
252 }
253 }
254
255 return;
256 }
257
258 $q = "SELECT * FROM lng_modules " .
259 "WHERE lang_key = " . $ilDB->quote($lang_key, "text") . " AND module = " .
260 $ilDB->quote($a_module, "text");
261 $r = $ilDB->query($q);
262 $row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
263
264 if ($row === false) {
265 return;
266 }
267
268 $new_text = unserialize($row["lang_array"], ["allowed_classes" => false]);
269 if (is_array($new_text)) {
270 $this->text = array_merge($this->text, $new_text);
271
272 if ($this->usage_log_enabled) {
273 foreach (array_keys($new_text) as $key) {
274 $this->map_modules_txt[$key] = $a_module;
275 }
276 }
277 }
278 }
279
283 public function getInstalledLanguages(): array
284 {
286 }
287
291 public static function _getInstalledLanguages(): array
292 {
293 $langlist = ilObject::_getObjectsByType("lng");
294
295 $languages = [];
296 foreach ($langlist as $lang) {
297 if (strpos($lang["desc"], "installed") === 0) {
298 $languages[] = $lang["title"];
299 }
300 }
301
302 return $languages ?: [];
303 }
304
305 public static function _lookupEntry(string $a_lang_key, string $a_mod, string $a_id): string
306 {
307 global $DIC;
308 $ilDB = $DIC->database();
309
310 $set = $ilDB->query($q = sprintf(
311 "SELECT * FROM lng_data WHERE module = %s " .
312 "AND lang_key = %s AND identifier = %s",
313 $ilDB->quote($a_mod, "text"),
314 $ilDB->quote($a_lang_key, "text"),
315 $ilDB->quote($a_id, "text")
316 ));
317 $rec = $ilDB->fetchAssoc($set);
318
319 if (isset($rec["value"]) && $rec["value"] != "") {
320 // remember the used topics
321 self::$used_topics[$a_id] = $a_id;
322 self::$used_modules[$a_mod] = $a_mod;
323
324 if (self::isUsageLogEnabled()) {
325 self::logUsage($a_mod, $a_id);
326 }
327
328 return $rec["value"];
329 }
330
331 return "-" . $a_id . "-";
332 }
333
337 public static function lookupId(string $a_lang_key): int
338 {
339 global $DIC;
340 $ilDB = $DIC->database();
341
342 $query = "SELECT obj_id FROM object_data " . " " .
343 "WHERE title = " . $ilDB->quote($a_lang_key, "text") . " " .
344 "AND type = " . $ilDB->quote("lng", "text");
345
346 $res = $ilDB->query($query);
347 while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
348 return (int) $row->obj_id;
349 }
350 return 0;
351 }
352
356 public function getUsedTopics(): array
357 {
358 asort(self::$used_topics);
359 return self::$used_topics;
360 }
361
365 public function getUsedModules(): array
366 {
367 asort(self::$used_modules);
368 return self::$used_modules;
369 }
370
374 public function getUserLanguage(): string
375 {
376 return $this->lang_user;
377 }
378
379 public function getCustomLangPath(): string
380 {
382 }
383
387 public static function getFallbackInstance(): ilLanguage
388 {
389 return new self("en");
390 }
391
395 public static function getGlobalInstance(): self
396 {
397 global $DIC;
398
399 $ilSetting = $DIC->settings();
400
401 $ilUser = null;
402 if ($DIC->offsetExists("ilUser")) {
403 $ilUser = $DIC->user();
404 }
405
406 $isset_get_lang = $DIC->http()->wrapper()->query()->has("lang");
407 if (!ilSession::get("lang") && !$isset_get_lang && $ilUser instanceof ilObjUser &&
408 (!$ilUser->getId() || $ilUser->isAnonymous())) {
409 $language_detection = new ilLanguageDetection();
410 $language = $language_detection->detect();
411
412 ilSession::set("lang", $language);
413 }
414
415 $post_change_lang_to = [];
416 if ($DIC->http()->wrapper()->post()->has('change_lang_to')) {
417 $post_change_lang_to = $DIC->http()->wrapper()->post()->retrieve(
418 'change_lang_to',
419 $DIC->refinery()->kindlyTo()->dictOf(
420 $DIC->refinery()->kindlyTo()->string()
421 )
422 );
423 }
424
425 // prefer personal setting when coming from login screen
426 // Added check for ilUser->getId > 0 because it is 0 when the language is changed and
427 // the terms of service should be displayed
428 if ($ilUser instanceof ilObjUser &&
429 (($ilUser->getId() && !$ilUser->isAnonymous()))
430 ) {
431 ilSession::set("lang", $ilUser->getLanguage());
432 }
433
434 $get_lang = null;
435 if ($isset_get_lang) {
436 $get_lang = $DIC->http()->wrapper()->query()->retrieve(
437 "lang",
438 $DIC->refinery()->kindlyTo()->string()
439 );
440 }
441 ilSession::set("lang", ($isset_get_lang && $get_lang) ? $get_lang : ilSession::get("lang"));
442
443 // check whether lang selection is valid
445 if (!in_array(ilSession::get("lang"), $langs, true)) {
446 if ($ilSetting instanceof ilSetting && (string) $ilSetting->get("language", '') !== "") {
447 ilSession::set("lang", $ilSetting->get("language"));
448 } else {
449 ilSession::set("lang", $langs[0]);
450 }
451 }
452
453 return new self(ilSession::get("lang"));
454 }
455
463 public function toJS($a_lang_key, ?ilGlobalTemplateInterface $a_tpl = null): void
464 {
465 global $DIC;
466 $tpl = $DIC["tpl"];
467
468 if (!is_object($a_tpl)) {
469 $a_tpl = $tpl;
470 }
471
472 if (!is_array($a_lang_key)) {
473 $a_lang_key = array($a_lang_key);
474 }
475
476 $map = array();
477 foreach ($a_lang_key as $lk) {
478 $map[$lk] = $this->txt($lk);
479 }
480 $this->toJSMap($map, $a_tpl);
481 }
482
488 public function toJSMap(array $a_map, ?ilGlobalTemplateInterface $a_tpl = null): void
489 {
490 global $DIC;
491 $tpl = $DIC["tpl"];
492
493 if (!is_object($a_tpl)) {
494 $a_tpl = $tpl;
495 }
496
497 if (!is_array($a_map)) {
498 return;
499 }
500
501 foreach ($a_map as $k => $v) {
502 if ($v != "") {
503 $a_tpl->addOnloadCode("il.Language.setLangVar('" . $k . "', " . json_encode($v, JSON_THROW_ON_ERROR) . ");");
504 }
505 }
506 }
507
511 protected static function logUsage(string $a_module, string $a_identifier): void
512 {
513 if ($a_module !== "" && $a_identifier !== "") {
514 self::$lng_log[$a_identifier] = $a_module;
515 }
516 }
517
524 protected static function isUsageLogEnabled(): bool
525 {
526 global $DIC;
527 $ilClientIniFile = $DIC->clientIni();
528 $ilDB = $DIC->database();
529
530 if (!$ilClientIniFile instanceof ilIniFile) {
531 return false;
532 }
533
534 if (defined("DEVMODE") && DEVMODE) {
535 return true;
536 }
537
538 if (!$ilClientIniFile->variableExists("system", "LANGUAGE_LOG")) {
539 return (int) $ilClientIniFile->readVariable("system", "LANGUAGE_LOG") === 1;
540 }
541 return false;
542 }
543
547 public function __destruct()
548 {
549 global $DIC;
550
551 //case $ilDB not existing should not happen but if something went wrong it shouldn't leads to any failures
552 if (!$this->usage_log_enabled || !$DIC->isDependencyAvailable("database")) {
553 return;
554 }
555
556 $ilDB = $DIC->database();
557
558 foreach (self::$lng_log as $identifier => $module) {
559 $wave[] = "(" . $ilDB->quote($module, "text") . ', ' . $ilDB->quote($identifier, "text") . ")";
560 unset(self::$lng_log[$identifier]);
561
562 if (count($wave) === 150 || (count(self::$lng_log) === 0 && count($wave) > 0)) {
563 $query = "REPLACE INTO lng_log (module, identifier) VALUES " . implode(", ", $wave);
564 $ilDB->manipulate($query);
565
566 $wave = array();
567 }
568 }
569 }
570} // END class.Language
Class ilCachedLanguage.
INIFile Parser Early access in init proceess! Avoid further dependencies like logging or other servic...
Class ilLanguageDetection.
language handling
toJSMap(array $a_map, ?ilGlobalTemplateInterface $a_tpl=null)
Transfer text to Javascript.
loadLanguageModule(string $a_module)
Load language module.
getLangKey()
Return lang key.
getContentLanguage()
Return content language.
static getFallbackInstance()
Builds a global default language instance.
bool $usage_log_enabled
toJS($a_lang_key, ?ilGlobalTemplateInterface $a_tpl=null)
Transfer text to Javascript.
static lookupId(string $a_lang_key)
Lookup obj_id of language.
getUsedTopics()
Return used topics.
static array $used_modules
string $lang_default
exists(string $a_topic)
Check if language entry exists.
static _getInstalledLanguages()
Get installed languages.
txt(string $a_topic, string $a_default_lang_fallback_mod="")
gets the text for a given topic if the topic is not in the list, the topic itself with "-" will be re...
static isUsageLogEnabled()
checks if language usage log is enabled you need MySQL to use this function this function is automati...
array $cached_modules
static array $used_topics
getUsedModules()
Return used modules.
__destruct()
destructor saves all language usages to db if log is enabled and ilDB exists
getDefaultLanguage()
Return default language.
static array $lng_log
static logUsage(string $a_module, string $a_identifier)
saves tupel of language module and identifier
array $map_modules_txt
getInstalledLanguages()
Get installed languages.
array $loaded_modules
string $comment_separator
txtlng(string $a_module, string $a_topic, string $a_language)
gets the text for a given topic in a given language if the topic is not in the list,...
getTextDirection()
Return text direction.
static getGlobalInstance()
Builds the global language object.
getUserLanguage()
Return language of user.
static _lookupEntry(string $a_lang_key, string $a_mod, string $a_id)
ilCachedLanguage $global_cache
string $cust_lang_path
__construct(string $a_lang_key)
Constructor read the single-language file and put this in an array text.
Component logger with individual log levels by component id.
User class.
static _getObjectsByType(string $obj_type="", ?int $owner=null)
static get(string $a_var)
static set(string $a_var, $a_val)
Set a value.
ILIAS Setting Class.
$res
Definition: ltiservices.php:69
Interface Observer \BackgroundTasks Contains several chained tasks and infos about them.
global $ilSetting
Definition: privfeed.php:31
global $DIC
Definition: shib_login.php:26
$q
Definition: shib_logout.php:23
$lang
Definition: xapiexit.php:25