0) { if (1 == Database::result($rs, 0, 'count')) { return true; } else { return false; } } else { return false; } } /** * Get name of language by id. * * @param int $language_id The language id * * @return string The original name of language */ public static function get_name_of_language_by_id($language_id) { $table = Database::get_main_table(TABLE_MAIN_LANGUAGE); $language_id = intval($language_id); $sql = "SELECT original_name FROM $table WHERE id = $language_id"; $rs = Database::query($sql); if (Database::num_rows($rs) > 0) { return Database::result($rs, 0, 'original_name'); } else { return ''; } } /** * Verified if language is sub-language. * * @param int $language_id * * @return bool */ public static function check_if_language_is_sub_language($language_id) { $table = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = 'SELECT count(*) AS count FROM '.$table.' WHERE id = '.intval($language_id).' AND NOT ISNULL(parent_id)'; $rs = Database::query($sql); if (Database::num_rows($rs) > 0 && 1 == Database::result($rs, '0', 'count')) { return true; } else { return false; } } /** * @param int $language_id * * @return bool */ public static function check_if_language_is_used(int $language_id): bool { $language_info = self::get_all_information_of_language($language_id); $table = Database::get_main_table(TABLE_MAIN_USER); $sql = 'SELECT count(*) AS count FROM '.$table.' WHERE locale ="'.Database::escape_string($language_info['english_name']).'" AND active <> '.USER_SOFT_DELETED; $rs = Database::query($sql); if (Database::num_rows($rs) > 0 && Database::result($rs, '0', 'count') >= 1) { return true; } else { return false; } } /** * Verified if language is father of an sub-language. * * @param int $language_id The language id * * @return bool */ public static function check_if_language_is_father($language_id) { $table = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = 'SELECT count(*) AS count FROM '.$table.' WHERE parent_id= '.intval($language_id).' AND NOT ISNULL(parent_id);'; $rs = Database::query($sql); if (Database::num_rows($rs) > 0 && 1 == Database::result($rs, '0', 'count')) { return true; } else { return false; } } /** * Make unavailable the language. * * @param int $language_id The language id * * @return bool */ public static function make_unavailable_language($language_id) { $tbl_admin_languages = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = "UPDATE $tbl_admin_languages SET available='0' WHERE id = ".intval($language_id).""; $result = Database::query($sql); return false !== $result; //only return false on sql error } /** * Make available the language. * * @param int $language_id language id * * @return bool */ public static function make_available_language($language_id) { $tbl_admin_languages = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = "UPDATE $tbl_admin_languages SET available='1' WHERE id = ".intval($language_id).""; $result = Database::query($sql); return false !== $result; //only return false on sql error } /** * Set platform language. * * @param int $language_id The language id * * @return bool */ public static function set_platform_language($language_id) { if (empty($language_id) || (intval($language_id) != $language_id)) { return false; } $language_id = intval($language_id); $tbl_admin_languages = Database::get_main_table(TABLE_MAIN_LANGUAGE); $tbl_settings_current = Database::get_main_table(TABLE_MAIN_SETTINGS); $sql = "SELECT * FROM $tbl_admin_languages WHERE id = $language_id"; $result = Database::query($sql); $lang = Database::fetch_array($result); $sql_update_2 = "UPDATE $tbl_settings_current SET selected_value = '".$lang['isocode']."' WHERE variable = 'platform_language'"; $result_2 = Database::query($sql_update_2); Event::addEvent( LOG_PLATFORM_LANGUAGE_CHANGE, LOG_PLATFORM_LANGUAGE, $lang['english_name'] ); return false !== $result_2; } /** * Get platform language ID. * * @return int The platform language ID */ public static function get_platform_language_id() { $name = api_get_setting('platformLanguage'); $tbl_admin_languages = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = "SELECT id FROM $tbl_admin_languages WHERE english_name ='$name'"; $res = Database::query($sql); if (Database::num_rows($res) < 1) { return false; } $row = Database::fetch_array($res); return (int) $row['id']; } /** * Get parent language path (or null if no parent). * * @deprecated * * @param string $language_path Children language path * * @return string Parent language path or null */ public static function get_parent_language_path($language_path) { $tbl_admin_languages = Database::get_main_table(TABLE_MAIN_LANGUAGE); $sql = "SELECT dokeos_folder FROM ".$tbl_admin_languages." WHERE id = ( SELECT parent_id FROM ".$tbl_admin_languages." WHERE dokeos_folder = '".Database::escape_string($language_path)."' ) "; $result = Database::query($sql); if (0 == Database::num_rows($result)) { return null; } $row = Database::fetch_array($result); return $row['dokeos_folder']; } /** * Get language matching isocode. * * @param string $isocode The language isocode (en, es, fr, zh-TW, etc) * * @return mixed English name of the matching language, or false if no active language could be found */ public static function getLanguageFromIsocode($isocode) { $isocode = Database::escape_string($isocode); $adminLanguagesTable = Database::get_main_table(TABLE_MAIN_LANGUAGE); // select language - if case several languages match, get the last (more recent) one $sql = "SELECT english_name FROM ".$adminLanguagesTable." WHERE isocode ='$isocode' AND available = 1 ORDER BY id DESC LIMIT 1"; $res = Database::query($sql); if (Database::num_rows($res) < 1) { return false; } $row = Database::fetch_assoc($res); return $row['english_name']; } /** * Get best language in browser preferences. * * @param string $preferences The browser-configured language preferences (e.g. "en,es;q=0.7;en-us;q=0.3", etc) * * @return mixed English name of the matching language, or false if no active language could be found */ public static function getLanguageFromBrowserPreference($preferences) { if (empty($preferences)) { return false; } $preferencesArray = explode(',', $preferences); if (count($preferencesArray) > 0) { foreach ($preferencesArray as $pref) { $s = strpos($pref, ';'); if ($s >= 2) { $code = substr($pref, 0, $s); } else { $code = $pref; } $name = self::getLanguageFromIsocode($code); if (false !== $name) { return $name; } } } return false; } /** * Convert a string to a valid PHP camelCase variable name. * * @param string $string * @return string */ public static function stringToCamelCaseVariableName($string) { $varName = preg_replace('/[^a-z0-9_]/i', '_', $string); // Replace invalid characters with '_' $varName = trim($varName, '_'); // Trim any '_' from the beginning and end $varName = ucwords(str_replace('_', ' ', $varName)); // Convert to camel case $varName = lcfirst(str_replace(' ', '', $varName)); // Remove spaces and convert the first character to lowercase return substr($varName, 0, 25); // Limit to 15 characters } /** * Retrieve the iso_code for a given language ID and its parent. * * @param int $languageId * @return array [childIsoCode, parentIsoCode] */ public static function getIsoCodes($languageId) { $em = Database::getManager(); $language = $em->getRepository('Chamilo\CoreBundle\Entity\Language')->find($languageId); if (!$language) { return [null, null]; } $childIsoCode = $language->getIsoCode(); $parentIsoCode = null; if ($language->getParent()) { $parentLanguage = $em->getRepository('Chamilo\CoreBundle\Entity\Language')->find($language->getParent()); if ($parentLanguage) { $parentIsoCode = $parentLanguage->getIsoCode(); } } return [$childIsoCode, $parentIsoCode]; } /** * Search for translations based on a term and language ID. * * @param string $term The term to search for. * @param int $languageId The ID of the language to search in. * * @return array An array of matched translations. */ public static function searchTranslations($term, $languageId): array { // Retrieve the ISO codes for the provided language ID. list($childIsoCode, $parentIsoCode) = self::getIsoCodes($languageId); // Define the files to search in based on the ISO codes. $files = ['en' => 'messages.en.po', $parentIsoCode => "messages.$parentIsoCode.po", $childIsoCode => "messages.$childIsoCode.po"]; $results = []; // Step 1: Search for all matches in messages.en.po. $matchedMsgids = self::searchMsgidInFile($term, $files['en']); // Step 2: For each matched msgid, search for its translation in the other files. foreach ($matchedMsgids as $msgid) { $entry = [ 'file' => $files['en'], 'variable' => $msgid, 'phpVarName' => self::stringToCamelCaseVariableName($msgid), 'en' => self::getTranslationForVariable($msgid, $files['en']) ]; $entry[$parentIsoCode] = self::getTranslationForVariable($msgid, $files[$parentIsoCode]); $entry[$childIsoCode] = self::getTranslationForVariable($msgid, $files[$childIsoCode], true); $results[] = $entry; } return $results; } /** * Search for a specific term inside a given .po file and return the msgids that match. * * @param string $term The term to search for. * @param string $filename The name of the .po file to search in. * * @return array An array of msgids that match the given term. */ private static function searchMsgidInFile($term, $filename) { $poFilePath = api_get_path(SYS_PATH) . self::LANGUAGE_TRANS_PATH . $filename; $matchedMsgids = []; if (file_exists($poFilePath)) { $lines = file($poFilePath, FILE_IGNORE_NEW_LINES); $currentVariable = null; foreach ($lines as $line) { if (strpos($line, 'msgid "') === 0) { $currentVariable = str_replace('msgid "', '', $line); $currentVariable = rtrim($currentVariable, '"'); if (stripos($currentVariable, $term) !== false) { $matchedMsgids[] = $currentVariable; } } } } return $matchedMsgids; } /** * Retrieve the translation (msgstr) for a given variable (msgid) from a specified .po file. * * @param string $variable The variable (msgid) to search for. * @param string $filename The name of the .po file to retrieve the translation from. * * @return string The translation (msgstr) for the provided variable, or an empty string if not found. */ private static function getTranslationForVariable(string $variable, string $filename, $checkSubLanguagePath = false): string { $poFilePath = self::getPoFilePath($filename, $checkSubLanguagePath); if (!file_exists($poFilePath)) { $shortLanguageCode = self::getShortLanguageCode($filename); $poFilePath = self::getPoFilePath($shortLanguageCode . '.po', $checkSubLanguagePath); if (!file_exists($poFilePath)) { return ''; } } $content = file_get_contents($poFilePath); $pattern = '/msgid "' . preg_quote($variable, '/') . '"\nmsgstr "(.*?)"/'; if (preg_match($pattern, $content, $match)) { return $match[1]; } return ''; } private static function getPoFilePath(string $filename, bool $checkSubLanguagePath): string { $path = $checkSubLanguagePath ? self::SUBLANGUAGE_TRANS_PATH : self::LANGUAGE_TRANS_PATH; return api_get_path(SYS_PATH) . $path . $filename; } private static function getShortLanguageCode(string $filename): string { $parts = explode('.', $filename); $languageCodeParts = explode('_', $parts[1]); return $parts[0].'.'.$languageCodeParts[0]; } /** * Updates or adds a msgid in the specified .po file. * * @param string $filename Name of the .po file * @param string $msgid Message identifier to search or add * @param string $content Associated message content * * @return array Returns true if the operation was successful, otherwise returns false */ public static function updateOrAddMsgid($filename, $msgid, $content): array { $filePath = api_get_path(SYS_PATH) . self::SUBLANGUAGE_TRANS_PATH . $filename; if (!file_exists($filePath)) { return ['success' => false, 'error' => 'File does not exist']; } if (!is_writable($filePath)) { try { if (!chmod($filePath, 0664)) { return ['success' => false, 'error' => 'Unable to set the file to writable']; } } catch (Exception $e) { return ['success' => false, 'error' => 'Failed to change file permissions: ' . $e->getMessage()]; } } $fileContents = file_get_contents($filePath); if ($fileContents === false) { return ['success' => false, 'error' => 'Failed to read file contents']; } $pattern = '/msgid "' . preg_quote($msgid, '/') . '"' . PHP_EOL . 'msgstr "(.*?)"/'; if (preg_match($pattern, $fileContents)) { $replacement = 'msgid "' . $msgid . '"' . PHP_EOL . 'msgstr "' . $content . '"'; $fileContents = preg_replace($pattern, $replacement, $fileContents); } else { $appendString = PHP_EOL . PHP_EOL . 'msgid "' . $msgid . '"' . PHP_EOL . 'msgstr "' . $content . '"'; $fileContents .= $appendString; } if (file_put_contents($filePath, $fileContents) === false) { return ['success' => false, 'error' => 'Failed to write to file']; } return ['success' => true]; } /** * Delete sub-language. * In order to avoid deletion of main languages, we check the existence of a parent. */ public static function removeSubLanguage(int $parentId, int $subLanguageId): bool { $entityManager = Database::getManager(); $subLanguage = $entityManager->getRepository(Language::class)->find($subLanguageId); $parentLanguage = $subLanguage ? $subLanguage->getParent() : null; if (!$subLanguage || !$parentLanguage || $parentLanguage->getId() != $parentId) { return false; } // Locate and delete the .po file of the sub-language $subLanguageIsoCode = $subLanguage->getIsocode(); $poFilePath = api_get_path(SYS_PATH) . self::SUBLANGUAGE_TRANS_PATH . "messages.$subLanguageIsoCode.po"; if (file_exists($poFilePath)) { unlink($poFilePath); } $entityManager->remove($subLanguage); $entityManager->flush(); return true; } /** * Add a sub-language. */ public static function addSubLanguage(string $originalName, string $englishName, bool $isAvailable, int $parentId, string $isoCode): bool|int { $entityManager = Database::getManager(); $parentLanguage = $entityManager->getRepository(Language::class)->find($parentId); if (!$parentLanguage) { return false; } $subLanguage = new Language(); $subLanguage->setOriginalName($originalName) ->setEnglishName($englishName) ->setIsocode($isoCode) ->setAvailable($isAvailable) ->setParent($parentLanguage); try { $entityManager->persist($subLanguage); $entityManager->flush(); } catch (\Exception $e) { // Handle exception if needed return false; } return $subLanguage->getId(); } /** * Remove a .po file for a sub-language. * * @param string $isoCode The ISO code of the sub-language (e.g., 'es_CO') * * @return bool True on success, false on failure */ public static function removePoFileForSubLanguage(string $isoCode): bool { if (empty($isoCode)) { return false; } // Path for the .po file you want to remove $poFilePath = api_get_path(SYS_PATH) . self::SUBLANGUAGE_TRANS_PATH . "messages.$isoCode.po"; if (file_exists($poFilePath)) { return unlink($poFilePath); } // File does not exist, consider it a successful removal return true; } /** * Check if a language exists by its ID. */ public static function languageExistsById(int $languageId): bool { $entityManager = Database::getManager(); $language = $entityManager->getRepository(Language::class)->find($languageId); return $language !== null; } /** * Check if the given language is a parent of any sub-language. */ public static function isParentOfSubLanguage(int $parentId): bool { $entityManager = Database::getManager(); $languageRepository = $entityManager->getRepository(Language::class); $childrenCount = $languageRepository->count(['parent' => $parentId]); return $childrenCount > 0; } /** * Get all information of a sub-language. */ public static function getAllInformationOfSubLanguage(int $parentId, int $subLanguageId): array { $entityManager = Database::getManager(); $languageRepository = $entityManager->getRepository(Language::class); $subLanguage = $languageRepository->findOneBy([ 'parent' => $parentId, 'id' => $subLanguageId ]); return $subLanguage ? self::convertLanguageToArray($subLanguage) : []; } /** * Convert a Language entity to an array. */ private static function convertLanguageToArray(Language $language): array { return [ 'id' => $language->getId(), 'original_name' => $language->getOriginalName(), 'english_name' => $language->getEnglishName(), 'isocode' => $language->getIsocode(), 'available' => $language->getAvailable(), // Add other fields as needed ]; } /** * Check if a language exists. */ public static function checkIfLanguageExists(string $originalName, string $englishName, string $isoCode): array { $entityManager = Database::getManager(); $languageRepository = $entityManager->getRepository(Language::class); $messageInformation = [ 'original_name' => false, 'english_name' => false, 'isocode' => false, 'execute_add' => true ]; if ($languageRepository->count(['originalName' => $originalName]) > 0) { $messageInformation['original_name'] = true; $messageInformation['execute_add'] = false; } if ($languageRepository->count(['englishName' => $englishName]) > 0) { $messageInformation['english_name'] = true; $messageInformation['execute_add'] = false; } $isoList = api_get_platform_isocodes(); // Assuming this is an existing function if (!in_array($isoCode, array_values($isoList))) { $messageInformation['isocode'] = true; $messageInformation['execute_add'] = false; } return $messageInformation; } /** * Gets the ISO code of the parent language for a given language. */ public static function getParentLocale(string $childIsoCode): ?string { $em = Database::getManager(); $languageRepository = $em->getRepository('Chamilo\CoreBundle\Entity\Language'); // Find the language by its ISO code $language = $languageRepository->findOneBy(['isocode' => $childIsoCode]); if (!$language) { return null; // Language not found } // Get the parent language if it exists $parentLanguage = $language->getParent(); if ($parentLanguage) { return $parentLanguage->getIsocode(); } return null; // No parent language } public static function generateSublanguageCode(string $parentCode, string $variant, int $maxLength = 10): string { $parentCode = strtolower(trim($parentCode)); $variant = strtolower(trim($variant)); // Generate a variant code by truncating the variant name $variantCode = substr($variant, 0, $maxLength - strlen($parentCode) - 1); // Build the complete code $fullCode = substr($parentCode . '_' . $variantCode, 0, $maxLength); return $fullCode; } }