diff --git a/composer.json b/composer.json index 4ca2d1092b73f04f4311d985ebd36bcd3608783c..8c300f4ac3178a468d446810813036cea791a1e1 100644 --- a/composer.json +++ b/composer.json @@ -31,7 +31,6 @@ "robrichards/xmlseclibs": "~2.0", "whitehat101/apr1-md5": "~1.0", "twig/twig": "~1.0", - "twig/extensions": "^1.3", "gettext/gettext": "^3.5" }, "require-dev": { diff --git a/lib/SimpleSAML/Locale/Localization.php b/lib/SimpleSAML/Locale/Localization.php index 7fbef2b3af7d8e2cba0e8ec68ec876ffbca85474..03a7576bb89b7e3c09eacf9923f905b2ed5fdc00 100644 --- a/lib/SimpleSAML/Locale/Localization.php +++ b/lib/SimpleSAML/Locale/Localization.php @@ -9,6 +9,9 @@ namespace SimpleSAML\Locale; +use Gettext\Translations; +use Gettext\Translator; + class Localization { @@ -22,13 +25,24 @@ class Localization /** * The default gettext domain. */ - private $domain = 'ssp'; + const DEFAULT_DOMAIN = 'ssp'; + + /** + * Default 1i18n backend + */ + const DEFAULT_I18NBACKEND = 'twig.gettextgettext'; /* - * The locale directory + * The default locale directory */ private $localeDir; + /* + * Where specific domains are stored + */ + private $localeDomainMap = array(); + + /** * Constructor * @@ -39,40 +53,95 @@ class Localization $this->configuration = $configuration; $this->localeDir = $this->configuration->resolvePath('locales'); $this->language = new Language($configuration); + $this->langcode = $this->language->getPosixLanguage($this->language->getLanguage()); $this->i18nBackend = $this->configuration->getString('language.i18n.backend', null); $this->setupL10N(); } + /* + * Add a new translation domain + * (We're assuming that each domain only exists in one place) + * + * @param string $localeDir Location of translations + * @param string $domain Domain at location + */ + private function addDomain($localeDir, $domain) + { + $this->localeDomainMap[$domain] = $localeDir; + } + + + /** + * Load translation domain from Gettext/Gettext using .po + * + * @param string $domain Name of domain + */ + private function loadGettextGettextFromPO($domain = self::DEFAULT_DOMAIN) { + $langcode = explode('_', $this->langcode)[0]; + $localeDir = $this->localeDomainMap[$domain]; + $poPath = $localeDir.'/'.$langcode.'/LC_MESSAGES/'.$domain.'.po'; + $translations = Translations::fromPoFile($poPath); + $t = new Translator(); + $t->loadTranslations($translations); + $t->register(); + } + + + /** + * Test to check if backend is set to default + * + * (if false: backend unset/there's an error) + */ + public function isI18NBackendDefault() + { + if ($this->i18nBackend === $this::DEFAULT_I18NBACKEND) { + return true; + } + return false; + } + + + /** + * Set up L18N if configured or fallback to old system + */ private function setupL10N() { // use old system - if (is_null($this->i18nBackend)) { + if (! $this->isI18NBackendDefault()) { + \SimpleSAML\Logger::debug("Localization: using old system"); return; } - $encoding = "UTF-8"; - $langcode = $this->language->getPosixLanguage($this->language->getLanguage()); - // use gettext and Twig.I18n - if ($this->i18nBackend == 'twig.i18n') { - putenv('LC_ALL='.$langcode); - setlocale(LC_ALL, $langcode); - bindtextdomain($this->domain, $this->localeDir); - bind_textdomain_codeset($this->domain, $encoding); - } + // setup default domain + $this->addDomain($this->localeDir, self::DEFAULT_DOMAIN); + $this->activateDomain(self::DEFAULT_DOMAIN); } + /** + * Set which translation domain to use + * + * @param string $domain Name of domain + */ public function activateDomain($domain) { - if ($this->i18nBackend == 'twig.i18n') { - textdomain($domain); - } + \SimpleSAML\Logger::debug("Localization: activate domain"); + $this->loadGettextGettextFromPO($domain); + $this->currentDomain = $domain; } + /** + * Get current translation domain + */ + public function getCurrentDomain() + { + return $this->currentDomain ? $this->currentDomain : self::DEFAULT_DOMAIN; + } + /** + * Go back to default translation domain + */ public function restoreDefaultDomain() { - if ($this->i18nBackend == 'twig.i18n') { - textdomain($this->domain); - } + $this->loadGettextGettextFromPO(self::DEFAULT_DOMAIN); + $this->currentDomain = self::DEFAULT_DOMAIN; } } - diff --git a/lib/SimpleSAML/XHTML/Template.php b/lib/SimpleSAML/XHTML/Template.php index 1bad9c98d65d8c3774678713d25241ddbd7a2d08..f8ad7e968d4ba26adcc163c487bee2cd47f13361 100644 --- a/lib/SimpleSAML/XHTML/Template.php +++ b/lib/SimpleSAML/XHTML/Template.php @@ -7,6 +7,8 @@ * @author Andreas Ă…kre Solberg, UNINETT AS. <andreas.solberg@uninett.no> * @package SimpleSAMLphp */ + + class SimpleSAML_XHTML_Template { @@ -154,9 +156,15 @@ class SimpleSAML_XHTML_Template } $twig = new \Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => $auto_reload)); - if ($this->localization->i18nBackend == 'twig.i18n') { - $this->localization->activateDomain('ssp'); - $twig->addExtension(new \Twig_Extensions_Extension_I18n()); + // set up translation + if ($this->localization->i18nBackend == 'twig.gettextgettext') { + /* if something like pull request #166 is ever merged with + * twig.extensions.i18n, use the line below: + * $twig->addExtension(new \Twig_Extensions_Extension_I18n('__', 'n__')); + * instead of the two lines after this comment + */ + $twig->addFilter(new Twig_SimpleFilter('trans', '__')); + $twig->addTokenParser(new \SimpleSAML_Twig_TokenParser_Trans()); } return $twig; } @@ -264,6 +272,7 @@ class SimpleSAML_XHTML_Template */ private function twigDefaultContext() { + $this->data['localeBackend'] = $this->configuration->getString('language.i18n.backend', 'ssp'); $this->data['currentLanguage'] = $this->translator->getLanguage()->getLanguage(); // show language bar by default if (!isset($this->data['hideLanguageBar'])) { diff --git a/templates/sandbox.twig b/templates/sandbox.twig index 97f0b64fdd52646ab138b18212d713a7f25165f5..3e1b94627f3076a27442e2af94c56923ed8a0a92 100644 --- a/templates/sandbox.twig +++ b/templates/sandbox.twig @@ -2,7 +2,10 @@ {% block content %} <p>This page exists as a sandbox to play with twig without affecting anything else. The template is in ./templates.</p> <p>{{ sometext }}</p> + <h2>And now for some localization</h2> + <p>Locale backend in use: {{ localeBackend }}</p> <p>Original: Hello, Untranslated World!</p> <p>Translated: {% trans 'Hello, Untranslated World!' %}</p> + <p>Filtertrans-test: {{ 'Hello, Untranslated World!'|trans }}</p> <p>Current locale set: {{ currentLanguage }}</p> {% endblock content %}