mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 12:05:37 +00:00
Update i18nTextCollector to collect strings also from themes
This commit is contained in:
parent
66f0fc4814
commit
04266f77cc
@ -3,6 +3,7 @@
|
||||
namespace SilverStripe\i18n\Messages;
|
||||
|
||||
use SilverStripe\Assets\Filesystem;
|
||||
use SilverStripe\Core\Path;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use Symfony\Component\Yaml\Dumper;
|
||||
use SilverStripe\i18n\Messages\Symfony\ModuleYamlLoader;
|
||||
@ -43,17 +44,17 @@ class YamlWriter implements Writer
|
||||
}
|
||||
|
||||
// Create folder for lang files
|
||||
$langFolder = $path . '/lang';
|
||||
$langFolder = Path::join($path, 'lang');
|
||||
if (!file_exists($langFolder ?? '')) {
|
||||
Filesystem::makeFolder($langFolder);
|
||||
touch($langFolder . '/_manifest_exclude');
|
||||
touch(Path::join($langFolder, '_manifest_exclude'));
|
||||
}
|
||||
|
||||
// De-normalise messages and convert to yml
|
||||
$content = $this->getYaml($messages, $locale);
|
||||
|
||||
// Open the English file and write the Master String Table
|
||||
$langFile = $langFolder . '/' . $locale . '.yml';
|
||||
$langFile = Path::join($langFolder, $locale . '.yml');
|
||||
if ($fh = fopen($langFile ?? '', "w")) {
|
||||
fwrite($fh, $content ?? '');
|
||||
fclose($fh);
|
||||
|
@ -12,6 +12,7 @@ use SilverStripe\Core\Injector\Injectable;
|
||||
use SilverStripe\Core\Manifest\ClassLoader;
|
||||
use SilverStripe\Core\Manifest\Module;
|
||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||
use SilverStripe\Core\Path;
|
||||
use SilverStripe\Dev\Debug;
|
||||
use SilverStripe\Control\Director;
|
||||
use ReflectionClass;
|
||||
@ -50,6 +51,8 @@ class i18nTextCollector
|
||||
{
|
||||
use Injectable;
|
||||
|
||||
private const THEME_PREFIX = 'themes:';
|
||||
|
||||
/**
|
||||
* Default (master) locale
|
||||
*
|
||||
@ -100,6 +103,13 @@ class i18nTextCollector
|
||||
*/
|
||||
protected $fileExtensions = ['php', 'ss'];
|
||||
|
||||
/**
|
||||
* List all modules and themes
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
private $modulesAndThemes;
|
||||
|
||||
/**
|
||||
* @param $locale
|
||||
*/
|
||||
@ -179,6 +189,8 @@ class i18nTextCollector
|
||||
return;
|
||||
}
|
||||
|
||||
$modules = $this->getModulesAndThemes();
|
||||
|
||||
// Write each module language file
|
||||
foreach ($entitiesByModule as $moduleName => $entities) {
|
||||
// Skip empty translations
|
||||
@ -188,7 +200,7 @@ class i18nTextCollector
|
||||
|
||||
// Clean sorting prior to writing
|
||||
ksort($entities);
|
||||
$module = ModuleLoader::inst()->getManifest()->getModule($moduleName);
|
||||
$module = $modules[$moduleName];
|
||||
$this->write($module, $entities);
|
||||
}
|
||||
}
|
||||
@ -215,9 +227,9 @@ class i18nTextCollector
|
||||
// Restrict modules we update to just the specified ones (if any passed)
|
||||
if (!empty($restrictToModules)) {
|
||||
// Normalise module names
|
||||
$modules = array_filter(array_map(function ($name) {
|
||||
$module = ModuleLoader::inst()->getManifest()->getModule($name);
|
||||
return $module ? $module->getName() : null;
|
||||
$allModules = $this->getModulesAndThemes();
|
||||
$modules = array_filter(array_map(function ($name) use ($allModules) {
|
||||
return array_key_exists($name, $allModules) ? $this->getModuleName($name, $allModules[$name]) : null;
|
||||
}, $restrictToModules ?? []));
|
||||
// Remove modules
|
||||
foreach (array_diff(array_keys($entitiesByModule ?? []), $modules) as $module) {
|
||||
@ -375,10 +387,10 @@ class i18nTextCollector
|
||||
protected function mergeWithExisting($entitiesByModule)
|
||||
{
|
||||
// For each module do a simple merge of the default yml with these strings
|
||||
$modules = $this->getModulesAndThemes();
|
||||
foreach ($entitiesByModule as $module => $messages) {
|
||||
// Load existing localisations
|
||||
$masterFile = ModuleLoader::inst()->getManifest()->getModule($module)->getPath() .
|
||||
DIRECTORY_SEPARATOR . 'lang' . DIRECTORY_SEPARATOR . $this->defaultLocale . '.yml';
|
||||
$masterFile = Path::join($modules[$module]->getPath(), 'lang', $this->defaultLocale . '.yml');
|
||||
$existingMessages = $this->getReader()->read($this->defaultLocale, $masterFile);
|
||||
|
||||
// Merge
|
||||
@ -401,11 +413,11 @@ class i18nTextCollector
|
||||
{
|
||||
// A master string tables array (one mst per module)
|
||||
$entitiesByModule = [];
|
||||
$modules = ModuleLoader::inst()->getManifest()->getModules();
|
||||
foreach ($modules as $module) {
|
||||
$modules = $this->getModulesAndThemes();
|
||||
foreach ($modules as $moduleName => $module) {
|
||||
// we store the master string tables
|
||||
$processedEntities = $this->processModule($module);
|
||||
$moduleName = $module->getName();
|
||||
$moduleName = $this->getModuleName($moduleName, $module);
|
||||
if (isset($entitiesByModule[$moduleName])) {
|
||||
$entitiesByModule[$moduleName] = array_merge_recursive(
|
||||
$entitiesByModule[$moduleName],
|
||||
@ -450,6 +462,37 @@ class i18nTextCollector
|
||||
return $entitiesByModule;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads all modules and themes installed, including app. Uses the format of
|
||||
* the @link ModuleLoader manifest for themes as well.
|
||||
* Themes can be references with "themes:{theme name}".
|
||||
*/
|
||||
private function getModulesAndThemes(): array
|
||||
{
|
||||
if (!$this->modulesAndThemes) {
|
||||
$modules = ModuleLoader::inst()->getManifest()->getModules();
|
||||
// load themes as modules
|
||||
$themes = array_diff(scandir(THEMES_PATH), ['..', '.']);
|
||||
if ($themes) {
|
||||
foreach ($themes as $theme) {
|
||||
if (is_dir(Path::join(THEMES_PATH, $theme))) {
|
||||
$modules[self::THEME_PREFIX . $theme] = new Module(Path::join(THEMES_PATH, $theme), BASE_PATH);
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->modulesAndThemes = $modules;
|
||||
}
|
||||
return $this->modulesAndThemes;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the module or theme
|
||||
*/
|
||||
private function getModuleName(string $origName, Module $module): string
|
||||
{
|
||||
return strpos($origName, self::THEME_PREFIX) === 0 ? $origName : $module->getName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Write entities to a module
|
||||
*
|
||||
@ -462,7 +505,7 @@ class i18nTextCollector
|
||||
$this->getWriter()->write(
|
||||
$entities,
|
||||
$this->defaultLocale,
|
||||
$this->baseSavePath . DIRECTORY_SEPARATOR . $module->getRelativePath()
|
||||
Path::join($this->baseSavePath, $module->getRelativePath())
|
||||
);
|
||||
return $this;
|
||||
}
|
||||
@ -517,25 +560,25 @@ class i18nTextCollector
|
||||
$modulePath = $module->getPath();
|
||||
|
||||
// Search all .ss files in themes
|
||||
if (stripos($module->getRelativePath() ?? '', 'themes' . DIRECTORY_SEPARATOR) === 0) {
|
||||
if (stripos($module->getRelativePath() ?? '', self::THEME_PREFIX) === 0) {
|
||||
return $this->getFilesRecursive($modulePath, null, 'ss');
|
||||
}
|
||||
|
||||
// If non-standard module structure, search all root files
|
||||
if (!is_dir($modulePath . DIRECTORY_SEPARATOR . 'code') && !is_dir($modulePath . DIRECTORY_SEPARATOR . 'src')) {
|
||||
if (!is_dir(Path::join($modulePath, 'code')) && !is_dir(Path::join($modulePath, 'src'))) {
|
||||
return $this->getFilesRecursive($modulePath);
|
||||
}
|
||||
|
||||
// Get code files
|
||||
if (is_dir($modulePath . DIRECTORY_SEPARATOR . 'src')) {
|
||||
$files = $this->getFilesRecursive($modulePath . DIRECTORY_SEPARATOR . 'src', null, 'php');
|
||||
if (is_dir(Path::join($modulePath, 'src'))) {
|
||||
$files = $this->getFilesRecursive(Path::join($modulePath, 'src'), null, 'php');
|
||||
} else {
|
||||
$files = $this->getFilesRecursive($modulePath . DIRECTORY_SEPARATOR . 'code', null, 'php');
|
||||
$files = $this->getFilesRecursive(Path::join($modulePath, 'code'), null, 'php');
|
||||
}
|
||||
|
||||
// Search for templates in this module
|
||||
if (is_dir($modulePath . DIRECTORY_SEPARATOR . 'templates')) {
|
||||
$templateFiles = $this->getFilesRecursive($modulePath . DIRECTORY_SEPARATOR . 'templates', null, 'ss');
|
||||
if (is_dir(Path::join($modulePath, 'templates'))) {
|
||||
$templateFiles = $this->getFilesRecursive(Path::join($modulePath, 'templates'), null, 'ss');
|
||||
} else {
|
||||
$templateFiles = $this->getFilesRecursive($modulePath, null, 'ss');
|
||||
}
|
||||
@ -987,8 +1030,6 @@ class i18nTextCollector
|
||||
return "{$namespace}.{$entity}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Helper function that searches for potential files (templates and code) to be parsed
|
||||
*
|
||||
@ -1004,7 +1045,7 @@ class i18nTextCollector
|
||||
$fileList = [];
|
||||
}
|
||||
// Skip ignored folders
|
||||
if (is_file($folder . DIRECTORY_SEPARATOR . '_manifest_exclude') || preg_match($folderExclude ?? '', $folder ?? '')) {
|
||||
if (is_file(Path::join($folder, '_manifest_exclude')) || preg_match($folderExclude ?? '', $folder ?? '')) {
|
||||
return $fileList;
|
||||
}
|
||||
|
||||
|
@ -3,15 +3,14 @@
|
||||
namespace SilverStripe\i18n\Tests;
|
||||
|
||||
use SilverStripe\Assets\Filesystem;
|
||||
use SilverStripe\Core\Config\Config;
|
||||
use SilverStripe\Core\Manifest\ModuleLoader;
|
||||
use SilverStripe\Core\Manifest\ModuleManifest;
|
||||
use SilverStripe\Core\Path;
|
||||
use SilverStripe\Dev\SapphireTest;
|
||||
use SilverStripe\i18n\i18n;
|
||||
use SilverStripe\i18n\Messages\YamlWriter;
|
||||
use SilverStripe\i18n\Tests\i18nTest\TestDataObject;
|
||||
use SilverStripe\i18n\Tests\i18nTextCollectorTest\Collector;
|
||||
use SilverStripe\i18n\TextCollection\i18nTextCollector;
|
||||
use SilverStripe\Security\Member;
|
||||
|
||||
class i18nTextCollectorTest extends SapphireTest
|
||||
{
|
||||
@ -734,6 +733,34 @@ PHP;
|
||||
);
|
||||
}
|
||||
|
||||
public function testCollectFromTheme()
|
||||
{
|
||||
// create new module manifest only containing the test theme
|
||||
$moduleManifest = new ModuleManifest($this->alternateBasePath);
|
||||
$moduleManifest->addModule(Path::join($this->alternateBasePath, 'themes', 'testtheme1'));
|
||||
$this->pushModuleManifest($moduleManifest);
|
||||
|
||||
$c = i18nTextCollector::create();
|
||||
$entities = $c->collect();
|
||||
|
||||
$this->assertArrayHasKey('testtheme1', $entities);
|
||||
|
||||
$this->assertEquals(
|
||||
[
|
||||
'i18nTestTheme1.LAYOUTTEMPLATE' => 'Theme1 Layout Template',
|
||||
'i18nTestTheme1.MAINTEMPLATE' => 'Theme1 Main Template',
|
||||
'i18nTestTheme1.ss.LAYOUTTEMPLATENONAMESPACE' => 'Theme1 Layout Template no namespace',
|
||||
'i18nTestTheme1.ss.REPLACEMENTNONAMESPACE' => 'Theme1 My replacement no namespace: {replacement}',
|
||||
'i18nTestTheme1Include.REPLACEMENTINCLUDENAMESPACE' => 'Theme1 My include replacement: {replacement}',
|
||||
'i18nTestTheme1Include.WITHNAMESPACE' => 'Theme1 Include Entity with Namespace',
|
||||
'i18nTestTheme1Include.ss.NONAMESPACE' => 'Theme1 Include Entity without Namespace',
|
||||
'i18nTestTheme1Include.ss.REPLACEMENTINCLUDENONAMESPACE' => 'Theme1 My include replacement no namespace: {replacement}',
|
||||
'i18nTestTheme1.REPLACEMENTNAMESPACE' => 'Theme1 My replacement: {replacement}',
|
||||
],
|
||||
$entities['testtheme1']
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that duplicate keys are resolved to the appropriate modules
|
||||
*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user