API Reference
Complete reference for every class and interface derafu/translation
ships.
Interfaces
TranslatableInterface
Derafu\Translation\Contract\TranslatableInterface. Extends Symfony’s own
Symfony\Contracts\Translation\TranslatableInterface and adds
Stringable, so a translatable value can always be cast to a string (the
untranslated, eagerly ICU-formatted version) even without a translator.
interface TranslatableInterface extends
\Symfony\Contracts\Translation\TranslatableInterface,
Stringable
{
public function __toString(): string;
}
Symfony\Contracts\Translation\TranslatableInterface itself requires:
public function trans(TranslatorInterface $translator, ?string $locale = null): string;
TranslationResourceProviderInterface
Derafu\Translation\Contract\TranslationResourceProviderInterface.
Declares directories containing translation files, meant to be collected
(e.g. via a dependency-injection tagged iterator) so
TranslationResourceRegistrar can register every directory without a
central place having to know about each source in advance.
interface TranslationResourceProviderInterface
{
/**
* @return iterable<string> Absolute paths to directories with files
* named `domain(+intl-icu)?.locale.format`.
*/
public function getDirectories(): iterable;
}
TranslatableMessageInterface
Derafu\Translation\Contract\TranslatableMessageInterface. A message that
can be read: its id, its parameters and its domain. It is what is behind
TranslatableMessage and what a translatable exception is made of. It lets a
message be logged by its id and its parameters, sent as data (for example to an
API client that translates by itself), or checked in a test without a
translator.
interface TranslatableMessageInterface extends TranslatableInterface, JsonSerializable
{
public function getMessage(): string;
/** @return array<string, mixed> */
public function getParameters(): array;
public function getDomain(): ?string;
public function getDefaultLocale(): ?string;
public function jsonSerialize(): array;
}
A message is also exported as data, so json_encode($message) gives its id, its
parameters, its domain and its default locale, and not an empty object:
{"message":"Hello {name}","parameters":{"name":"Ana"},"domain":"auth","defaultLocale":null}
It is a representation to read, for logs, APIs and the stores that keep JSON (the
session of Mezzio, for example). The message is not rebuilt from it: a view
translates from the data, message.message|trans(message.parameters, message.domain).
A parameter that is a translatable message, or a translatable exception, is
exported as its own data; any other exception, as its message. What is not JSON (a
date, an object, a resource) stays as json_encode() makes it, so it does not come
back as it was.
TranslatableAwareInterface
Derafu\Translation\Contract\TranslatableAwareInterface. Something that is
made of a translatable value and gives it:
interface TranslatableAwareInterface
{
public function getTranslatable(): TranslatableInterface;
}
TranslatableMessageAwareInterface
Derafu\Translation\Contract\TranslatableMessageAwareInterface. Extends
TranslatableAwareInterface for the objects whose translatable value is a
message that can be read:
interface TranslatableMessageAwareInterface extends TranslatableAwareInterface
{
/** @throws TranslatableLogicException If the value is not a message. */
public function getTranslatableMessage(): TranslatableMessageInterface;
}
Every translatable exception and error of the package implements it (see Exceptions).
Classes
TranslatableMessage
Derafu\Translation\TranslatableMessage. A message with ICU placeholders
that can be translated using a translator, or eagerly ICU-formatted (as-is)
when no translator is available.
final class TranslatableMessage implements TranslatableMessageInterface
{
/**
* @param string $message ICU MessageFormat string, used as the
* translation id and as the fallback text.
* @param array<string, mixed> $parameters ICU placeholder values.
* @param string|null $domain Translation domain, null for Symfony's
* default ('messages').
* @param string|null $defaultLocale Locale used for eager ICU
* formatting in __toString() when no translator is available, and as
* the fallback locale in trans() when none is given.
*/
public function __construct(
string $message,
array $parameters = [],
?string $domain = null,
?string $defaultLocale = null,
);
/**
* Translates the message. If the translator knows its catalogues
* (`TranslatorBagInterface`) and has no entry for the message (in the
* locale, its fallbacks and the domain), returns what __toString()
* returns instead of letting the translator substitute the parameter
* names inside the braces.
*/
public function trans(TranslatorInterface $translator, ?string $locale = null): string;
/**
* Formats the raw message via ICU, without a translator. Returns the
* raw message unchanged if the pattern is invalid.
*/
public function __toString(): string;
// The getters and the data of TranslatableMessageInterface.
public function getMessage(): string;
public function getParameters(): array;
public function getDomain(): ?string;
public function getDefaultLocale(): ?string;
public function jsonSerialize(): array;
}
A parameter that is a Throwable is its message, not its __toString() (the
dump that PHP makes of an exception, with its file, line and trace). A
translatable exception is translated by the translator when it has the entry, and
without one it is formatted with its message.
TranslationResourceRegistrar
Derafu\Translation\TranslationResourceRegistrar. Discovers translation
files in directories and registers them as resources of a
Symfony\Component\Translation\Translator, using the LoaderInterfaces
registered on it. This is the piece symfony/translation doesn’t provide
standalone (directory discovery is normally wired by a full framework).
final class TranslationResourceRegistrar
{
public function __construct(private readonly Translator $translator);
/**
* Registers every file directly inside a directory.
*
* @throws InvalidArgumentException If the directory doesn't exist, a
* file's name doesn't follow the `domain.locale.format` convention, or
* its extension isn't supported.
*/
public function registerDirectory(string $directory): void;
/**
* @param iterable<string> $directories
*/
public function registerDirectories(iterable $directories): void;
/**
* @param iterable<TranslationResourceProviderInterface> $providers
*/
public function registerFromProviders(iterable $providers): void;
/** @return array<string> Every locale registered so far. */
public function getRegisteredLocales(): array;
/** @return array<string> Every domain registered so far. */
public function getRegisteredDomains(): array;
}
Registration order is precedence order: for the same key in the same domain/locale, the last directory registered wins.
Supported file extensions: yaml, yml, json, php, xlf, xliff,
po, mo, csv, ini.
SimpleTranslationResourceProvider
Derafu\Translation\SimpleTranslationResourceProvider. Default
implementation of TranslationResourceProviderInterface that wraps a
fixed list of directories.
final class SimpleTranslationResourceProvider implements TranslationResourceProviderInterface
{
/** @param iterable<string> $directories */
public function __construct(private readonly iterable $directories);
public function getDirectories(): iterable;
}
TranslatorFactory
Derafu\Translation\TranslatorFactory. Builds a
Symfony\Component\Translation\Translator with every file loader
TranslationResourceRegistrar supports already registered, plus the given
resource providers, plus fallback locales.
final class TranslatorFactory
{
/**
* @param array<string> $fallbackLocales
* @param iterable<TranslationResourceProviderInterface> $resourceProviders
* Registered immediately, inside this call, so registration always
* happens whenever a Translator is built — regardless of whether
* anything else references a registrar directly (a dependency-injected
* service nothing depends on is never instantiated by the container).
*/
public static function create(
string $defaultLocale,
array $fallbackLocales = [],
iterable $resourceProviders = [],
): Translator;
}
Trait
TranslatableExceptionTrait
Derafu\Translation\Trait\TranslatableExceptionTrait. Adds translation
support to any exception. Used internally by every exception class listed
below — apply it directly only if you can’t extend one of them (see
Exceptions).
trait TranslatableExceptionTrait
{
protected string $defaultDomain = 'errors';
protected string $defaultLocale = 'en';
/**
* @param string|array|TranslatableInterface $message
* - string: used as both message and translation id.
* - array: first element is the message/id, the rest are named ICU
* parameters.
* - TranslatableInterface: used directly.
* @throws InvalidArgumentException When an empty array is given, or
* its first element isn't a string.
*/
public function __construct(
string|array|TranslatableInterface $message,
int $code = 0,
?Throwable $previous = null,
);
public function trans(TranslatorInterface $translator, ?string $locale = null): string;
/** @return array<string, mixed> */
public function __serialize(): array;
/** @param array<string, mixed> $data */
public function __unserialize(array $data): void;
}
getTranslatable() and getTranslatableMessage() give what the exception
translates with (see Exceptions).
__serialize()/__unserialize() intentionally drop the stack trace (it
may contain non-serializable values such as closures or resources), so
these exceptions can safely be stored in sessions (e.g. for flash
messages). After unserializing, getTrace() no longer reflects the
exception’s original call stack.
Exception Hierarchy
Every class below implements TranslatableInterface via
TranslatableExceptionTrait, and mirrors the equivalent native PHP SPL
exception:
| Class | Extends |
|---|---|
Exception\Core\TranslatableException |
Exception |
Exception\Core\TranslatableLogicException |
LogicException |
Exception\Core\TranslatableRuntimeException |
RuntimeException |
Exception\Core\TranslatableJsonException |
JsonException |
Exception\Logic\TranslatableBadFunctionCallException |
BadFunctionCallException |
Exception\Logic\TranslatableBadMethodCallException |
BadMethodCallException |
Exception\Logic\TranslatableDomainException |
DomainException |
Exception\Logic\TranslatableInvalidArgumentException |
InvalidArgumentException |
Exception\Logic\TranslatableLengthException |
LengthException |
Exception\Logic\TranslatableOutOfRangeException |
OutOfRangeException |
Exception\Runtime\TranslatableOutOfBoundsException |
OutOfBoundsException |
Exception\Runtime\TranslatableOverflowException |
OverflowException |
Exception\Runtime\TranslatableRangeException |
RangeException |
Exception\Runtime\TranslatableUnderflowException |
UnderflowException |
Exception\Runtime\TranslatableUnexpectedValueException |
UnexpectedValueException |
All under the Derafu\Translation\Exception\ namespace.
The \Error family has its equivalents, with the same trait, under
Derafu\Translation\Error\:
| Class | Extends |
|---|---|
TranslatableError |
Error |
TranslatableTypeError |
TypeError |
TranslatableValueError |
ValueError |
TranslatableArithmeticError |
ArithmeticError |
TranslatableDivisionByZeroError |
DivisionByZeroError |
Lint
Tools for tests, never for the code that runs. They need
nikic/php-parser. See Lint for the conventions, what not to do and
the known limits.
| Class | What it is |
|---|---|
Lint\TranslationAudit |
audit(string $directory, TranslationResourceProviderInterface|iterable $providers, string $locale = 'es', array $allowedThrowables = [], array $messageMethods = []): TranslationAuditReport. Messages, catalogues and exceptions in one call. |
Lint\TranslationAuditReport |
The facts that an audit found: dynamicMessages, missingTranslations, notUsedBySources, notTranslatable, nothingFound; and describe(array $findings): list<string> to turn them into lines for a failed test. |
Lint\MessageMethod |
A method of a package that receives the id of a message: __construct(string $class, string $method, ?string $domain = null, int|string $id = 0, int|string|null $domainArgument = null). |
Lint\MessageReferenceScanner |
__construct(list<MessageMethod> $methods = []), scanFile(), scanDirectory(): the messages that the code builds. |
Lint\MessageReference |
A message found: id, domain, class, file, line, function, expression, isDynamic(), identity(). |
Lint\ThrowReferenceScanner |
scanFile(), scanDirectory(): the exceptions that are not translatable, thrown or declared. |
Lint\ThrowReference |
An exception found: class, extends, file, line, loadable, isDeclaration(). |
Dependencies
derafu/translation requires:
php: ^8.5ext-intl(PHP’s ICU bindings, for\MessageFormatter)symfony/translation-contracts: ^3.7symfony/yaml: ^8.1symfony/translation: ^8.1
No dependency on symfony/framework-bundle, symfony/http-kernel, or
symfony/dependency-injection — the dependency-injection recipe described
in Advanced Usage is entirely optional wiring that lives
in the consuming application.