Translatable.php
PHP
Path: src/I18n/Translatable.php
<?php
namespace mini\I18n;
use mini\Mini;
/**
* Translatable string class that implements Stringable
*
* Stores the source text, variables, and calling file context for future translation.
* Currently acts as a pass-through, returning the original text with variable substitution.
*/
class Translatable implements \Stringable
{
private string $sourceText;
private array $vars;
private string $sourceFile;
public function __construct(string $text, array $vars = [], ?string $sourceFile = null)
{
$this->sourceText = $text;
$this->vars = $vars;
$this->sourceFile = $sourceFile ?? $this->getCallingFile();
}
/**
* Convert to string using Translator from container
*/
public function __toString(): string
{
// Get translator from container if available, otherwise fallback to direct interpolation
try {
$translator = Mini::$mini->get(TranslatorInterface::class);
return $translator->translate($this);
} catch (\Exception $e) {
// Fallback if translator is not available or throws error
}
// Fallback: direct interpolation (for backward compatibility)
return $this->interpolateVariables($this->sourceText, $this->vars);
}
/**
* Get the file that called t() using debug_backtrace()
*/
private function getCallingFile(): string
{
static $thisFile = null;
static $functionsFile = null;
// Cache the paths to skip (this file and the functions.php file)
if ($thisFile === null) {
$thisFile = __FILE__;
$functionsFile = __DIR__ . '/functions.php';
}
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
// Get project root from Mini singleton
$projectRoot = Mini::$mini->root;
// Find the first call that's not in this file or the I18n functions.php
foreach ($backtrace as $frame) {
if (isset($frame['file']) &&
$frame['file'] !== $thisFile &&
$frame['file'] !== $functionsFile) {
// Return relative path from project root
$relativePath = str_replace($projectRoot . '/', '', $frame['file']);
return $relativePath;
}
}
return 'unknown';
}
/**
* Replace {variable_name} placeholders with values from vars array
*/
private function interpolateVariables(string $text, array $vars): string
{
if (empty($vars)) {
return $text;
}
return preg_replace_callback('/\{([a-zA-Z_][a-zA-Z0-9_]*)\}/', function ($matches) use ($vars) {
$varName = $matches[1];
return isset($vars[$varName]) ? (string) $vars[$varName] : $matches[0];
}, $text);
}
/**
* Get source text (for debugging/development)
*/
public function getSourceText(): string
{
return $this->sourceText;
}
/**
* Get source file (for debugging/development)
*/
public function getSourceFile(): string
{
return $this->sourceFile;
}
/**
* Get variables (for debugging/development)
*/
public function getVars(): array
{
return $this->vars;
}
}