Skip to content

Instantly share code, notes, and snippets.

@mathroc
Created November 9, 2021 17:31
Show Gist options
  • Save mathroc/e64c831d8bc0101be9388f4140d2d5f6 to your computer and use it in GitHub Desktop.
Save mathroc/e64c831d8bc0101be9388f4140d2d5f6 to your computer and use it in GitHub Desktop.
<?php
// Translator
interface Translator
{
/**
* @param array<mixed> $context
*/
public function translate(string $message, array $context = [], ?LocalesPreferences $localesPreferences = null): string;
}
interface LocalesPreferences
{
/**
* @param array<Locale> $availableLocales
*/
public function bestLocale(array $availableLocales): ?Locale;
}
// Translation catalog
interface LocalizableMessage
{
public function localize(LocalesPreferences $localesPreferences): ?LocalizedMessage;
}
interface LocalizedMessage
{
/**
* @param array<mixed> $context
*/
public function render(array $context): string;
}
interface TranslationCatalog
{
public function fetch(string $message): ?LocalizableMessage;
}
// exemple
class MyTranslator implements Translator
{
/**
* @param callable(string $message): LocalizedMessage
*/
public function __construct(
private TranslationCatalog $catalog,
private Closure $fallback,
private LocalesPreferences $defaultLocalesPreferences
) {}
/**
* @param array<mixed> $context
*/
public function translate(string $message, array $context = [], ?LocalesPreferences $localesPreferences = null): string
{
$localizableMessage = $this->catalog->fetch($message);
$localizedMessage = $localizableMessage?->localize($localesPreferences ?? $this->defaultLocalesPreferences)
?: ($this->fallback)($message);
return $localizedMessage->render($context);
}
}
class MyLocalesPreferences implements LocalesPreferences
{
/**
* @param array<Locale> $availableLocales
*/
public function bestLocale(array $availableLocales): ?Locale
{
return $availableLocales[array_rand($availableLocales, 1)] ?? null;
}
}
class MyLocalizableMessage implements LocalizableMessage
{
/** @var array<Locales> */
private array $locales = [];
/** SplObjectStorage<Locale,string> */
private SplObjectStorage $messages;
/**
* @param array<array{locale:Locale,message:string}>
*/
public function __construct(array $messages) {
$this->messages = new SplObjectStorage();
foreach ($messages as ["locale" => $locale, "message" => $message]) {
$this->locales[] = $locale;
$this->messages->attach($locale, $message);
}
}
public function localize(LocalesPreferences $localesPreferences): ?LocalizedMessage {
return $this->messages[$localesPreferences->bestLocale($this->locales)] ?? null;
}
}
class MySprintfLocalizedMessage implements LocalizedMessage
{
public function __construct(private string $localizedContent) {}
/**
* @param array<mixed> $context
*/
public function render(array $context): string {
return sprintf($this->localizedContent, ...$context);
}
}
class MyTranslationCatalog implements TranslationCatalog
{
/**
* @param array<string,LocalizableMessage> $messages
*/
public function __construct(private array $messages) {}
public function fetch(string $message): ?LocalizableMessage {
return $this->messages[$message] ?? null;
}
}
$catalog = new MyTranslationCatalog([
"Hello World!" => new MyLocalizableMessage([[
"locale" => new Locale("fr"),
"message" => new MySprintfLocalizedMessage("Bonjour, monde !"),
]]),
"Hello %s!" => new MyLocalizableMessage([[
"locale" => new Locale("fr"),
"message" => new MySprintfLocalizedMessage("Bonjour, %s !"),
]]),
]);
global $translator;
$translator = new MyTranslator(
$catalog,
static fn (string $message) => new MySprintfLocalizedMessage($message),
new MyLocalesPreferences(),
);
/**
* @param array<mixed> $context
*/
function __(string $key, array $context = []): string
{
global $translator;
return $translator->translate($key, $context);
}
echo __("Hello %s!", ["John"]), PHP_EOL;
echo __("Good bye %s!", ["John"]), PHP_EOL;
// output:
// Bonjour, John !
// Good bye John!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment