From f82aaa82a5cd1840df88eec1a9daa5b235a135e4 Mon Sep 17 00:00:00 2001 From: Toby Zerner Date: Wed, 10 Jun 2015 14:23:56 +0930 Subject: [PATCH] Lay the groundwork for translation & refactor asset compilation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ditched the idea of having language packs as extensions. Reasoning: 1. Because we use machine keys for translations (rather than English keys), extensions need to be able to define default translations. If English translations are to be included in extensions and not in a language pack extension, then it doesn’t make sense to have other languages as language pack extensions. Inconsistency → complexity. 2. Translations should maintain version parity with their respective extensions. There’s no way to do this if extension translations are external to the extension. Instead, localisation will be a core effort, as well as a per-extension effort. Translators will be encouraged to send PRs to core + extensions. In core, each locale has a directory containing three files: - translations.yml - config.js: contains pluralisation logic for the JS app, as well as moment.js localisation if necessary - config.php: contains pluralisation logic for the PHP app Extensions can use the Flarum\Extend\Locale extender to add/override translations/config to a locale. Asset compilation has been completely refactored with a better architecture. Translations + config.js are compiled and cached for the currently active locale. --- framework/core/js/lib/utils/app.js | 6 + framework/core/js/lib/utils/translator.js | 32 ++++ framework/core/locale/en/config.js | 3 + framework/core/locale/en/config.php | 7 + framework/core/locale/en/translations.yml | 2 + .../2015_02_24_000000_create_users_table.php | 1 + framework/core/src/Assets/AssetManager.php | 60 +++++++ .../core/src/Assets/CompilerInterface.php | 10 ++ framework/core/src/Assets/JsCompiler.php | 9 + framework/core/src/Assets/LessCompiler.php | 26 +++ .../core/src/Assets/RevisionCompiler.php | 95 ++++++++++ .../core/src/Core/CoreServiceProvider.php | 14 ++ .../src/Core/Seeders/ConfigTableSeeder.php | 1 + .../core/src/Extend/AdminTranslations.php | 18 ++ framework/core/src/Extend/ForumAssets.php | 2 +- .../core/src/Extend/ForumTranslations.php | 19 ++ framework/core/src/Extend/Locale.php | 57 ++++++ .../core/src/Forum/Actions/IndexAction.php | 74 ++++++-- .../core/src/Forum/ForumServiceProvider.php | 9 +- framework/core/src/Locale/JsCompiler.php | 24 +++ framework/core/src/Locale/LocaleManager.php | 65 +++++++ .../core/src/Locale/TranslationCompiler.php | 29 ++++ framework/core/src/Locale/Translator.php | 42 +++++ framework/core/src/Support/AssetManager.php | 163 ------------------ 24 files changed, 589 insertions(+), 179 deletions(-) create mode 100644 framework/core/js/lib/utils/translator.js create mode 100644 framework/core/locale/en/config.js create mode 100644 framework/core/locale/en/config.php create mode 100644 framework/core/locale/en/translations.yml create mode 100644 framework/core/src/Assets/AssetManager.php create mode 100644 framework/core/src/Assets/CompilerInterface.php create mode 100644 framework/core/src/Assets/JsCompiler.php create mode 100644 framework/core/src/Assets/LessCompiler.php create mode 100644 framework/core/src/Assets/RevisionCompiler.php create mode 100644 framework/core/src/Extend/AdminTranslations.php create mode 100644 framework/core/src/Extend/ForumTranslations.php create mode 100644 framework/core/src/Extend/Locale.php create mode 100644 framework/core/src/Locale/JsCompiler.php create mode 100644 framework/core/src/Locale/LocaleManager.php create mode 100644 framework/core/src/Locale/TranslationCompiler.php create mode 100644 framework/core/src/Locale/Translator.php delete mode 100644 framework/core/src/Support/AssetManager.php diff --git a/framework/core/js/lib/utils/app.js b/framework/core/js/lib/utils/app.js index 8857a96d7..998b2d224 100644 --- a/framework/core/js/lib/utils/app.js +++ b/framework/core/js/lib/utils/app.js @@ -1,10 +1,12 @@ import ItemList from 'flarum/utils/item-list'; import Alert from 'flarum/components/alert'; import ServerError from 'flarum/utils/server-error'; +import Translator from 'flarum/utils/translator'; class App { constructor() { this.initializers = new ItemList(); + this.translator = new Translator(); this.cache = {}; this.serverError = null; } @@ -55,6 +57,10 @@ class App { var queryString = m.route.buildQueryString(params); return url+(queryString ? '?'+queryString : ''); } + + translate(key, input) { + return this.translator.translate(key, input); + } } export default App; diff --git a/framework/core/js/lib/utils/translator.js b/framework/core/js/lib/utils/translator.js new file mode 100644 index 000000000..e8235e864 --- /dev/null +++ b/framework/core/js/lib/utils/translator.js @@ -0,0 +1,32 @@ +export default class Translator { + constructor() { + this.translations = {}; + } + + plural(count) { + return count == 1 ? 'one' : 'other'; + } + + translate(key, input) { + var parts = key.split('.'); + var translation = this.translations; + + parts.forEach(function(part) { + translation = translation && translation[part]; + }); + + if (typeof translation === 'object' && typeof input.count !== 'undefined') { + translation = translation[this.plural(input.count)]; + } + + if (typeof translation === 'string') { + for (var i in input) { + translation = translation.replace(new RegExp('{'+i+'}', 'gi'), input[i]); + } + + return translation; + } else { + return key; + } + } +} diff --git a/framework/core/locale/en/config.js b/framework/core/locale/en/config.js new file mode 100644 index 000000000..42739e270 --- /dev/null +++ b/framework/core/locale/en/config.js @@ -0,0 +1,3 @@ +app.translator.plural = function(count) { + return count == 1 ? 'one' : 'other'; +}; diff --git a/framework/core/locale/en/config.php b/framework/core/locale/en/config.php new file mode 100644 index 000000000..3ca0f9cf2 --- /dev/null +++ b/framework/core/locale/en/config.php @@ -0,0 +1,7 @@ + function ($count) { + return $count == 1 ? 'one' : 'other'; + } +]; diff --git a/framework/core/locale/en/translations.yml b/framework/core/locale/en/translations.yml new file mode 100644 index 000000000..898cbf708 --- /dev/null +++ b/framework/core/locale/en/translations.yml @@ -0,0 +1,2 @@ +core: + diff --git a/framework/core/migrations/2015_02_24_000000_create_users_table.php b/framework/core/migrations/2015_02_24_000000_create_users_table.php index ec637bfc0..31656606d 100644 --- a/framework/core/migrations/2015_02_24_000000_create_users_table.php +++ b/framework/core/migrations/2015_02_24_000000_create_users_table.php @@ -19,6 +19,7 @@ class CreateUsersTable extends Migration $table->string('email', 150)->unique(); $table->boolean('is_activated')->default(0); $table->string('password', 100); + $table->string('locale', 10)->default('en'); $table->text('bio')->nullable(); $table->text('bio_html')->nullable(); $table->string('avatar_path', 100)->nullable(); diff --git a/framework/core/src/Assets/AssetManager.php b/framework/core/src/Assets/AssetManager.php new file mode 100644 index 000000000..1e6df4fc6 --- /dev/null +++ b/framework/core/src/Assets/AssetManager.php @@ -0,0 +1,60 @@ +js = $js; + $this->less = $less; + } + + public function addFile($file) + { + $ext = pathinfo($file, PATHINFO_EXTENSION); + + switch ($ext) { + case 'js': + $this->js->addFile($file); + break; + + case 'css': + case 'less': + $this->less->addFile($file); + break; + + default: + throw new RuntimeException('Unsupported asset type: '.$ext); + } + } + + public function addFiles(array $files) + { + array_walk($files, [$this, 'addFile']); + } + + public function addLess($string) + { + $this->less->addString($string); + } + + public function addJs($strings) + { + $this->js->addString($string); + } + + public function getCssFile() + { + return $this->less->getFile(); + } + + public function getJsFile() + { + return $this->js->getFile(); + } +} diff --git a/framework/core/src/Assets/CompilerInterface.php b/framework/core/src/Assets/CompilerInterface.php new file mode 100644 index 000000000..a5946dde0 --- /dev/null +++ b/framework/core/src/Assets/CompilerInterface.php @@ -0,0 +1,10 @@ + true, + 'cache_dir' => storage_path().'/less' + ]); + + foreach ($this->files as $file) { + $parser->parseFile($file); + } + + foreach ($this->strings as $string) { + $parser->parse($string); + } + + return $parser->getCss(); + } +} diff --git a/framework/core/src/Assets/RevisionCompiler.php b/framework/core/src/Assets/RevisionCompiler.php new file mode 100644 index 000000000..171732630 --- /dev/null +++ b/framework/core/src/Assets/RevisionCompiler.php @@ -0,0 +1,95 @@ +path = $path; + $this->filename = $filename; + } + + public function addFile($file) + { + $this->files[] = $file; + } + + public function addString($string) + { + $this->strings[] = $string; + } + + public function getFile() + { + if (! ($revision = $this->getRevision())) { + $revision = Str::quickRandom(); + $this->putRevision($revision); + } + + $lastModTime = 0; + foreach ($this->files as $file) { + $lastModTime = max($lastModTime, filemtime($file)); + } + + $ext = pathinfo($this->filename, PATHINFO_EXTENSION); + $file = $this->path.'/'.substr_replace($this->filename, '-'.$revision, -strlen($ext) - 1, 0); + + if (! file_exists($file) + || filemtime($file) < $lastModTime) { + file_put_contents($file, $this->compile()); + } + + return $file; + } + + protected function format($string) + { + return $string; + } + + protected function compile() + { + $output = ''; + + foreach ($this->files as $file) { + $output .= $this->format(file_get_contents($file)); + } + + foreach ($this->strings as $string) { + $output .= $this->format($string); + } + + return $output; + } + + protected function getRevisionFile() + { + return $this->path.'/rev-manifest.json'; + } + + protected function getRevision() + { + if (file_exists($file = $this->getRevisionFile())) { + $manifest = json_decode(file_get_contents($file), true); + return array_get($manifest, $this->filename); + } + } + + protected function putRevision($revision) + { + if (file_exists($file = $this->getRevisionFile())) { + $manifest = json_decode(file_get_contents($file), true); + } else { + $manifest = []; + } + + $manifest[$this->filename] = $revision; + + return file_put_contents($this->getRevisionFile(), json_encode($manifest)); + } +} diff --git a/framework/core/src/Core/CoreServiceProvider.php b/framework/core/src/Core/CoreServiceProvider.php index 5556a18f7..5ff1ec549 100644 --- a/framework/core/src/Core/CoreServiceProvider.php +++ b/framework/core/src/Core/CoreServiceProvider.php @@ -18,6 +18,7 @@ use Flarum\Core\Events\RegisterUserGambits; use Flarum\Extend\Permission; use Flarum\Extend\ActivityType; use Flarum\Extend\NotificationType; +use Flarum\Extend\Locale; class CoreServiceProvider extends ServiceProvider { @@ -56,6 +57,17 @@ class CoreServiceProvider extends ServiceProvider (new ActivityType('Flarum\Core\Activity\StartedDiscussionActivity', 'Flarum\Api\Serializers\PostBasicSerializer')), (new ActivityType('Flarum\Core\Activity\JoinedActivity', 'Flarum\Api\Serializers\UserBasicSerializer')) ); + + foreach (['en'] as $locale) { + $dir = __DIR__.'/../../locale/'.$locale; + + $this->extend( + (new Locale($locale)) + ->translations($dir.'/translations.yml') + ->config($dir.'/config.php') + ->js($dir.'/config.js') + ); + } } /** @@ -72,6 +84,8 @@ class CoreServiceProvider extends ServiceProvider $this->app->singleton('flarum.formatter', 'Flarum\Core\Formatter\FormatterManager'); + $this->app->singleton('flarum.localeManager', 'Flarum\Locale\LocaleManager'); + $this->app->bind( 'Flarum\Core\Repositories\DiscussionRepositoryInterface', 'Flarum\Core\Repositories\EloquentDiscussionRepository' diff --git a/framework/core/src/Core/Seeders/ConfigTableSeeder.php b/framework/core/src/Core/Seeders/ConfigTableSeeder.php index 96d58846e..8efb0adc7 100644 --- a/framework/core/src/Core/Seeders/ConfigTableSeeder.php +++ b/framework/core/src/Core/Seeders/ConfigTableSeeder.php @@ -20,6 +20,7 @@ class ConfigTableSeeder extends Seeder 'welcome_message' => 'Flarum is now at a point where you can have basic conversations, so here is a little demo for you to break.', 'welcome_title' => 'Welcome to Flarum Demo Forum', 'extensions_enabled' => '[]', + 'locale' => 'en', 'theme_primary_color' => '#536F90', 'theme_secondary_color' => '#536F90', 'theme_dark_mode' => false, diff --git a/framework/core/src/Extend/AdminTranslations.php b/framework/core/src/Extend/AdminTranslations.php new file mode 100644 index 000000000..bea1f8fbb --- /dev/null +++ b/framework/core/src/Extend/AdminTranslations.php @@ -0,0 +1,18 @@ +keys = $keys; + } + + public function extend(Container $container) + { + + } +} diff --git a/framework/core/src/Extend/ForumAssets.php b/framework/core/src/Extend/ForumAssets.php index 20fb0a260..7071f8eff 100644 --- a/framework/core/src/Extend/ForumAssets.php +++ b/framework/core/src/Extend/ForumAssets.php @@ -14,7 +14,7 @@ class ForumAssets implements ExtenderInterface public function extend(Application $app) { $app['events']->listen('Flarum\Forum\Events\RenderView', function ($event) { - $event->assets->addFile($this->files); + $event->assets->addFiles($this->files); }); } } diff --git a/framework/core/src/Extend/ForumTranslations.php b/framework/core/src/Extend/ForumTranslations.php new file mode 100644 index 000000000..3fae872d1 --- /dev/null +++ b/framework/core/src/Extend/ForumTranslations.php @@ -0,0 +1,19 @@ +keys = $keys; + } + + public function extend(Application $container) + { + IndexAction::$translations = array_merge(IndexAction::$translations, $this->keys); + } +} diff --git a/framework/core/src/Extend/Locale.php b/framework/core/src/Extend/Locale.php new file mode 100644 index 000000000..817a92d2c --- /dev/null +++ b/framework/core/src/Extend/Locale.php @@ -0,0 +1,57 @@ +locale = $locale; + } + + public function translations($translations) + { + $this->translations = $translations; + + return $this; + } + + public function config($config) + { + $this->config = $config; + + return $this; + } + + public function js($js) + { + $this->js = $js; + + return $this; + } + + public function extend(Application $container) + { + $manager = $container->make('flarum.localeManager'); + + if ($this->translations) { + $manager->addTranslations($this->locale, $this->translations); + } + + if ($this->config) { + $manager->addConfig($this->locale, $this->config); + } + + if ($this->js) { + $manager->addJsFile($this->locale, $this->js); + } + } +} diff --git a/framework/core/src/Forum/Actions/IndexAction.php b/framework/core/src/Forum/Actions/IndexAction.php index 18f70afec..c1d36890b 100644 --- a/framework/core/src/Forum/Actions/IndexAction.php +++ b/framework/core/src/Forum/Actions/IndexAction.php @@ -10,9 +10,15 @@ use DB; use Flarum\Forum\Events\RenderView; use Flarum\Api\Request as ApiRequest; use Flarum\Core; +use Flarum\Assets\AssetManager; +use Flarum\Assets\JsCompiler; +use Flarum\Assets\LessCompiler; +use Flarum\Locale\JsCompiler as LocaleJsCompiler; class IndexAction extends BaseAction { + public static $translations = []; + public function handle(Request $request, $params = []) { $config = DB::table('config')->whereIn('key', ['base_url', 'api_url', 'forum_title', 'welcome_title', 'welcome_message'])->lists('value', 'key'); @@ -44,23 +50,63 @@ class IndexAction extends BaseAction ->with('session', $session) ->with('alert', $alert); - $assetManager = app('flarum.forum.assetManager'); $root = __DIR__.'/../../..'; - $assetManager->addFile([ - $root.'/js/forum/dist/app.js', - $root.'/less/forum/app.less' - ]); - $assetManager->addLess(' - @fl-primary-color: '.Core::config('theme_primary_color').'; - @fl-secondary-color: '.Core::config('theme_secondary_color').'; - @fl-dark-mode: '.(Core::config('theme_dark_mode') ? 'true' : 'false').'; - @fl-colored_header: '.(Core::config('theme_colored_header') ? 'true' : 'false').'; - '); + $public = public_path().'/assets'; - event(new RenderView($view, $assetManager, $this)); + $assets = new AssetManager( + new JsCompiler($public, 'forum.js'), + new LessCompiler($public, 'forum.css') + ); + + $assets->addFile($root.'/js/forum/dist/app.js'); + $assets->addFile($root.'/less/forum/app.less'); + + $variables = [ + 'fl-primary-color' => Core::config('theme_primary_color', '#000'), + 'fl-secondary-color' => Core::config('theme_secondary_color', '#000'), + 'fl-dark-mode' => Core::config('theme_dark_mode') ? 'true' : 'false', + 'fl-colored-header' => Core::config('theme_colored_header') ? 'true' : 'false' + ]; + foreach ($variables as $name => $value) { + $assets->addLess("@$name: $value;"); + } + + $locale = $user->locale ?: Core::config('locale', 'en'); + + $localeManager = app('flarum.localeManager'); + $translations = $localeManager->getTranslations($locale); + $jsFiles = $localeManager->getJsFiles($locale); + + $localeCompiler = new LocaleJsCompiler($public, 'locale-'.$locale.'.js'); + $localeCompiler->setTranslations(static::filterTranslations($translations)); + array_walk($jsFiles, [$localeCompiler, 'addFile']); + + event(new RenderView($view, $assets, $this)); return $view - ->with('styles', $assetManager->getCSSFiles()) - ->with('scripts', $assetManager->getJSFiles()); + ->with('styles', [$assets->getCssFile()]) + ->with('scripts', [$assets->getJsFile(), $localeCompiler->getFile()]); + } + + protected static function filterTranslations($translations) + { + $filtered = []; + + foreach (static::$translations as $key) { + $parts = explode('.', $key); + $level = &$filtered; + + foreach ($parts as $part) { + if (! isset($level[$part])) { + $level[$part] = []; + } + + $level = &$level[$part]; + } + + $level = array_get($translations, $key); + } + + return $filtered; } } diff --git a/framework/core/src/Forum/ForumServiceProvider.php b/framework/core/src/Forum/ForumServiceProvider.php index 9ce974e34..67a6f4374 100644 --- a/framework/core/src/Forum/ForumServiceProvider.php +++ b/framework/core/src/Forum/ForumServiceProvider.php @@ -1,7 +1,8 @@ extend( + new ForumTranslations([ + // + ]) + ); } /** diff --git a/framework/core/src/Locale/JsCompiler.php b/framework/core/src/Locale/JsCompiler.php new file mode 100644 index 000000000..de2d82b3c --- /dev/null +++ b/framework/core/src/Locale/JsCompiler.php @@ -0,0 +1,24 @@ +translations = $translations; + } + + public function compile() + { + $output = "var app = require('flarum/app')['default']; app.translator.translations = ".json_encode($this->translations).";"; + + foreach ($this->files as $filename) { + $output .= file_get_contents($filename); + } + + return $output; + } +} diff --git a/framework/core/src/Locale/LocaleManager.php b/framework/core/src/Locale/LocaleManager.php new file mode 100644 index 000000000..bc9e600e8 --- /dev/null +++ b/framework/core/src/Locale/LocaleManager.php @@ -0,0 +1,65 @@ +translations[$locale])) { + $this->translations[$locale] = []; + } + + $this->translations[$locale][] = $translations; + } + + public function addJsFile($locale, $js) + { + if (! isset($this->js[$locale])) { + $this->js[$locale] = []; + } + + $this->js[$locale][] = $js; + } + + public function addConfig($locale, $config) + { + if (! isset($this->config[$locale])) { + $this->config[$locale] = []; + } + + $this->config[$locale][] = $config; + } + + public function getTranslations($locale) + { + $files = array_get($this->translations, $locale, []); + + $parts = explode('-', $locale); + + if (count($parts) > 1) { + $files = array_merge(array_get($this->translations, $parts[0], []), $files); + } + + $compiler = new TranslationCompiler($locale, $files); + + return $compiler->getTranslations(); + } + + public function getJsFiles($locale) + { + $files = array_get($this->js, $locale, []); + + $parts = explode('-', $locale); + + if (count($parts) > 1) { + $files = array_merge(array_get($this->js, $parts[0], []), $files); + } + + return $files; + } +} diff --git a/framework/core/src/Locale/TranslationCompiler.php b/framework/core/src/Locale/TranslationCompiler.php new file mode 100644 index 000000000..ce625007a --- /dev/null +++ b/framework/core/src/Locale/TranslationCompiler.php @@ -0,0 +1,29 @@ +locale = $locale; + $this->filenames = $filenames; + } + + public function getTranslations() + { + // @todo caching + + $translations = []; + + foreach ($this->filenames as $filename) { + $translations = array_replace_recursive($translations, Yaml::parse(file_get_contents($filename))); + } + + return $translations; + } +} diff --git a/framework/core/src/Locale/Translator.php b/framework/core/src/Locale/Translator.php new file mode 100644 index 000000000..239df50c9 --- /dev/null +++ b/framework/core/src/Locale/Translator.php @@ -0,0 +1,42 @@ +translations = $translations; + $this->plural = $plural; + } + + public function plural($count) + { + $callback = $this->plural; + + return $callback($count); + } + + public function translate($key, array $input = []) + { + $translation = array_get($this->translations, $key); + + if (is_array($translation) && isset($input['count'])) { + $translation = $translation[$this->plural($input['count'])]; + } + + if (is_string($translation)) { + foreach ($input as $k => $v) { + $translation = str_replace('{'.$k.'}', $v, $translation); + } + + return $translation; + } else { + return $key; + } + } +} diff --git a/framework/core/src/Support/AssetManager.php b/framework/core/src/Support/AssetManager.php deleted file mode 100644 index dd511c366..000000000 --- a/framework/core/src/Support/AssetManager.php +++ /dev/null @@ -1,163 +0,0 @@ - [], - 'js' => [], - 'less' => [] - ]; - - protected $less = []; - - protected $publicPath; - - protected $name; - - protected $storage; - - public function __construct(Filesystem $storage, $publicPath, $name) - { - $this->storage = $storage; - $this->publicPath = $publicPath; - $this->name = $name; - } - - public function addFile($files) - { - foreach ((array) $files as $file) { - $ext = pathinfo($file, PATHINFO_EXTENSION); - $this->files[$ext][] = $file; - } - } - - public function addLess($strings) - { - foreach ((array) $strings as $string) { - $this->less[] = $string; - } - } - - protected function getAssetDirectory() - { - $dir = $this->publicPath; - if (! $this->storage->isDirectory($dir)) { - $this->storage->makeDirectory($dir); - } - return $dir; - } - - protected function getRevisionFile() - { - return $this->getAssetDirectory().'/'.$this->name; - } - - protected function getRevision() - { - if (file_exists($file = $this->getRevisionFile())) { - return file_get_contents($file); - } - } - - protected function putRevision($revision) - { - return file_put_contents($this->getRevisionFile(), $revision); - } - - protected function getFiles($type, Closure $callback) - { - $dir = $this->getAssetDirectory(); - - if (! ($revision = $this->getRevision())) { - $revision = Str::quickRandom(); - $this->putRevision($revision); - } - - $lastModTime = 0; - foreach ($this->files[$type] as $file) { - $lastModTime = max($lastModTime, filemtime($file)); - } - - if (! file_exists($file = $dir.'/'.$this->name.'-'.$revision.'.'.$type) - || filemtime($file) < $lastModTime) { - $this->storage->put($file, $callback()); - } - - return [$file]; - } - - public function clearCache() - { - if ($revision = $this->getRevision()) { - $dir = $this->getAssetDirectory(); - foreach (['css', 'js'] as $type) { - @unlink($dir.'/'.$this->name.'-'.$revision.'.'.$type); - } - } - } - - public function getCSSFiles() - { - return $this->getFiles('css', function () { - return $this->compileCSS(); - }); - } - - public function getJSFiles() - { - return $this->getFiles('js', function () { - return $this->compileJS(); - }); - } - - public function compileLess() - { - ini_set('xdebug.max_nesting_level', 200); - - $parser = new Less_Parser(['compress' => true, 'cache_dir' => storage_path().'/less']); - - $css = []; - $dir = $this->getAssetDirectory(); - foreach ($this->files['less'] as $file) { - $parser->parseFile($file); - } - - foreach ($this->less as $less) { - $parser->parse($less); - } - - return $parser->getCss(); - } - - public function compileCSS() - { - $css = $this->compileLess(); - - foreach ($this->files['css'] as $file) { - $css .= $this->storage->get($file); - } - - // minify - - return $css; - } - - public function compileJS() - { - $js = ''; - - foreach ($this->files['js'] as $file) { - $js .= $this->storage->get($file).';'; - } - - // minify - - return $js; - } -}