ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
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 . "/Customizing/global/lang";
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  require_once("./Services/Language/classes/class.ilCachedLanguage.php");
109  $this->global_cache = ilCachedLanguage::getInstance($this->lang_key);
110  if ($this->global_cache->isActive()) {
111  $this->cached_modules = $this->global_cache->getTranslations();
112  }
113  $this->loadLanguageModule("common");
114  }
115 
119  public function getLangKey(): string
120  {
121  return $this->lang_key;
122  }
123 
127  public function getDefaultLanguage(): string
128  {
129  return $this->lang_default ?? "en";
130  }
131 
135  public function getTextDirection(): string
136  {
137  $rtl = array("ar", "fa", "ur", "he");
138  if (in_array($this->getContentLanguage(), $rtl)) {
139  return "rtl";
140  }
141  return "ltr";
142  }
143 
147  public function getContentLanguage(): string
148  {
149  if ($this->getUserLanguage()) {
150  return $this->getUserLanguage();
151  }
152  return $this->getLangKey();
153  }
154 
159  public function txtlng(string $a_module, string $a_topic, string $a_language): string
160  {
161  if (strcmp($a_language, $this->lang_key) === 0) {
162  return $this->txt($a_topic);
163  } else {
164  return self::_lookupEntry($a_language, $a_module, $a_topic);
165  }
166  }
167 
172  public function txt(string $a_topic, string $a_default_lang_fallback_mod = ""): string
173  {
174  if (empty($a_topic)) {
175  return "";
176  }
177 
178  // remember the used topics
179  self::$used_topics[$a_topic] = $a_topic;
180 
181  $translation = $this->text[$a_topic] ?? "";
182 
183  if ($translation === "" && $a_default_lang_fallback_mod !== "") {
184  // #13467 - try current language first (could be missing module)
185  if ($this->lang_key != $this->lang_default) {
186  $translation = self::_lookupEntry(
187  $this->lang_key,
188  $a_default_lang_fallback_mod,
189  $a_topic
190  );
191  }
192  // try default language last
193  if ($translation === "" || $translation === "-" . $a_topic . "-") {
194  $translation = self::_lookupEntry(
195  $this->lang_default,
196  $a_default_lang_fallback_mod,
197  $a_topic
198  );
199  }
200  }
201 
202 
203  if ($translation === "") {
204  if (ILIAS_LOG_ENABLED && is_object($this->log)) {
205  $this->log->debug("Language (" . $this->lang_key . "): topic -" . $a_topic . "- not present");
206  }
207  return "-" . $a_topic . "-";
208  }
209 
210  if ($this->usage_log_enabled) {
211  self::logUsage($this->map_modules_txt[$a_topic] ?? "", $a_topic);
212  }
213 
214  return $translation;
215  }
216 
220  public function exists(string $a_topic): bool
221  {
222  return isset($this->text[$a_topic]);
223  }
224 
228  public function loadLanguageModule(string $a_module): void
229  {
230  global $DIC;
231  $ilDB = $DIC->database();
232 
233  if (in_array($a_module, $this->loaded_modules, true)) {
234  return;
235  }
236 
237  $this->loaded_modules[] = $a_module;
238 
239  // remember the used modules globally
240  self::$used_modules[$a_module] = $a_module;
241 
242  $lang_key = $this->lang_key;
243 
244  if (empty($this->lang_key)) {
245  $lang_key = $this->lang_user;
246  }
247 
248  if (isset($this->cached_modules[$a_module]) && is_array($this->cached_modules[$a_module])) {
249  $this->text = array_merge($this->text, $this->cached_modules[$a_module]);
250 
251  if ($this->usage_log_enabled) {
252  foreach (array_keys($this->cached_modules[$a_module]) as $key) {
253  $this->map_modules_txt[$key] = $a_module;
254  }
255  }
256 
257  return;
258  }
259 
260  $q = "SELECT * FROM lng_modules " .
261  "WHERE lang_key = " . $ilDB->quote($lang_key, "text") . " AND module = " .
262  $ilDB->quote($a_module, "text");
263  $r = $ilDB->query($q);
264  $row = $r->fetchRow(ilDBConstants::FETCHMODE_ASSOC);
265 
266  if ($row === false) {
267  return;
268  }
269 
270  $new_text = unserialize($row["lang_array"], ["allowed_classes" => false]);
271  if (is_array($new_text)) {
272  $this->text = array_merge($this->text, $new_text);
273 
274  if ($this->usage_log_enabled) {
275  foreach (array_keys($new_text) as $key) {
276  $this->map_modules_txt[$key] = $a_module;
277  }
278  }
279  }
280  }
281 
285  public function getInstalledLanguages(): array
286  {
287  return self::_getInstalledLanguages();
288  }
289 
293  public static function _getInstalledLanguages(): array
294  {
295  include_once "./Services/Object/classes/class.ilObject.php";
296  $langlist = ilObject::_getObjectsByType("lng");
297 
298  $languages = [];
299  foreach ($langlist as $lang) {
300  if (strpos($lang["desc"], "installed") === 0) {
301  $languages[] = $lang["title"];
302  }
303  }
304 
305  return $languages ?: [];
306  }
307 
308  public static function _lookupEntry(string $a_lang_key, string $a_mod, string $a_id): string
309  {
310  global $DIC;
311  $ilDB = $DIC->database();
312 
313  $set = $ilDB->query($q = sprintf(
314  "SELECT * FROM lng_data WHERE module = %s " .
315  "AND lang_key = %s AND identifier = %s",
316  $ilDB->quote($a_mod, "text"),
317  $ilDB->quote($a_lang_key, "text"),
318  $ilDB->quote($a_id, "text")
319  ));
320  $rec = $ilDB->fetchAssoc($set);
321 
322  if (isset($rec["value"]) && $rec["value"] != "") {
323  // remember the used topics
324  self::$used_topics[$a_id] = $a_id;
325  self::$used_modules[$a_mod] = $a_mod;
326 
327  if (self::isUsageLogEnabled()) {
328  self::logUsage($a_mod, $a_id);
329  }
330 
331  return $rec["value"];
332  }
333 
334  return "-" . $a_id . "-";
335  }
336 
340  public static function lookupId(string $a_lang_key): int
341  {
342  global $DIC;
343  $ilDB = $DIC->database();
344 
345  $query = "SELECT obj_id FROM object_data " . " " .
346  "WHERE title = " . $ilDB->quote($a_lang_key, "text") . " " .
347  "AND type = " . $ilDB->quote("lng", "text");
348 
349  $res = $ilDB->query($query);
350  while ($row = $res->fetchRow(ilDBConstants::FETCHMODE_OBJECT)) {
351  return (int) $row->obj_id;
352  }
353  return 0;
354  }
355 
359  public function getUsedTopics(): array
360  {
361  asort(self::$used_topics);
362  return self::$used_topics;
363  }
364 
368  public function getUsedModules(): array
369  {
370  asort(self::$used_modules);
371  return self::$used_modules;
372  }
373 
377  public function getUserLanguage(): string
378  {
379  return $this->lang_user;
380  }
381 
382  public function getCustomLangPath(): string
383  {
384  return $this->cust_lang_path;
385  }
386 
390  public static function getFallbackInstance(): ilLanguage
391  {
392  return new self("en");
393  }
394 
398  public static function getGlobalInstance(): self
399  {
400  global $DIC;
401 
402  $ilSetting = $DIC->settings();
403 
404  $ilUser = null;
405  if ($DIC->offsetExists("ilUser")) {
406  $ilUser = $DIC->user();
407  }
408 
409  $isset_get_lang = $DIC->http()->wrapper()->query()->has("lang");
410  if (!ilSession::get("lang") && !$isset_get_lang && $ilUser instanceof ilObjUser &&
411  (!$ilUser->getId() || $ilUser->isAnonymous())) {
412  $language_detection = new ilLanguageDetection();
413  $language = $language_detection->detect();
414 
415  ilSession::set("lang", $language);
416  }
417 
418  $post_change_lang_to = [];
419  if ($DIC->http()->wrapper()->post()->has('change_lang_to')) {
420  $post_change_lang_to = $DIC->http()->wrapper()->post()->retrieve(
421  'change_lang_to',
422  $DIC->refinery()->kindlyTo()->dictOf(
423  $DIC->refinery()->kindlyTo()->string()
424  )
425  );
426  }
427 
428  // prefer personal setting when coming from login screen
429  // Added check for ilUser->getId > 0 because it is 0 when the language is changed and
430  // the terms of service should be displayed
431  if ($ilUser instanceof ilObjUser &&
432  (($ilUser->getId() && !$ilUser->isAnonymous()))
433  ) {
434  ilSession::set("lang", $ilUser->getPref("language"));
435  }
436 
437  $get_lang = null;
438  if ($isset_get_lang) {
439  $get_lang = $DIC->http()->wrapper()->query()->retrieve(
440  "lang",
441  $DIC->refinery()->kindlyTo()->string()
442  );
443  }
444  ilSession::set("lang", ($isset_get_lang && $get_lang) ? $get_lang : ilSession::get("lang"));
445 
446  // check whether lang selection is valid
447  $langs = self::_getInstalledLanguages();
448  if (!in_array(ilSession::get("lang"), $langs, true)) {
449  if ($ilSetting instanceof ilSetting && (string) $ilSetting->get("language", '') !== "") {
450  ilSession::set("lang", $ilSetting->get("language"));
451  } else {
452  ilSession::set("lang", $langs[0]);
453  }
454  }
455 
456  return new self(ilSession::get("lang"));
457  }
458 
466  public function toJS($a_lang_key, ilGlobalTemplateInterface $a_tpl = null): void
467  {
468  global $DIC;
469  $tpl = $DIC["tpl"];
470 
471  if (!is_object($a_tpl)) {
472  $a_tpl = $tpl;
473  }
474 
475  if (!is_array($a_lang_key)) {
476  $a_lang_key = array($a_lang_key);
477  }
478 
479  $map = array();
480  foreach ($a_lang_key as $lk) {
481  $map[$lk] = $this->txt($lk);
482  }
483  $this->toJSMap($map, $a_tpl);
484  }
485 
491  public function toJSMap(array $a_map, ilGlobalTemplateInterface $a_tpl = null): void
492  {
493  global $DIC;
494  $tpl = $DIC["tpl"];
495 
496  if (!is_object($a_tpl)) {
497  $a_tpl = $tpl;
498  }
499 
500  if (!is_array($a_map)) {
501  return;
502  }
503 
504  foreach ($a_map as $k => $v) {
505  if ($v != "") {
506  $a_tpl->addOnloadCode("il.Language.setLangVar('" . $k . "', " . json_encode($v, JSON_THROW_ON_ERROR) . ");");
507  }
508  }
509  }
510 
514  protected static function logUsage(string $a_module, string $a_identifier): void
515  {
516  if ($a_module !== "" && $a_identifier !== "") {
517  self::$lng_log[$a_identifier] = $a_module;
518  }
519  }
520 
527  protected static function isUsageLogEnabled(): bool
528  {
529  global $DIC;
530  $ilClientIniFile = $DIC->clientIni();
531  $ilDB = $DIC->database();
532 
533  if (!$ilClientIniFile instanceof ilIniFile) {
534  return false;
535  }
536 
537  if (defined("DEVMODE") && DEVMODE) {
538  return true;
539  }
540 
541  if (!$ilClientIniFile->variableExists("system", "LANGUAGE_LOG")) {
542  return (int) $ilClientIniFile->readVariable("system", "LANGUAGE_LOG") === 1;
543  }
544  return false;
545  }
546 
550  public function __destruct()
551  {
552  global $DIC;
553 
554  //case $ilDB not existing should not happen but if something went wrong it shouldn't leads to any failures
555  if (!$this->usage_log_enabled || !$DIC->isDependencyAvailable("database")) {
556  return;
557  }
558 
559  $ilDB = $DIC->database();
560 
561  foreach (self::$lng_log as $identifier => $module) {
562  $wave[] = "(" . $ilDB->quote($module, "text") . ', ' . $ilDB->quote($identifier, "text") . ")";
563  unset(self::$lng_log[$identifier]);
564 
565  if (count($wave) === 150 || (count(self::$lng_log) === 0 && count($wave) > 0)) {
566  $query = "REPLACE INTO lng_log (module, identifier) VALUES " . implode(", ", $wave);
567  $ilDB->manipulate($query);
568 
569  $wave = array();
570  }
571  }
572  }
573 } // 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.
Class ChatMainBarProvider .
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
global $DIC
Definition: feed.php:28
ilCachedLanguage $global_cache
string $key
Consumer key/client ID value.
Definition: System.php:193
static lookupId(string $a_lang_key)
Lookup obj_id of language.
$query
getInstalledLanguages()
Get installed languages.
static getFallbackInstance()
Builds a global default language instance.
static array $used_modules
$lang
Definition: xapiexit.php:26
getUsedTopics()
Return used topics.
global $ilSetting
Definition: privfeed.php:17
$ilUser
Definition: imgupload.php:34
__destruct()
destructor saves all language usages to db if log is enabled and ilDB exists
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.
if($DIC->http() ->request() ->getMethod()=="GET" &&isset($DIC->http() ->request() ->getQueryParams()['tex'])) $tpl
Definition: latex.php:41
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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.
This file is part of ILIAS, a powerful learning management system published by ILIAS open source e-Le...
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