integrated Phing as build framework, moved translation export for SPA from artisan

This commit is contained in:
Jeff Poirier 2024-08-21 00:42:44 +00:00
parent 04da6074e8
commit 0f7be5a43a
9 changed files with 185 additions and 132 deletions

1
.gitignore vendored
View file

@ -5,6 +5,7 @@
/.composer
/.env
/.env.dottie-backup
/.vscode
#/.git
/.git-credentials
/.gitconfig

View file

@ -7,18 +7,27 @@
<!-- added folders to include lookups -->
<includepath classpath = "vendor/phing/phing/classes" />
<includepath classpath = "build/lib" />
<includepath classpath = "build/phing/tasks" />
<includepath classpath = "build/phing" />
<!-- CUSTOM TASKS -->
<taskdef classname="pixelfed.i18n.generate" name="i18nGenerate" />
<taskdef classname="tasks.pixelfed.i18n.GenerateTask" name="generatei18n" />
<!-- TARGETS -->
<target name = "build" description = "assemble/generate codebase">
<i18nGenerate />
</target>
<target name = "build_translations" description = "generate/verify translations">
<generatei18n />
</target>
<target name = "install" depends = "build" description = "install all requirements on the system">
<target name = "build_web" description = "generate/pack web assets">
<exec executable = "npm" checkreturn = "true" logoutput = "true">
<arg value = "run" />
<arg value = "development" />
</exec>
</target>
<target name = "build" description = "assemble/generate codebase">
<phingcall target = "build_translations" />
<phingcall target = "build_web" />
</target>
</project>

View file

@ -0,0 +1,31 @@
<?php
namespace lib;
require_once 'phing/Task.php';
use Illuminate\Contracts\Console\Kernel;
use Illuminate\Foundation\Application;
/**
* Base class for all Pixelfed custom build tasks
*/
abstract class BuildTaskBase extends \Task {
/**
* Laravel application instance
*/
protected $app;
public function __construct() {
$this->bootstrapLaravelApplication();
}
/**
* Couples the Laravel application lifetime with the task lifetime,
* ensuring build tasks can use the same paradigm as artisan commands
*/
private function bootstrapLaravelApplication() {
$this->app = require Application::inferBasePath() . '/bootstrap/app.php';
$this->app->make(Kernel::class)->bootstrap();
}
}

43
build/lib/Directories.php Normal file
View file

@ -0,0 +1,43 @@
<?php
/**
* Singleton that stores important directories
* task-wide.
*/
class DIRECTORIES {
private static $instance;
/// Root folder for translationn source files
public string $TranslationsRoot;
/// Export folder for single page web application
public string $TranslationsExportSpa;
/// Export folder for single page web application (alternate path)
public string $TranslationsExportSpaAlt;
public function __construct() {
$this->TranslationsRoot = resource_path('lang/');
$this->TranslationsExportSpa = resource_path('assets/js/i18n/');
$this->TranslationsExportSpaAlt = public_path('_lang/');
}
protected function __clone() { }
public function __wakeup()
{ throw new \Exception("Cannot unserialize a singleton."); }
/**
* Get the unique instance of the singleton.
*
* Only available after completion of BuildTaskBase constructor
* due to dependency on Laravel application initilization.
*/
public static function get(): DIRECTORIES {
if(!isset(self::$instance)) {
self::$instance = new DIRECTORIES();
}
return self::$instance;
}
}

View file

