diff --git a/module/VuFind/src/VuFind/Bootstrapper.php b/module/VuFind/src/VuFind/Bootstrapper.php index baf2aa69a544731d440bf2bc4a4017c138b4b8c4..261c1ef3596e0ac17455fbb9e9d74fc283a7158a 100644 --- a/module/VuFind/src/VuFind/Bootstrapper.php +++ b/module/VuFind/src/VuFind/Bootstrapper.php @@ -289,6 +289,30 @@ class Bootstrapper return false; } + /** + * Support method for initLanguage() -- look up all text domains. + * + * @return array + */ + protected function getTextDomains() + { + $base = APPLICATION_PATH; + $local = LOCAL_OVERRIDE_DIR; + $languagePathParts = ["$base/languages"]; + if (!empty($local)) { + $languagePathParts[] = "$local/languages"; + } + $languagePathParts[] = "$base/themes/*/languages"; + + $domains = []; + foreach ($languagePathParts as $current) { + $places = glob($current . '/*', GLOB_ONLYDIR | GLOB_NOSORT); + $domains = array_merge($domains, array_map('basename', $places)); + } + + return array_unique($domains); + } + /** * Set up language handling. * @@ -325,11 +349,18 @@ class Bootstrapper if (!in_array($language, array_keys($config->Languages->toArray()))) { $language = $config->Site->language; } - try { - $sm->get('VuFind\Translator') - ->addTranslationFile('ExtendedIni', null, 'default', $language) - ->setLocale($language); + $translator = $sm->get('VuFind\Translator'); + $translator->setLocale($language) + ->addTranslationFile('ExtendedIni', null, 'default', $language); + foreach ($this->getTextDomains() as $domain) { + // Set up text domains using the domain name as the filename; + // this will help the ExtendedIni loader dynamically locate + // the appropriate files. + $translator->addTranslationFile( + 'ExtendedIni', $domain, $domain, $language + ); + } } catch (\Zend\Mvc\Exception\BadMethodCallException $e) { if (!extension_loaded('intl')) { throw new \Exception( diff --git a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php index 83a6454b89c11c8f1cc7a19b9be4845b7ae7d79c..629b5100f7fedd07a34098d928cc70f977ec090d 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php +++ b/module/VuFind/src/VuFind/I18n/Translator/Loader/ExtendedIni.php @@ -107,12 +107,11 @@ class ExtendedIni implements FileLoaderInterface * Load method defined by FileLoaderInterface. * * @param string $locale Locale to read from language file - * @param string $filename Language file to read (not used) + * @param string $filename Relative base path for language file (used for + * loading text domains; optional) * * @return TextDomain * @throws InvalidArgumentException - * - * @SuppressWarnings(PHPMD.UnusedFormalParameter) */ public function load($locale, $filename) { @@ -120,12 +119,12 @@ class ExtendedIni implements FileLoaderInterface $this->resetLoadedFiles(); // Load base data: - $data = $this->loadLanguageFile($locale . '.ini'); + $data = $this->loadLanguageLocale($locale, $filename); // Load fallback data, if any: if (!empty($this->fallbackLocales)) { foreach ($this->fallbackLocales as $fallbackLocale) { - $newData = $this->loadLanguageFile($fallbackLocale . '.ini'); + $newData = $this->loadLanguageLocale($fallbackLocale, $filename); $newData->merge($data); $data = $newData; } @@ -134,6 +133,21 @@ class ExtendedIni implements FileLoaderInterface return $data; } + /** + * Get the language file name for a language and domain + * + * @param string $locale Locale name + * @param string $domain Text domain (if any) + * + * @return string + */ + public function getLanguageFilename($locale, $domain) + { + return empty($domain) + ? $locale . '.ini' + : $domain . '/' . $locale . '.ini'; + } + /** * Reset the loaded file list. * @@ -160,14 +174,33 @@ class ExtendedIni implements FileLoaderInterface return false; } + /** + * Load the language file for a given locale and domain. + * + * @param string $locale Locale name + * @param string $domain Text domain (if any) + * + * @return TextDomain + */ + protected function loadLanguageLocale($locale, $domain) + { + $filename = $this->getLanguageFilename($locale, $domain); + // Load the language file, and throw a fatal exception if it's missing + // and we're not dealing with text domains. A missing base file is an + // unexpected, fatal error; a missing domain-specific file is more likely + // due to the possibility of incomplete translations. + return $this->loadLanguageFile($filename, empty($domain)); + } + /** * Search the path stack for language files and merge them together. * - * @param string $filename Name of file to search path stack for. + * @param string $filename Name of file to search path stack for. + * @param bool $failOnError If true, throw an exception when file not found. * * @return TextDomain */ - protected function loadLanguageFile($filename) + protected function loadLanguageFile($filename, $failOnError = true) { // Don't load a file that has already been loaded: if ($this->checkAndMarkLoadedFile($filename)) { @@ -189,7 +222,13 @@ class ExtendedIni implements FileLoaderInterface } } if ($data === false) { - throw new InvalidArgumentException("Ini file '{$filename}' not found"); + // Should we throw an exception? If not, return an empty result: + if ($failOnError) { + throw new InvalidArgumentException( + "Ini file '{$filename}' not found" + ); + } + return new TextDomain(); } return $data; diff --git a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php index ee06acb90b4871068400f9ca95844a74b44b90fb..723560f53d2fcab6985613673eb9d4eb4338398a 100644 --- a/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php +++ b/module/VuFind/src/VuFind/I18n/Translator/TranslatorAwareTrait.php @@ -125,8 +125,11 @@ trait TranslatorAwareTrait */ protected function translateString($str, $tokens = [], $default = null) { - $msg = null === $this->translator - ? $str : $this->translator->translate($str); + // Figure out the text domain for the string: + list($domain, $str) = $this->extractTextDomain($str); + + $msg = (null === $this->translator) + ? $str : $this->translator->translate($str, $domain); // Did the translation fail to change anything? If so, use default: if (null !== $default && $msg == $str) { @@ -145,4 +148,21 @@ trait TranslatorAwareTrait return $msg; } + + /** + * Given a translation string with or without a text domain, return an + * array with the raw string and the text domain separated. + * + * @param string $str String to parse + * + * @return array + */ + protected function extractTextDomain($str) + { + $parts = explode('::', $str); + if (count($parts) == 2) { + return $parts; + } + return ['default', $str]; + } }