public function register(Fol $app) { $app['middleware'] = function ($app) { $middleware = []; if ($app->has('users')) { $middleware[] = new Middlewares\DigestAuthentication($app['users']); } $middleware[] = new Middlewares\Expires(); $middleware[] = (new Middlewares\ErrorHandler())->catchExceptions()->statusCode(function ($code) { return $code > 400 && $code < 600; })->arguments($app); $middleware[] = new Middlewares\BasePath($app->getUrlPath()); $middleware[] = new Middlewares\TrailingSlash(); $middleware[] = new Middlewares\ContentType(); $middleware[] = new Middlewares\ContentLanguage(['en', 'gl', 'es']); $middleware[] = function ($request, $next) use($app) { $language = $request->getHeaderLine('Accept-Language'); $translator = new Translator(); $translator->loadTranslations(Translations::fromPoFile(dirname(dirname(__DIR__)) . '/locales/' . $language . '.po')); $prev = $translator->register(); $app['templates']->addData(['language' => $language]); $response = $next($request); if ($prev) { $prev->register(); } return $response; }; $middleware[] = (new Middlewares\MethodOverride())->parsedBodyParameter('method-override'); $middleware[] = (new Middlewares\Reader(dirname(dirname(__DIR__)) . '/assets'))->continueOnError(); $middleware[] = (new Middlewares\AuraRouter($app['router']))->arguments($app); return new Dispatcher($middleware); }; }
protected function execute(InputInterface $input, OutputInterface $output) { $this->writeSection($output, 'Po to Csv converter'); $this->cwd = getcwd() . DIRECTORY_SEPARATOR; $output->writeln('<info>Using CWD</info> ' . $this->cwd); $poFiles = $this->getInputPoFiles($input, $output); $outputFolder = $this->getOutputFolder($input, $output); $useDefaults = $input->getOption('use-defaults'); if ($useDefaults) { $output->writeln('<info>Po files</info>:'); $this->writeList($output, $poFiles); $output->writeln(['', '<info>Output folder</info>: ' . $outputFolder]); } $poHandles = []; foreach ($poFiles as $poFile) { $key = basename($poFile, '.po'); $output->writeln('<info>loading ' . $key . '</info>...'); $poHandles[$key] = Translations::fromPoFile($poFile); } $output->writeln('<info>merging po files</info>...'); $csvArray = []; foreach ($poHandles as $language => $poHandle) { foreach ($poHandle as $translation) { $original = trim($translation->original); $translation = trim($translation->translation); if (!isset($csvArray[$original])) { $csvArray[$original] = [$language => $translation]; } elseif (!isset($csvArray[$original][$language])) { $csvArray[$original][$language] = $translation; } elseif ($csvArray[$original][$language] != $translation) { $csvArray[$original][$language] = $this->handleConflict($input, $output, $original, $csvArray[$original][$language], $translation); } } } $output->writeln('<info>writing csv</info>...'); $writer = Writer::createFromFileObject(new SplTempFileObject()); $writer->setDelimiter(';'); $header = ['original']; $header = array_merge($header, array_keys($poHandles)); $writer->insertOne($header); foreach ($csvArray as $original => $item) { $row = []; foreach ($header as $column) { if ($column === 'original') { $row[] = $original; } else { $row[] = isset($item[$column]) ? $item[$column] : null; } } $writer->insertOne($row); } $outputFile = $outputFolder . DIRECTORY_SEPARATOR . 'translations.csv'; file_put_contents($outputFile, $writer->__toString()); $output->writeln('<info>done. output file is</info> ' . $outputFile); }
/** * main * */ public function main() { $default = APP . 'Locale' . DS . 'default.pot'; $response = $this->in("What is the full path you would like to merge file (created pot file)?\nExample:" . $default . "\n[Q]uit", null, $default); if (strtoupper($response) === 'Q') { $this->out('Merge Aborted'); $this->_stop(); } $created = new File($response, false, 0755); if (!$created->exists()) { $this->err('The file path you supplied was not found. Please try again.'); $this->_stop(); } $default = APP . 'Locale' . DS . 'ja' . DS . 'default.po'; $response = $this->in("What is the full path you would like to merge file (current po file)?\nExample: " . $default . "\n[Q]uit", null, $default); if (strtoupper($response) === 'Q') { $this->out('Merge Aborted'); $this->_stop(); } $current = new File($response, false, 0755); if (!$current->exists()) { $this->err('The file path you supplied was not found. Please try again.'); $this->_stop(); } $createdTranslations = Translations::fromPoFile($created->path); $createdTranslations->addFromPoFile($current->path); $merged = $createdTranslations->toPoString(); $this->createFile($current->path, $merged); }
/** * Load the i12n translation file. */ public function load_translation() { $translator = new \Gettext\Translator(); $i18n_path = realpath(__DIR__ . '/../../i18n/' . Config::$locale . '.po'); if (file_exists($i18n_path)) { $translations = \Gettext\Translations::fromPoFile($i18n_path); $translator->loadTranslations($translations); } Translator::initGettextFunctions($translator); }
protected function loadTranslations() { if (!file_exists($this->arguments['src-po'])) { $this->error('src-po file not found'); exit; } $this->translations = Translations::fromPoFile($this->arguments['src-po']); list($code) = explode('_', $this->translations->getLanguage()); $this->translateDirection = ($this->arguments['src-lang'] ?: 'en') . '-' . $code; }
/** * Updates the catalog file from the given po file * * @return void */ public function updateFromCatalog() { $domain = $this->params['domain']; #$localePaths = App::path('Locale'); $localePath = ROOT . '/src/Locale/'; $catalogFile = $localePath . $domain . '.pot'; if (!file_exists($catalogFile)) { return $this->abort(sprintf('Catalog File %s not found.', $catalogFile)); } $poFiles = []; $folder = new Folder($localePath); $tree = $folder->tree(); foreach ($tree[1] as $file) { $basename = basename($file); if ($domain . '.po' === $basename) { $poFiles[] = $file; } } if (empty($poFiles)) { return $this->abort('I could not find any matching po files in the given locale path (%s) for the domain (%s)', $localePath, $domain); } if (!$this->params['overwrite']) { $this->out('I will update the following .po files and their corresponding .mo file with the keys from catalog: ' . $catalogFile); foreach ($poFiles as $poFile) { $this->out(' - ' . $poFile); } $response = $this->in('Would you like to continue?', ['y', 'n'], 'n'); if ($response !== 'y') { return $this->abort('Aborted'); } } foreach ($poFiles as $poFile) { $catalogEntries = Translations::fromPoFile($catalogFile); $moFile = str_replace('.po', '.mo', $poFile); $translationEntries = Translations::fromPoFile($poFile); $newTranslationEntries = $catalogEntries->mergeWith($translationEntries, Merge::REFERENCES_THEIRS); $newTranslationEntries->deleteHeaders(); foreach ($translationEntries->getHeaders() as $key => $value) { $newTranslationEntries->setHeader($key, $value); } if ($this->params['strip-references']) { foreach ($newTranslationEntries as $translation) { $translation->deleteReferences(); } } $newTranslationEntries->toPoFile($poFile); $newTranslationEntries->toMoFile($moFile); $this->out('Updated ' . $poFile); $this->out('Updated ' . $moFile); } }
protected function execute(InputInterface $input, OutputInterface $output) { $container = $this->getContainer(); $filesystem = new Filesystem(); $locator = $container->get("agit.common.filecollector"); $bundles = $container->getParameter("kernel.bundles"); $catalogPath = $container->getParameter("kernel.root_dir") . "/{$this->catalogSubdir}"; $this->getContainer()->get("event_dispatcher")->dispatch("agit.intl.global.translations", new TranslationsEvent($this)); foreach ($container->getParameter("agit.intl.locales") as $locale) { $catalog = new Translations(); $messagesPath = "{$catalogPath}/{$locale}/LC_MESSAGES"; $catalogFile = "{$messagesPath}/agit.po"; $oldCatalog = $filesystem->exists($catalogFile) ? Translations::fromPoFile($catalogFile) : new Translations(); foreach ($oldCatalog as $translation) { $translation->deleteReferences(); } $catalog->mergeWith($oldCatalog, 0); foreach ($bundles as $alias => $namespace) { $bundlePath = $locator->resolve($alias); $bundleCatalogFile = "{$bundlePath}/{$this->catalogSubdir}/bundle.{$locale}.po"; $bundleCatalog = $filesystem->exists($bundleCatalogFile) ? Translations::fromPoFile($bundleCatalogFile) : new Translations(); $catalog->mergeWith($bundleCatalog, Merge::ADD); } if (isset($this->extraTranslations[$locale])) { foreach ($this->extraTranslations[$locale] as $translation) { $catalog[] = $translation; } } // NOTE: we delete all headers and only set language in order to avoid garbage commits $catalog->deleteHeaders(); $catalog->setLanguage($locale); $catalog->setHeader("Content-Type", "text/plain; charset=UTF-8"); $filesystem->dumpFile($catalogFile, $catalog->toPoString()); $filesystem->dumpFile("{$messagesPath}/agit.mo", $catalog->toMoString()); } }
public function generateTranslationFiles(Locale $locale, $activeTranslations = null) { $envPoFile = $locale->getPoFilePath(WP_ENV); $poFile = $locale->getPoFilePath(); $moFile = $locale->getMoFilePath(); if (is_null($activeTranslations)) { $activeTranslations = $locale->hasPoFile() ? Translations::fromPoFile($locale->getPoFilePath()) : new Translations(); } // Add local modifications to the default set should there // be any. if ($locale->hasPoFile(WP_ENV)) { $this->hardTranslationSetMerge($locale, Translations::fromPoFile($envPoFile), $activeTranslations); } $textDomain = Strata::i18n()->getTextdomain(); $activeTranslations->setDomain($textDomain); $activeTranslations->setHeader('Language', $locale->getCode()); $activeTranslations->setHeader('Text Domain', $textDomain); $activeTranslations->setHeader('X-Domain', $textDomain); @unlink($poFile); @unlink($moFile); $activeTranslations->toPoFile($poFile); $activeTranslations->toMoFile($moFile); }
/** * @param string $path */ public static function addTranslation($path) { $file = new File($path); if ($file->exists()) { $parts = explode('.', $path); $extension = $parts[count($parts) - 1]; switch ($extension) { case 'po': $translations = Translations::fromPoFile($path); break; case 'mo': $translations = Translations::fromMoFile($path); break; case 'php': $translations = Translations::fromPhpArrayFile($path); break; } self::$activeTranslation->mergeWith($translations); self::$t->loadTranslations(self::$activeTranslation); } }
public static function updateMO() { $path = base_path() . "/" . dirname(self::getFile(self::$locale)); $file = $path . '/' . self::$config['domain']; $translations = Translations::fromPoFile($file . '.po'); $translations->toMoFile($file . '.mo'); }
/** * Load translation domain from Gettext/Gettext using .po * * Note: Since Twig I18N does not support domains, all loaded files are * merged. Use contexts if identical strings need to be disambiguated. * * @param string $domain Name of domain * @param boolean $catchException Whether to catch an exception on error or return early * * @throws \Exception If something is wrong with the locale file for the domain and activated language */ private function loadGettextGettextFromPO($domain = self::DEFAULT_DOMAIN, $catchException = true) { try { $langPath = $this->getLangPath($domain); } catch (\Exception $e) { $error = "Something went wrong when trying to get path to language file, cannot load domain '{$domain}'."; \SimpleSAML\Logger::error($_SERVER['PHP_SELF'] . ' - ' . $error); if ($catchException) { // bail out! return; } else { throw $e; } } $poFile = $domain . '.po'; $poPath = $langPath . $poFile; if (file_exists($poPath) && is_readable($poPath)) { $translations = Translations::fromPoFile($poPath); $this->translator->loadTranslations($translations); } else { $error = "Localization file '{$poFile}' not found in '{$langPath}', falling back to default"; \SimpleSAML\Logger::error($_SERVER['PHP_SELF'] . ' - ' . $error); } }
public static function load() { $locale = self::$locale . '.UTF-8'; # IMPORTANT: locale must be installed in server! # sudo locale-gen es_ES.UTF-8 # sudo update-locale putenv('LANG=' . $locale); putenv('LANGUAGE=' . $locale); putenv('LC_MESSAGES=' . $locale); putenv('LC_PAPER=' . $locale); putenv('LC_TIME=' . $locale); putenv('LC_MONETARY=' . $locale); setlocale(LC_MESSAGES, $locale); setlocale(LC_COLLATE, $locale); setlocale(LC_TIME, $locale); setlocale(LC_MONETARY, $locale); bindtextdomain(self::$config['domain'], self::$config['storage']); bind_textdomain_codeset(self::$config['domain'], 'UTF-8'); textdomain(self::$config['domain']); # Also, we will work with gettext/gettext library # because PHP gones crazy when mo files are updated $path = dirname(self::getFile(self::$locale)); $file = $path . '/' . self::$config['domain']; if (is_file($file . '.php')) { $translations = $file . '.php'; } elseif (is_file($file . '.mo')) { $translations = Translations::fromMoFile($file . '.mo'); } elseif (is_file($file . '.po')) { $translations = Translations::fromPoFile($file . '.po'); } else { $translations = new Translations(); } Translator::initGettextFunctions((new Translator())->loadTranslations($translations)); }
<?php require '../vendor/autoload.php'; use Gettext\Translations; use Gettext\Translator; $locales = array('en', 'es', 'nb'); $text = "Hello, untranslated world!"; echo "<p>Original string: {$text}</p>"; foreach ($locales as $locale) { // START setup echo '<p>'; echo "Trying to set locale: {$locale}"; echo '<br>'; $translations = Translations::fromPoFile("../locale/{$locale}/LC_MESSAGES/test1.po"); echo '<br>'; $t = new Translator(); $t->loadTranslations($translations); Translator::initGettextFunctions($t); // END setup echo __($text); echo '<br>'; echo '</p>'; }
private static function loadFormatPo($file) { return is_file($file . '.po') ? Translations::fromPoFile($file . '.po') : null; }
protected function execute(InputInterface $input, OutputInterface $output) { $container = $this->getContainer(); $filesystem = new Filesystem(); $bundleAlias = $input->getArgument("bundle"); $bundlePath = $container->get("agit.common.filecollector")->resolve($bundleAlias); $defaultLocale = $container->get("agit.intl.locale")->getDefaultLocale(); $locales = $input->getArgument("locales") ? array_map("trim", explode(",", $input->getArgument("locales"))) : $container->getParameter("agit.intl.locales"); $globalCatalogPath = $container->getParameter("kernel.root_dir") . "/{$this->catalogSubdir}"; $this->cacheBasePath = sprintf("%s/agit.intl.temp/%s", sys_get_temp_dir(), $bundleAlias); $filesystem->mkdir($this->cacheBasePath); $finder = (new Finder())->in("{$bundlePath}")->name("*\\.php")->name("*\\.js")->notPath("/test.*/i")->notPath("public/js/ext"); $files = []; foreach ($finder as $file) { $filePath = $file->getRealpath(); $alias = str_replace($bundlePath, "@{$bundleAlias}/", $filePath); $files[$filePath] = $alias; } $this->getContainer()->get("event_dispatcher")->dispatch("agit.intl.bundle.files", new BundleTranslationFilesEvent($this, $bundleAlias, $this->cacheBasePath)); $this->extraTranslations = new Translations(); $this->getContainer()->get("event_dispatcher")->dispatch("agit.intl.bundle.translations", new BundleTranslationsEvent($this, $bundleAlias)); $files += $this->extraSourceFiles; $frontendFiles = array_filter(array_keys($files), function ($file) { return preg_match("|\\.js\$|", $file); }); $backendFiles = array_filter(array_keys($files), function ($file) { return !preg_match("|\\.js\$|", $file); }); $frontendCatalogs = ""; foreach ($locales as $locale) { if (!preg_match("|^[a-z]{2}_[A-Z]{2}|", $locale)) { throw new Exception("Invalid locale: {$locale}"); } // we use the global catalog as source for already translated strings $globalCatalogFile = "{$globalCatalogPath}/{$locale}/LC_MESSAGES/agit.po"; $globalCatalog = $filesystem->exists($globalCatalogFile) ? $this->deleteReferences(Translations::fromPoFile($globalCatalogFile)) : new Translations(); $bundleCatalogFile = "{$bundlePath}/{$this->catalogSubdir}/bundle.{$locale}.po"; $oldBundleCatalog = $filesystem->exists($bundleCatalogFile) ? $this->deleteReferences(Translations::fromPoFile($bundleCatalogFile)) : new Translations(); // NOTE: we delete all headers and only set language, in order to avoid garbage commits $bundleCatalog = new Translations(); $bundleCatalog->deleteHeaders(); $bundleCatalog->setLanguage($locale); // first: only JS messages foreach ($frontendFiles as $file) { $bundleCatalog->addFromJsCodeFile($file, $this->extractorOptions); } $bundleCatalog->mergeWith($oldBundleCatalog, 0); $bundleCatalog->mergeWith($globalCatalog, 0); if ($bundleCatalog->count() && $locale !== $defaultLocale) { $transMap = []; foreach ($bundleCatalog as $entry) { $msgid = ltrim($entry->getId(), ""); $msgstr = $entry->getTranslation(); $transMap[$msgid] = $entry->hasPlural() ? array_merge([$msgstr], $entry->getPluralTranslations()) : $msgstr; } $frontendCatalogs .= sprintf("ag.intl.register(\"%s\", %s);\n\n", $locale, json_encode($transMap, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE)); } // now the same with all messages foreach ($backendFiles as $file) { $bundleCatalog->addFromPhpCodeFile($file, $this->extractorOptions); } $bundleCatalog->mergeWith($this->extraTranslations, Merge::ADD); $bundleCatalog->mergeWith($oldBundleCatalog, 0); $bundleCatalog->mergeWith($globalCatalog, 0); $catalog = $bundleCatalog->toPoString(); $catalog = str_replace(array_keys($files), array_values($files), $catalog); if ($bundleCatalog->count()) { $filesystem->dumpFile("{$bundlePath}/{$this->catalogSubdir}/bundle.{$locale}.po", $catalog); } } if ($frontendCatalogs) { $filesystem->dumpFile("{$bundlePath}/{$this->frontendSubdir}/translations.js", $frontendCatalogs); } $filesystem->remove($this->cacheBasePath); }
/** * Saves the list of $translation to the $locale. * @param Locale $locale * @param Translations $translation */ private function addGettextEntriesToLocale(Locale $locale, Translations $translations) { $defaultTranslations = $locale->hasPoFile() ? Translations::fromPoFile($locale->getPoFilePath()) : new Translations(); $i18n = Strata::i18n(); // it looks reversed to merge defaults into the found strings, but it's the // most efficient way of keeping existing translations $i18n->hardTranslationSetMerge($locale, $defaultTranslations, $translations); $i18n->generateTranslationFiles($locale, $translations); }
/** * Extracts translatable strings from PHP files with xgettext. * * @param string $rootDirectory The base root directory * @param array[string] $phpFiles The relative paths to the PHP files to be parsed * * @throws \Exception Throws an \Exception in case of problems * * @return \Gettext\Translations */ protected static function parseDirectoryDo_xgettext($rootDirectory, $phpFiles) { $initialDirectory = @getcwd(); if ($initialDirectory === false) { throw new \Exception('Unable to determine the current working directory'); } if (@chdir($rootDirectory) === false) { throw new \Exception('Unable to switch to directory ' . $rootDirectory); } try { $tempDirectory = \C5TL\Options::getTemporaryDirectory(); $tempFileList = @tempnam($tempDirectory, 'cil'); if ($tempFileList === false) { throw new \Exception(t('Unable to create a temporary file')); } if (@file_put_contents($tempFileList, implode("\n", $phpFiles)) === false) { global $php_errormsg; if (isset($php_errormsg) && $php_errormsg) { throw new \Exception("Error writing a temporary file: {$php_errormsg}"); } else { throw new \Exception('Error writing a temporary file'); } } $tempFilePot = @tempnam($tempDirectory, 'cil'); if ($tempFilePot === false) { throw new \Exception(t('Unable to create a temporary file')); } $line = 'xgettext'; $line .= ' --default-domain=messages'; // Domain $line .= ' --output=' . escapeshellarg(basename($tempFilePot)); // Output .pot file name $line .= ' --output-dir=' . escapeshellarg(dirname($tempFilePot)); // Output .pot folder name $line .= ' --language=PHP'; // Source files are in php $line .= ' --from-code=UTF-8'; // Source files are in utf-8 $line .= ' --add-comments=i18n'; // Place comment blocks preceding keyword lines in output file if they start with '// i18n: ' $line .= ' --keyword'; // Don't use default keywords $line .= ' --keyword=t:1'; // Look for the first argument of the "t" function for extracting translatable text in singular form $line .= ' --keyword=t2:1,2'; // Look for the first and second arguments of the "t2" function for extracting both the singular and plural forms $line .= ' --keyword=tc:1c,2'; // Look for the first argument of the "tc" function for extracting translation context, and the second argument is the translatable text in singular form. $line .= ' --no-escape'; // Do not use C escapes in output $line .= ' --add-location'; // Generate '#: filename:line' lines $line .= ' --files-from=' . escapeshellarg($tempFileList); // Get list of input files from file $line .= ' 2>&1'; $output = array(); $rc = null; @exec($line, $output, $rc); @unlink($tempFileList); unset($tempFileList); if (!is_int($rc)) { $rc = -1; } if (!is_array($output)) { $output = array(); } if ($rc !== 0) { throw new \Exception('xgettext failed: ' . implode("\n", $output)); } $newTranslations = \Gettext\Translations::fromPoFile($tempFilePot); @unlink($tempFilePot); unset($tempFilePot); } catch (\Exception $x) { @chdir($initialDirectory); if (isset($tempFilePot) && @is_file($tempFilePot)) { @unlink($tempFilePot); } if (isset($tempFileList) && @is_file($tempFileList)) { @unlink($tempFileList); } throw $x; } return $newTranslations; }
protected function execute(InputInterface $input, OutputInterface $output) { try { $vsh = Core::make('helper/validation/strings'); /* @var \Concrete\Core\Utility\Service\Validation\Strings $vsh */ $fh = Core::make('helper/file'); /* @var \Concrete\Core\File\Service\File $fh */ // Let's determine the package handle, directory and version $packageHandle = null; $packageDirectory = null; $packageVersion = null; $p = $input->getArgument('package'); if (is_dir($p) || !$vsh->handle($p)) { $packageDirectory = @realpath($p); if ($packageDirectory === false) { throw new Exception("Unable to find the directory '{$p}'"); } $controllerFile = $packageDirectory . '/' . FILENAME_CONTROLLER; if (!is_file($controllerFile)) { throw new Exception("The directory '{$packageDirectory}' does not seems to contain a valid concrete5 package"); } $controllerContents = $fh->getContents($controllerFile); if ($controllerContents) { $allTokens = @token_get_all($controllerContents); if ($allTokens) { $tokens = array_values(array_filter($allTokens, function ($token) { $keep = true; if (is_array($token)) { switch ($token[0]) { case T_DOC_COMMENT: case T_WHITESPACE: case T_COMMENT: $keep = false; break; } } return $keep; })); // Look for package version for ($i = 0; $i < count($tokens) - 2; ++$i) { if ($packageHandle === null && is_array($tokens[$i + 0]) && $tokens[$i + 0][0] === T_VARIABLE && $tokens[$i + 0][1] === '$pkgHandle' && is_string($tokens[$i + 1]) && $tokens[$i + 1] === '=' && is_array($tokens[$i + 2]) && $tokens[$i + 2][0] === T_CONSTANT_ENCAPSED_STRING) { $packageHandle = @eval('return ' . $tokens[$i + 2][1] . ';'); } if ($packageVersion === null && is_array($tokens[$i + 0]) && $tokens[$i + 0][0] === T_VARIABLE && $tokens[$i + 0][1] === '$pkgVersion' && is_string($tokens[$i + 1]) && $tokens[$i + 1] === '=' && is_array($tokens[$i + 2]) && $tokens[$i + 2][0] === T_CONSTANT_ENCAPSED_STRING) { $packageVersion = @eval('return ' . $tokens[$i + 2][1] . ';'); } } } } if ($packageHandle === null) { $packageHandle = basename($packageDirectory); } } else { foreach (Package::getAvailablePackages(false) as $pkg) { if (strcasecmp($p, $pkg->getPackageHandle()) === 0) { $packageHandle = $pkg->getPackageHandle(); $packageDirectory = $pkg->getPackagePath(); $packageVersion = $pkg->getPackageVersion(); break; } } if ($packageHandle === null) { throw new Exception("Unable to find a package with handle '{$p}'"); } } $packageLanguagesDirectory = $packageDirectory . '/' . DIRNAME_LANGUAGES; // Determine the locales to translate $locales = array(); $localeOption = $input->getOption('locale'); if (in_array('-', $localeOption, true)) { // We don't want any locale } elseif (empty($localeOption)) { // List the currently package locales foreach ($fh->getDirectoryContents($packageLanguagesDirectory) as $item) { if (is_file("{$packageLanguagesDirectory}/{$item}/LC_MESSAGES/messages.mo") || is_file("{$baseDir}/{$item}/LC_MESSAGES/messages.po")) { $locales[] = $item; } } if (empty($locales)) { // Let's use the core locales $locales = Localization::getAvailableInterfaceLanguages(); } } else { // Normalize the locales (eg from it-it to it_IT) foreach ($localeOption as $lo) { $chunks = array(); foreach (explode('_', str_replace('-', '_', $lo)) as $index => $chunk) { if ($index === 0) { // Language (eg zh) $chunks[] = strtolower($chunk); } elseif (strlen($chunk) === 4) { // Script (eg Hans) $chunks[] = ucfirst(strtolower($chunk)); } else { // Territory (eg CN) $chunks[] = strtoupper($chunk); } } $locales[] = implode('_', $chunks); } } // Initialize the master translations file (.pot) $pot = new Translations(); $pot->setHeader('Project-Id-Version', "{$packageHandle} {$packageVersion}"); $pot->setLanguage('en_US'); $pot->setHeader('Report-Msgid-Bugs-To', $input->getOption('contact')); $pot->setHeader('Last-Translator', $input->getOption('translator')); // Parse the package directory $output->writeln('Parsing package contents'); foreach (\C5TL\Parser::getAllParsers() as $parser) { if ($parser->canParseDirectory()) { $output->write('- running parser "' . $parser->getParserName() . '"... '); $parser->parseDirectory($packageDirectory, "packages/{$packageHandle}", $pot, false, $input->getOption('exclude-3rdparty')); $output->writeln('done.'); } } // Save the pot file $output->write('Saving .pot file... '); if (!is_dir($packageLanguagesDirectory)) { @mkdir($packageLanguagesDirectory, 0775, true); if (!is_dir($packageLanguagesDirectory)) { throw new Exception("Unable to create the directory {$packageLanguagesDirectory}"); } } $potFilename = "{$packageLanguagesDirectory}/messages.pot"; if ($pot->toPoFile($potFilename) === false) { throw new Exception("Unable to save the .pot file to {$potFilename}"); } $output->writeln('done.'); // Creating/updating the locale files foreach ($locales as $locale) { $output->writeln("Working on locale {$locale}"); $poDirectory = "{$packageLanguagesDirectory}/{$locale}/LC_MESSAGES"; $po = clone $pot; $po->setLanguage($locale); $poFile = "{$poDirectory}/messages.po"; $moFile = "{$poDirectory}/messages.mo"; if (is_file($poFile)) { $output->write("- reading current .po file... "); $oldPo = Translations::fromPoFile($poFile); $output->writeln('done.'); } elseif (is_file($moFile)) { $output->write("- decompiling current .mo file... "); $oldPo = Translations::fromMoFile($poFile); $output->writeln('done.'); } else { $oldPo = null; } if ($oldPo !== null) { $output->write("- merging translations... "); $po->mergeWith($oldPo, 0); $output->writeln('done.'); } $output->write("- saving .po file... "); if (!is_dir($poDirectory)) { @mkdir($poDirectory, 0775, true); if (!is_dir($poDirectory)) { throw new Exception("Unable to create the directory {$poDirectory}"); } } $po->toPoFile($poFile); $output->writeln('done.'); $output->write("- saving .mo file... "); $po->toMoFile($moFile); $output->writeln('done.'); } } catch (Exception $x) { $output->writeln($x->getMessage()); return 1; } }
private static function processDirectory($state, OutputInterface $output, $dirAbs, $dirRel) { $hDir = @opendir($dirAbs); if ($hDir === false) { throw new Exception("Failed to open directory {$dirAbs}"); } try { $storePrefix = $state->packageHandle; if ($dirRel !== '') { $storePrefix .= "/{$dirRel}"; } if ($state->zip !== null) { if (@$state->zip->addEmptyDir($storePrefix) === false) { $err = "Failed to create directory entry {$storePrefix} in zip file"; $reason = $state->zip->getStatusString(); if ($reason) { $err .= ": {$reason}"; } throw new Exception($err); } } $store = array(); $subDirs = array(); while (($item = readdir($hDir)) !== false) { if ($item === '.' || $item === '..') { continue; } if ($item[0] === '.' && !in_array(static::KEEP_DOT, $state->keep)) { continue; } $itemAbs = $dirAbs . DIRECTORY_SEPARATOR . $item; if (is_dir($itemAbs)) { $subDirs[] = $item; continue; } if (isset($store[$item])) { continue; } $store[$item] = array('kind' => 'file', 'file' => $itemAbs); $p = strrpos($item, '.'); $ext = $p === false || $p === 0 ? '' : strtolower(substr($item, $p + 1)); switch ($ext) { case 'php': if ($state->shortTags !== static::SHORTTAGS_NO) { $newContents = static::expandShortTags($itemAbs, $state->shortTags !== static::SHORTTAGS_KEEPECHO); if ($newContents !== null) { if ($state->updateSourceDirectory) { if (@file_put_contents($itemAbs, $newContents) === false) { throw new Exception("Failed to update PHP file {$itemAbs}"); } } elseif ($state->zip !== null) { $store[$item] = array('kind' => 'contents', 'contents' => $newContents); } $output->writeln("Expanded short tags in: {$dirRel}/{$item}"); } } break; case 'pot': if ($dirRel === 'languages') { if ($state->zip !== null && !in_array(static::KEEP_SOURCES, $state->keep)) { $output->writeln("Skipped source file: {$dirRel}/{$item}"); unset($store[$item]); } } break; case 'po': if (preg_match('%^languages/[^/]+/LC_MESSAGES$%', $dirRel)) { if ($state->zip !== null && !in_array(static::KEEP_SOURCES, $state->keep)) { $output->writeln("Skipped source file: {$dirRel}/{$item}"); unset($store[$item]); } $compile = false; $compiledAbs = substr($itemAbs, 0, -2) . 'mo'; switch ($state->compileTranslations) { case static::YNA_YES: $compile = true; break; case static::YNA_AUTO: if (is_file($compiledAbs)) { $sourceTime = @filemtime($itemAbs); $compiledTime = @filemtime($compiledAbs); if ($sourceTime && $compiledTime && $sourceTime > $compiledTime) { $compile = true; } } else { $compile = true; } break; } if ($compile) { $compiledItem = substr($item, 0, -2) . 'mo'; $output->writeln("Compiling language : {$dirRel}/{$item} => {$dirRel}/{$compiledItem}"); $translations = Translations::fromPoFile($itemAbs); if ($state->updateSourceDirectory) { if ($translations->toMoFile($compiledAbs) === false) { throw new Exception("Failed to write compiled translations file to {$compiledAbs}"); } $store[$compiledItem] = array('kind' => 'file', 'file' => $compiledAbs); } elseif ($state->zip !== null) { $store[$compiledItem] = array('kind' => 'contents', 'contents' => $translations->toMoString()); } } } break; case 'svg': if ($item === 'icon.svg') { $iconSize = null; if ($dirRel === '') { // Package icon $iconSize = $state->packageFormat === static::PACKAGEFORMAT_CURRENT ? array('width' => 200, 'height' => 200) : array('width' => 97, 'height' => 97); } elseif (preg_match('%^blocks/[^/]+$%', $dirRel)) { $iconSize = $state->packageFormat === static::PACKAGEFORMAT_CURRENT ? array('width' => 50, 'height' => 50) : array('width' => 16, 'height' => 16); } if ($iconSize !== null) { if ($state->zip !== null && !in_array(static::KEEP_SOURCES, $state->keep)) { $output->writeln("Skipped source file: {$dirRel}/{$item}"); unset($store[$item]); } $compile = false; $compiledAbs = substr($itemAbs, 0, -3) . 'png'; switch ($state->compileIcons) { case static::YNA_YES: $compile = true; break; case static::YNA_AUTO: if (is_file($compiledAbs)) { $sourceTime = @filemtime($itemAbs); $compiledTime = @filemtime($compiledAbs); if ($sourceTime && $compiledTime && $sourceTime > $compiledTime) { $compile = true; } } else { $compile = true; } break; } if ($compile) { $compiledItem = substr($item, 0, -3) . 'png'; $output->writeln("Generating png icon: {$dirRel}/{$item} => {$dirRel}/{$compiledItem}"); $tmpDir = Core::make('helper/file')->getTemporaryDirectory(); if (!is_dir($tmpDir)) { throw new Exception('Failed to retrieve the temporary directory'); } $tmpFile = @tempnam($tmpDir, 'c5p'); if ($tmpFile === false) { throw new Exception('Failed to create a temporary file in directory ' . $tmpDir); } try { $cmd = 'inkscape'; $cmd .= ' --file=' . escapeshellarg($itemAbs); $cmd .= ' --export-png=' . escapeshellarg($tmpFile); $cmd .= ' --export-area-page'; $cmd .= ' --export-width=' . $iconSize['width']; $cmd .= ' --export-height=' . $iconSize['height']; $cmd .= ' 2>&1'; $execOutput = array(); @exec($cmd, $execOutput, $rc); if (!is_int($rc)) { $rc = -1; } if ($rc !== 0) { throw new Exception(implode("\n", $execOutput)); } $pngData = @file_get_contents($tmpFile); if ($pngData === false) { throw new Exception('Failed to read the generated PNG file'); } } catch (Exception $x) { @unlink($tmpFile); throw $x; } @unlink($tmpFile); if ($pngData === '') { throw new Exception("Inkscape failed to generate the PNG file:\n" . implode("\n", $execOutput)); } if ($state->updateSourceDirectory) { if (@file_put_contents($compiledAbs, $pngData) === false) { throw new Exception("Failed to write rendered SVN icon to {$compiledAbs}"); } $store[$compiledItem] = array('kind' => 'file', 'file' => $compiledAbs); } elseif ($state->zip !== null) { $store[$compiledItem] = array('kind' => 'contents', 'contents' => $pngData); } } } } break; } } @closedir($hDir); if ($state->zip !== null) { foreach ($store as $storeItem => $storeWhat) { $storeName = $storePrefix . '/' . $storeItem; switch ($storeWhat['kind']) { case 'file': if ($state->zip->addFile($storeWhat['file'], $storeName) === false) { $err = "Failed to store file {$storeName} to zip file"; $reason = $state->zip->getStatusString(); if ($reason) { $err .= ": {$reason}"; } throw new Exception($err); } break; case 'contents': if ($state->zip->addFromString($storeName, $storeWhat['contents']) === false) { $err = "Failed to store file {$storeName} to zip file"; $reason = $state->zip->getStatusString(); if ($reason) { $err .= ": {$reason}"; } throw new Exception($err); } break; default: throw new Exception('?'); } } } unset($store); foreach ($subDirs as $subDir) { static::processDirectory($state, $output, $dirAbs . DIRECTORY_SEPARATOR . $subDir, $dirRel === '' ? $subDir : "{$dirRel}/{$subDir}"); } } catch (Exception $x) { @closedir($hDir); throw $x; } }
public function testGeneratedPO() { $translationsFolder = DIR_APPLICATION . '/' . DIRNAME_LANGUAGES; if (!is_dir($translationsFolder)) { mkdir($translationsFolder); } // Extract the translatable strings and create the template .pot file $potFile = $translationsFolder . '/messages.pot'; $translations = new \Gettext\Translations(); $cifParser = new \C5TL\Parser\Cif(); $phpParser = new \C5TL\Parser\Php(); $blockTemplatesParser = new \C5TL\Parser\BlockTemplates(); $themesPresetsParser = new \C5TL\Parser\ThemePresets(); $parsersForDir = array('' => array($phpParser), DIRNAME_BLOCKS => array($blockTemplatesParser), DIRNAME_THEMES => array($phpParser, $themesPresetsParser, $blockTemplatesParser), 'config/install' => array($cifParser)); foreach ($parsersForDir as $parsersDir => $parsers) { $dir = DIR_BASE_CORE; $relDir = DIRNAME_CORE; if ($parsersDir !== '') { $dir .= '/' . $parsersDir; $relDir .= '/' . $parsersDir; } foreach ($parsers as $parser) { try { $parser->parseDirectory($dir, $relDir, $translations); } catch (Exception $x) { $reason = $x->getMessage(); $stack = $x->getTraceAsString(); $this->markTestSkipped(<<<EOT Extraction of translatable strings has been skipped. Reason: {$reason} Stack trace: {$stack} EOT ); return; } } } $translatableStrings = count($translations); $this->assertGreaterThan(2000, $translatableStrings); $translations->toPoFile($potFile); $this->assertFileExists($potFile); // Create a .po file with translations set to source strings $poFile = $translationsFolder . '/messages.po'; $cmd = 'msgen --lang=en-US --output-file=' . escapeshellarg($poFile) . ' ' . escapeshellarg($potFile) . ' 2>&1'; $output = array(); @exec($cmd, $output, $rc); $this->assertSame(0, $rc, "msgen output:\n" . implode("\n", $output)); $this->assertFileExists($poFile); // Set the plural rules $translations = \Gettext\Translations::fromPoFile($poFile); $translations->setLanguage('en_US'); $translations->toPoFile($poFile); $this->assertSame($translatableStrings, count($translations)); // Compile the .po file checking the strings $moFile = $translationsFolder . '/messages.mo'; $cmd = 'msgfmt'; $cmd .= ' --check-format'; // check language dependent format strings $cmd .= ' --check-header'; // verify presence and contents of the header entry $cmd .= ' --check-domain'; // check for conflicts between domain directives and the --output-file option $cmd .= ' --verbose'; // increase verbosity level $cmd .= ' --output-file=' . escapeshellarg($moFile); $cmd .= ' ' . escapeshellarg($poFile); $cmd .= ' 2>&1'; $output = array(); @exec($cmd, $output, $rc); $this->assertSame(0, $rc, "msgfmt output:\n" . implode("\n", $output)); $this->assertFileExists($moFile); }
protected function execute(InputInterface $input, OutputInterface $output) { $this->writeSection($output, 'Csv to Po converter'); $this->cwd = getcwd() . DIRECTORY_SEPARATOR; $output->writeln('<info>Using CWD</info> ' . $this->cwd); $csvFile = $this->getInputCsvFile($input, $output); $poFiles = $this->getInputPoFiles($input, $output); $outputFolder = $this->getOutputFolder($input, $output); $useDefaults = $input->getOption('use-defaults'); if ($useDefaults) { $output->writeln('<info>Csv file</info>:' . "\n" . $csvFile . "\n"); $output->writeln('<info>Po files</info>:'); $this->writeList($output, $poFiles); $output->writeln("\n" . '<info>Output folder</info>: ' . "\n" . $outputFolder . "\n"); } $writeHandles = []; foreach ($poFiles as $poFile) { $key = basename($poFile, '.po'); $output->writeln('<info>loading ' . $key . '</info>...'); $writeHandles[$key] = Translations::fromPoFile($poFile); } $header = null; $output->writeln('<info>reading from csv</info>...'); $reader = Reader::createFromPath($csvFile); foreach ($reader as $i => $row) { if ($header == null) { $header = $row; continue; } $original = null; foreach ($row as $j => $string) { if ($original == null) { $original = $string; continue; } if (empty($string)) { continue; } $writeHandle = $writeHandles[$header[$j]]; $translation = $writeHandle->find(null, $original); if ($translation != false && $translation->translation != $string) { if (!empty($translation->translation)) { $this->handleConflict($input, $output, $original, $translation->translation, $string); } else { $translation->setTranslation($string); } } else { $translation = new Translation(null, $original); $translation->setTranslation($string); $writeHandle[] = $translation; } } } foreach ($writeHandles as $key => $writeHandle) { $output->writeln('<info>writing ' . $key . '</info>...'); $content = Po::toString($writeHandle); file_put_contents($this->outputDir . DIRECTORY_SEPARATOR . $key . '.po', $content); } $output->writeln('<info>done</info>'); }