@ -1,108 +0,0 @@
<?php
/*
* Translation-related tooling.
*
* Translation is in flux at this time, as it is split between two
* realms; server-side HTML delivered via Laravel and a single page
* app powered by Vue.js.
*
* Both frameworks have translation systems (Illumate\Translation for
* Laravel and an i18n plugin for Vue.js) with spiritually similar
* systems, using native data structures (arrays for PHP and hashes
* for Vue.js).
*
* The translation support solution that encapsulate both, currently,
* is to start from the php files and push to the SPA, in order to
* allow for full translation.
*
* SPA translations are contained in the web.php data file.
*/
namespace pixelfed\i18n;
use Illuminate\Console\Command;
define('RESOURCES_PATH', 'resources/lang');
/**
* Operates on a set of translation values.
*/
class TranslationSet {
private string $lang_code;
/**
* Construct a translation set from a language code
*
* @return TranslationSet Translations for requested language
*/
public static function FromLanguageCode(string $code) {
return new \TranslationSet($code);
}
public function ExportToJson() {
$strings = \Lang::get('web', [], $this->lang_code);
$json = json_encode($strings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$path = "{DIRECTORIES::get().export}{$this->lang_code}.json";
file_put_contents($path, $json);
$pathAlt = "{DIRECTORIES::get().exportAlt}{$this->lang_code}.json";
file_put_contents($pathAlt, $json);
}
private function __construct(string $code) {
$this->lang_code = $code;
$this->rootPath = base_path('resources/lang') . '/' . $code;
}
};
class LanguageCodes {
/**
* By scanning the translation resources folder, builds
* a list of all currently defined translation languages.
*
* @return array[] Language codes for all defined translations
*/
public static function GetAllDefined() {
foreach (new \DirectoryIterator($path) as $io) {
$name = $io->getFilename();
$skip = ['vendor'];
if($io->isDot() || in_array($name, $skip)) {
continue;
}
if($io->isDir()) {
array_push($langs, $name);
}
}
}
};
/**
* This singleton class efficiently stores and
* tracks directories used in various operations
* of i18n.
*/
class DIRECTORIES {
private static $instance;
// enforce singleton pattern
protected function __construct() {
$this->export = resource_path('assets/js/i18n/');
$this->exportAlt = public_path('_lang/');
}
protected function __clone() { }
public function __wakeup()
{ throw new \Exception("Cannot unserialize a singleton."); }
public static function get(): DIRECTORIES {
if(!isset(self::$instance)) {
self::$instance = new DIRECTORIES();
}
return self::$instance;
}
public $export;
public $exportAlt;
}

View file

@ -0,0 +1,31 @@
<?php
namespace lib\i18n;
require_once 'Directories.php';
class LanguageCodes {
/**
* By scanning the translation resources folder, builds
* a list of all currently defined translation languages.
*
* @return array[] Language codes for all defined translations
*/
public static function GetAllDefined() {
$langs = array();
foreach (new \DirectoryIterator(\DIRECTORIES::get()->TranslationsRoot) as $io) {
$name = $io->getFilename();
$skip = ['vendor'];
if($io->isDot() || in_array($name, $skip)) {
continue;
}
if($io->isDir()) {
array_push($langs, $name);
}
}
return $langs;
}
};

View file

@ -0,0 +1,40 @@
<?php
namespace lib\i18n;
require_once 'Directories.php';
/**
* Tracks a set of translations for a given language
* code.
*/
class TranslationSet {
/**
* Obtain a translation set for a language code
*
* @return TranslationSet Translations for requested language
*/
public static function FromLanguageCode(string $code) {
return new TranslationSet($code);
}
/**
* Export subset of translations for the single page web
* application for Pixelfed.
*
* Translations are taken from the web PHP source file and
* pushed out as a Vue.js i18n JSON file.
*/
public function ExportForSinglePageApp() {
$dirs = \DIRECTORIES::get();
$strings = \Lang::get('web', [], $this->lang_code);
$json = json_encode($strings, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
$path = "{$dirs->TranslationsExportSpa}{$this->lang_code}.json";
file_put_contents($path, $json);
$pathAlt = "{$dirs->TranslationsExportSpaAlt}{$this->lang_code}.json";
file_put_contents($pathAlt, $json);
}
private function __construct(private string $lang_code) {}
};

View file

@ -0,0 +1,24 @@
<?php
require 'BuildTaskBase.php';
require 'i18n/LanguageCode.php';
require 'i18n/TranslationSet.php';
use lib\BuildTaskBase;
use lib\i18n\LanguageCodes;
use lib\i18n\TranslationSet;
class GenerateTask extends BuildTaskBase {
public function __construct() { parent::__construct(); }
public function init() {}
public function main() {
foreach(LanguageCodes::GetAllDefined() as $lang) {
TranslationSet::FromLanguageCode($lang)->ExportForSinglePageApp();
}
}
};

View file

@ -1,18 +0,0 @@
<?php
namespace pixelfed\i18n;
require_once 'phing/Task.php';
require_once 'i18n.php';
use Task;
class generate extends Task {
public function __construct() {}
public function main() {
foreach(LanguageCodes::GetAllDefined() as $lang) {
TranslationSet.FromLanguageCode($lang).ExportToJson();
}
}
};