* @package tao */ class tao_models_classes_LanguageService extends tao_models_classes_GenerisService { // --- ASSOCIATIONS --- const TRANSLATION_PREFIX = __CLASS__ . ':all'; // --- ATTRIBUTES --- const CLASS_URI_LANGUAGES = 'http://www.tao.lu/Ontologies/TAO.rdf#Languages'; const CLASS_URI_LANGUAGES_USAGES = 'http://www.tao.lu/Ontologies/TAO.rdf#LanguagesUsages'; const PROPERTY_LANGUAGE_USAGES = 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageUsages'; const PROPERTY_LANGUAGE_ORIENTATION = 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageOrientation'; const INSTANCE_LANGUAGE_USAGE_GUI = 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageUsageGUI'; const INSTANCE_LANGUAGE_USAGE_DATA = 'http://www.tao.lu/Ontologies/TAO.rdf#LanguageUsageData'; const INSTANCE_ORIENTATION_LTR = 'http://www.tao.lu/Ontologies/TAO.rdf#OrientationLeftToRight'; const INSTANCE_ORIENTATION_RTL = 'http://www.tao.lu/Ontologies/TAO.rdf#OrientationRightToLeft'; // --- OPERATIONS --- /** * Short description of method createLanguage * * @access public * @author Joel Bout, * @param string $code * @return core_kernel_classes_Resource * @throws common_exception_Error Not implemented in this class yet. */ public function createLanguage($code) { throw new common_exception_Error(__METHOD__ . ' not yet implemented in ' . __CLASS__); } /** * Short description of method getLanguageByCode * * @access public * @author Joel Bout, * @param string $code * @return core_kernel_classes_Resource|null */ public function getLanguageByCode($code) { $returnValue = null; $langClass = new core_kernel_classes_Class(static::CLASS_URI_LANGUAGES); $langs = $langClass->searchInstances([ OntologyRdf::RDF_VALUE => $code ], [ 'like' => false ]); if (count($langs) == 1) { $returnValue = current($langs); } else { common_Logger::w('Could not find language with code ' . $code); } return $returnValue; } /** * Short description of method getCode * * @access public * @author Joel Bout, * @param core_kernel_classes_Resource $language * @return string */ public function getCode(core_kernel_classes_Resource $language) { $returnValue = (string) ''; $valueProperty = new core_kernel_classes_Property(OntologyRdf::RDF_VALUE); $returnValue = $language->getUniquePropertyValue($valueProperty); return (string) $returnValue; } /** * Short description of method getAvailableLanguagesByUsage * * @access public * @author Joel Bout, * @param core_kernel_classes_Resource $usage * @return array */ public function getAvailableLanguagesByUsage(core_kernel_classes_Resource $usage) { $returnValue = []; $langClass = new core_kernel_classes_Class(static::CLASS_URI_LANGUAGES); $returnValue = $langClass->searchInstances([ static::PROPERTY_LANGUAGE_USAGES => $usage->getUri() ], [ 'like' => false ]); return (array) $returnValue; } /** * Checks the language availability in the given context(usage). * * @param string $code The language code to check. (for example: en-US) * @param core_kernel_classes_Resource $usage The context of the availability. * * @return bool */ public function isLanguageAvailable($code, core_kernel_classes_Resource $usage) { $langClass = new core_kernel_classes_Class(static::CLASS_URI_LANGUAGES); $result = $langClass->searchInstances( [ OntologyRdf::RDF_VALUE => $code, static::PROPERTY_LANGUAGE_USAGES => $usage->getUri(), ], ['like' => false] ); return !empty($result); } public function addTranslationsForLanguage(core_kernel_classes_Resource $language) { $langCode = $this->getCode($language); $rdf = ModelManager::getModel()->getRdfInterface(); $extensions = common_ext_ExtensionsManager::singleton()->getInstalledExtensions(); foreach ($extensions as $extension) { $pack = new RdfPack($langCode, $extension); foreach ($pack as $triple) { $rdf->add($triple); } } } /** * Regenerates client and server translation * @return string[] list of client files regenerated */ public function generateAll($checkPreviousBundle = false) { $this->generateServerBundles(); $files = $this->generateClientBundles($checkPreviousBundle); return $files; } /** * * @author Lionel Lecaque, lionel@taotesting.com * * @param bool $checkPreviousBundle * * @return array */ public function generateClientBundles($checkPreviousBundle = false) { $returnValue = []; $extensions = array_map( function ($extension) { return $extension->getId(); }, common_ext_ExtensionsManager::singleton()->getInstalledExtensions() ); // lookup for languages into tao $languages = tao_helpers_translation_Utils::getAvailableLanguages(); $path = ROOT_PATH . 'tao/views/locales/'; $generated = 0; $generate = true; foreach ($languages as $langCode) { try { $bundle = new TranslationBundle($langCode, $extensions, ROOT_PATH, TAO_VERSION); if ($checkPreviousBundle) { $currentBundle = $path . $langCode . '.json'; if (file_exists($currentBundle)) { $bundleData = json_decode(file_get_contents($currentBundle), true); if ($bundleData['serial'] === $bundle->getSerial()) { $generate = false; } } if ($generate) { $file = $bundle->generateTo($path, false); } } else { $file = $bundle->generateTo($path); } if ($file) { $generated++; $returnValue[] = $file; } else { if ($generate) { common_Logger::e('Failure generating message.js for lang ' . $langCode); } else { common_Logger::d('Actual File is more recent, skip ' . $langCode); } } } catch (common_exception_Error $e) { common_Logger::e('Failure: ' . $e->getMessage()); } } common_Logger::i($generated . ' translation bundles have been (re)generated'); return $returnValue; } /** * Generate server translation file, forching a cache overwrite */ public function generateServerBundles() { $usage = $this->getResource(self::INSTANCE_LANGUAGE_USAGE_GUI); foreach ($this->getAvailableLanguagesByUsage($usage) as $language) { $langCode = $this->getCode($language); $this->buildServerBundle($langCode); } } /** * Returns the translation strings for a given language * Conflicting translations get resolved by order of dependencies * @param string $langCode * @return array translation strings */ public function getServerBundle($langCode) { $cache = $this->getServiceLocator()->get(common_cache_Cache::SERVICE_ID); try { $translations = $cache->get(self::TRANSLATION_PREFIX . $langCode); } catch (common_cache_NotFoundException $ex) { $translations = $this->buildServerBundle($langCode); } return $translations; } /** * Rebuild the translation cache from the POs situated in each installed extension * @param string $langCode * @return array translation */ protected function buildServerBundle($langCode) { $extensions = common_ext_ExtensionsManager::singleton()->getInstalledExtensions(); $extensions = helpers_ExtensionHelper::sortByDependencies($extensions); $translations = []; foreach ($extensions as $extension) { $file = $extension->getDir() . 'locales' . DIRECTORY_SEPARATOR . $langCode . DIRECTORY_SEPARATOR . 'messages.po'; $new = l10n::getPoFile($file); if (is_array($new)) { $translations = array_merge($translations, $new); } } $cache = $this->getServiceLocator()->get(common_cache_Cache::SERVICE_ID); $cache->put($translations, self::TRANSLATION_PREFIX . $langCode); return $translations; } /** * Short description of method getDefaultLanguageByUsage * * @access public * @author Joel Bout, * @param core_kernel_classes_Resource $usage * @return core_kernel_classes_Resource * @throws common_exception_Error Not implemented in this class yet. */ public function getDefaultLanguageByUsage(core_kernel_classes_Resource $usage) { throw new common_exception_Error(__METHOD__ . ' not yet implemented in ' . __CLASS__); } /** * Filter a value of a language to transform it into uri * * If it's an uri, returns it * If it's a language code returns the associated uri * Else returns the default language uri * * @param $value * @return string * @throws common_exception_Error */ public static function filterLanguage($value) { $uri = self::getExistingLanguageUri($value); /** @noinspection NullPointerExceptionInspection */ return $uri !== null ? $uri : self::singleton()->getLanguageByCode(DEFAULT_LANG)->getUri(); } /** * @param string $value language code or uri * @return string|null language uri if language found or null otherwise * @throws common_exception_Error */ public static function getExistingLanguageUri($value) { if (filter_var($value, FILTER_VALIDATE_URL) !== false) { $langByUri = new \core_kernel_classes_Resource($value); return $langByUri->exists() ? $value : null; } $langByCode = self::singleton()->getLanguageByCode($value); return $langByCode !== null ? $langByCode->getUri() : null; } /** * Convenience method that returns available language descriptions to be inserted in the * knowledge base. * * @return array of ns => files */ private function getLanguageFiles() { $extManager = $this->getServiceLocator()->get(common_ext_ExtensionsManager::SERVICE_ID); $localesPath = $extManager->getExtensionById('tao')->getDir().'locales'; if (!@is_dir($localesPath) || !@is_readable($localesPath)) { throw new tao_install_utils_Exception("Cannot read 'locales' directory in extenstion 'tao'."); } $files = []; $localeDirectories = scandir($localesPath); foreach ($localeDirectories as $localeDir) { $path = $localesPath . '/' . $localeDir; if ($localeDir[0] != '.' && @is_dir($path)) { // Look if the lang.rdf can be read. $languageModelFile = $path . '/lang.rdf'; if (@file_exists($languageModelFile) && @is_readable($languageModelFile)) { $files[] = $languageModelFile; } } } return $files; } /** * Return the definition of the languages as an RDF iterator * @return AppendIterator */ public function getLanguageDefinition() { $model = new AppendIterator(); foreach ($this->getLanguageFiles() as $rdfPath) { $iterator = new FileIterator($rdfPath); $model->append($iterator->getIterator()); } return $model; } }