config = app('config'); $this->request = app('request'); // Get list of available languages $this->langlist = $this->getLanguageList(); $lang = $this->getCurrentLanguage(); $this->readIni(); } /** * Set the language for the application. * * @param string $lang The language code to be set. * @return bool True if the language is valid and successfully set, False otherwise. */ public function setLanguage(string $lang): bool { if (! $this->isValidLanguage($lang)) { return false; } $this->language = $lang; session(['usersettings.language' => $lang]); if ((! isset($_COOKIE['language']) || $_COOKIE['language'] !== $lang) && ! $this->request->isApiOrCronRequest()) { $isAPIRequest = $this->request->isApiOrCronRequest(); EventDispatcher::addFilterListener( 'leantime.core.http.httpkernel.handle.beforeSendResponse', fn ($response) => tap($response, fn (Response $response) => $response->headers->setCookie( Cookie::create('language') ->withValue($lang) ->withExpires(time() + 60 * 60 * 24 * 30) ->withPath(Str::finish($this->config->appDir, '/')) ->withSameSite('lax') )) ); } $this->readIni(); return true; } /** * Get the currently selected language. * * @return string The currently selected language. */ public function getCurrentLanguage(): string { if (session()->has('usersettings.language')) { $this->language = session('usersettings.language'); return $this->language; } if (isset($_COOKIE['language'])) { $this->language = $_COOKIE['language']; return $this->language; } if (session('companysettings.language')) { $this->language = session('companysettings.language'); return $this->language; } $language = substr($_SERVER['HTTP_ACCEPT_LANGUAGE'] ?? 'en-US', 0, 2); $language = str_replace('_', '-', $language); if ($language && $this->isValidLanguage($language)) { return $this->language; } $this->language = $this->config->language; return $this->language; } /** * Check if a given language code is valid. * * @param string $langCode The language code to check. * @return bool True if the language code is valid, false otherwise. */ public function isValidLanguage(string $langCode): bool { return isset($this->langlist[$langCode]); } /** * Read and load the language resources from the ini files. * * @return array The array of language resources loaded from the ini files. * * @throws Exception If the default english language file en-US.ini cannot be found. */ public function readIni(): array { if (@Cache::store('installation')->has('languages.lang_'.$this->language)) { $this->ini_array = self::dispatchFilter( 'language_resources', Cache::store('installation')->get('languages.lang_'.$this->language), [ 'language' => $this->language, ] ) ?? Cache::store('installation')->get('languages.lang_'.$this->language); Cache::store('installation')->set('languages.lang_'.$this->language, $this->ini_array); return $this->ini_array; } // Default to english US if (! file_exists(self::DEFAULT_LANG_FOLDER.'/en-US.ini')) { throw new Exception('Cannot find default english language file en-US.ini'); } $mainLanguageArray = parse_ini_file(self::DEFAULT_LANG_FOLDER.'en-US.ini', false, INI_SCANNER_RAW); foreach ( $languageFiles = self::dispatchFilter('language_files', [ // Complement english with english customization self::CUSTOM_LANG_FOLDER.'en-US.ini' => false, // Overwrite english language by non-english language self::DEFAULT_LANG_FOLDER.$this->language.'.ini' => true, // Overwrite with non-engish customizations self::CUSTOM_LANG_FOLDER.$this->language.'.ini' => true, ], ['language' => $this->language]) as $language_file => $isForeign ) { $mainLanguageArray = $this->includeOverrides($mainLanguageArray, $language_file, $isForeign); } $this->ini_array = self::dispatchFilter( 'language_resources', $mainLanguageArray, [ 'language' => $this->language, ] ); Cache::store('installation')->set('languages.lang_'.$this->language, $this->ini_array); return $this->ini_array; } /** * Include language overrides from an ini file. * * @param array $language The original language array. * @param string $filepath The path to the ini file. * @param bool $foreignLanguage Whether the language is foreign or not. Defaults to false. * @return array The modified language array. * * @throws Exception If the ini file cannot be parsed. */ protected function includeOverrides(array $language, string $filepath, bool $foreignLanguage = false): array { if ($foreignLanguage && $this->language == 'en-US') { return $language; } if (! file_exists($filepath)) { return $language; } $ini_overrides = parse_ini_file($filepath, false, INI_SCANNER_RAW); if (! is_array($ini_overrides)) { throw new Exception("Could not parse ini file $filepath"); } foreach ($ini_overrides as $languageKey => $languageValue) { $language[$languageKey] = $languageValue; } return $language; } /** * Get the list of languages. * * Retrieves the list of languages from a cache or from INI files if the cache is not available. * The list of languages is stored in an associative array where the keys represent the language codes * and the values represent the language names. * * @return bool|array The list of languages as an associative array, or false if the list is empty or cannot be retrieved. */ public function getLanguageList(): bool|array { if (Cache::store('installation')->has('languages.langlist')) { return Cache::store('installation')->get('languages.langlist'); } $langlist = false; if (file_exists(self::DEFAULT_LANG_FOLDER.'/languagelist.ini')) { $langlist = parse_ini_file( self::DEFAULT_LANG_FOLDER.'/languagelist.ini', false, INI_SCANNER_RAW ); } if (file_exists(self::CUSTOM_LANG_FOLDER.'/languagelist.ini')) { $langlist = parse_ini_file( self::CUSTOM_LANG_FOLDER.'/languagelist.ini', false, INI_SCANNER_RAW ); } $parsedLangList = self::dispatchFilter('languages', $langlist); Cache::store('installation')->set('languages.langlist', $parsedLangList); return $parsedLangList; } /** * Get a translated string or a default value if the index is not found. * * @param string $index The index of the translated string. * @param string $default The default value to return if the index is not found. Defaults to an empty string. * @return string The translated string or the default value if the index is not found. */ public function __(string $index, string $default = ''): string { // If index cannot be found return default or original string if (! isset($this->ini_array[$index])) { if (! empty($default)) { return $default; } if ($this->alert) { return sprintf('%s', $index); } return $index; } $returnValue = match (trim($index)) { 'language.dateformat' => session('usersettings.date_format') ?? $this->ini_array['language.dateformat'], 'language.timeformat' => session('usersettings.time_format') ?? $this->ini_array['language.timeformat'], default => $this->ini_array[$index], }; return (string) $returnValue; } public function mergeLanguageArray($newLanguageArray) { if (is_array($newLanguageArray)) { $this->ini_array = array_merge($this->ini_array, $newLanguageArray); } } public function get(string $index, $default = '', $locale = '') { $contentReplacement = ''; if (is_array($default) && count($default) > 0) { $contentReplacement = $default[0]; } return $this->__($index, $contentReplacement); } }