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