zappy-base/src/I18n/Translator.php

164 lines
4.3 KiB
PHP

<?php
namespace CrocWork\Zappy\I18n;
/**
* Translator class
*
* The Translator class provides an implementation for setting the application's
* locale and providing translations for use in the application ui. This implementation
* uses a regular expression to check if entered locales conform to a standardized format.
*/
class Translator
{
/**
* Regular expression used to check the form of locale strings.
*/
private const LOCALE_REGEX = '/^[A-Za-z]{2,3}([_-][A-Za-z]{4})?([_-][A-Za-z]{2})?$/';
/**
* The default application locale.
* @var string
*/
private string $fallbackLocale;
/**
* The currently active locale.
* @var string
*/
private string $locale;
/**
* The path where translation files are stored.
* @var string
*/
private string $translationsPath;
/**
* The currently loaded translations.
* @var array
*/
private array $translations;
/**
* Initialize a Translator instance.
*
* To initialize a Translator, it must know where translation files are to be found and what
* locale to set by default on initialization. These values are checked for correctness, upon
* failure an exception will be thrown.
*
* @param string $fallbackLocale
* @param string $translationsPath
*/
public function __construct(string $fallbackLocale, string $translationsPath)
{
$this->fallbackLocale = $fallbackLocale;
$this->locale = $this->fallbackLocale;
$this->translationsPath = $translationsPath;
if(!file_exists($translationsPath) || !is_dir($translationsPath)) {
throw new \RuntimeException("Could not initialize translator: invalid translations path.");
}
}
public function provides(): string
{
return 'translator';
}
/**
* Get the default locale string.
* @return string
*/
public function getFallbackLocale(): string
{
return $this->fallbackLocale;
}
/**
* Get the current locale string.
* @return string
*/
public function getLocale(): string
{
return $this->locale;
}
/**
* Set the current locale string. If the form is invalid, an exception will be thrown.
* @param string $locale
* @return $this
*/
public function setLocale(string $locale): static
{
if(!static::checkLocale($locale)) {
throw new \RuntimeException("Could not set locale: invalid locale format.");
}
$this->locale = $locale;
$this->loadTranslations();
return $this;
}
/**
* Get a translation from the translation array.
* @param string $key
* @param $default
* @return mixed|null|string
*/
public function getTranslation(string $key, $default = null)
{
if(empty($this->translations)) {
return $default;
}
$keys = explode ('.', $key);
$value = $this->translations;
foreach($keys as $index) {
if(!key_exists($index, $value)) return $default;
$value = $value[$index];
}
return $value;
}
/**
* Check if a locale string has the correct format.
* @param string $locale
* @return bool
*/
public static function checkLocale(string $locale): bool
{
return (!empty($locale) && preg_match_all(static::LOCALE_REGEX, $locale));
}
/**
* Load translations for the currently selected locale.
* @return void
*/
private function loadTranslations(): void
{
$dir = $this->translationsPath;
$locale = $this->locale;
$localeDir = $dir . DIRECTORY_SEPARATOR . $locale;
if(!file_exists($localeDir)) {
//TODO: is the error really needed?
throw new \RuntimeException("Unable to load translations; translation directory not found.");
}
$files = glob($localeDir . DIRECTORY_SEPARATOR . '*.php');
if($files === false || empty($files)) {
return;
}
$this->translations = [];
foreach($files as $file) {
$translations = require $file;
$key = basename($file, '.php');
$this->translations[$key] = $translations;
}
}
}