388 lines
14 KiB
PHP
388 lines
14 KiB
PHP
<?php
|
|
|
|
/**
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; under version 2
|
|
* of the License (non-upgradable).
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*
|
|
* Copyright (c) 2002-2008 (original work) Public Research Centre Henri Tudor & University of Luxembourg (under the project TAO & TAO2);
|
|
* 2008-2010 (update and modification) Deutsche Institut für Internationale Pädagogische Forschung (under the project TAO-TRANSFER);
|
|
* 2009-2012 (update and modification) Public Research Centre Henri Tudor (under the project TAO-SUSTAIN & TAO-DEV);
|
|
*
|
|
*/
|
|
|
|
use oat\generis\model\OntologyRdf;
|
|
use oat\tao\helpers\translation\TranslationBundle;
|
|
use oat\generis\model\data\ModelManager;
|
|
use oat\tao\helpers\translation\rdf\RdfPack;
|
|
use oat\generis\model\kernel\persistence\file\FileIterator;
|
|
|
|
/**
|
|
* Short description of class tao_models_classes_LanguageService
|
|
*
|
|
* @access public
|
|
* @author Joel Bout, <joel.bout@tudor.lu>
|
|
* @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, <joel.bout@tudor.lu>
|
|
* @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, <joel.bout@tudor.lu>
|
|
* @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, <joel.bout@tudor.lu>
|
|
* @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, <joel.bout@tudor.lu>
|
|
* @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, <joel.bout@tudor.lu>
|
|
* @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;
|
|
}
|
|
}
|