Index

I18n

MMO\core · 1 symbol · generated 30 Sep 2026

class

I18n

I18n.php:18

Internationalization module.

class MMO\core\I18n extends MMO\core\classes\aModule
Namespace
MMO\core
Extends
aModule
You should write all your message in the same language (English normally). Any dynamic text in a message should be written as a placeholder moustache (`{xxx}`). To translate a message, we use its md5 and look for a translation for this message in the required language.

Index

LANGUAGE_REGEX
dateMDYCountry that use notation month/day/year
lang
translations
DefaultConfig() static
findLanguage($lang, $languages, $useValues)Find language in a list of languages
formatDate($format, $date, $lang)Format a date, then translate its textual parts (translateDate).
get($text, $lang)Look up a single translation, with no placeholder substitution (translate for that).
getByPrefix($prefix, $lang)Returns a list of translation with a given prefix, removing the prefix from the id
isMDYFormat($lang)
load($lang, $translations)Register (merging into any already-loaded for the same language) a set of message => translation pairs.
matchLanguage($range, $lang)RFC 4647 §3.3.2, the exact matching algorithm, transcribed step by step:
onInc($event)
setLanguage($lang)Set language.
splitLanguage($lang)Match the full input against the RFC 5646 §2.1 'langtag' production:
translate($message, $context, $prefix, $lang)Translate a message and its context
translateDate($date, $lang)Translate the textual parts of an already-formatted date (e.g.
_($message, $context, $prefix, $lang) staticMain entry point to translate a string

Constants1

LANGUAGE_REGEX

const LANGUAGE_REGEX = '(?<language>[A-Za-z]{2,3}(?:-(?<extlang>[A-Za-z]{3}(?:-[A-Za-z]{3}){0,2}))?|[A-Za-z]{4}|[A-Za-z]{5,8})(?:-(?<script>[A-Za-z]{4}))?(?:-(?<region>[A-Za-z]{2}|[0-9]{3}))?(?:-(?<variants>(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3})(?:-(?:[A-Za-z0-9]{5,8}|[0-9][A-Za-z0-9]{3}))*))?(?:-(?<extensions>[A-WY-Za-wy-z0-9]-[A-Za-z0-9]{2,8}(?:-[A-Za-z0-9]{2,8})*(?:-[A-WY-Za-wy-z0-9]-[A-Za-z0-9]{2,8}(?:-[A-Za-z0-9]{2,8})*)*))?(?:-(?<privateuse>[xX](?:-[A-Za-z0-9]{1,8})+))?'

Properties3

dateMDY

Country that use notation month/day/year

array $dateMDY = […13 items]
Type
array

lang

#[MMO\core\attributes\Serialize(null, 'setLanguage')] protected string $lang = ''
Type
string

translations

protected array $translations = []
Type
array

Methods14

DefaultConfig()

staticI18n.php:42
static function DefaultConfig(): array
returnsarraydefault configuration: the current language defaults to the LANGUAGE constant.

findLanguage($lang, $languages, $useValues)

I18n.php:253

Find language in a list of languages

function findLanguage(string $lang, array $languages, bool $useValues = false): string|int|null
$langstringlanguage tag
$languagesarraylist or map of languages
$useValues = falseboolif true, use values instead of keys for the lookup
returnsstring | int | nullthe key in $languages or null

formatDate($format, $date, $lang)

I18n.php:308

Format a date, then translate its textual parts (translateDate).

function formatDate( string $format = 'l d F Y', DateTimeInterface|string|int $date = 'now', ?string $lang = null )
$format = 'l d F Y'stringa DateTime::format format string.
$date = 'now'DateTimeInterface | string | intdateTime.
$lang = nullnull | string
returns
$i18n->load('fr', ['/dates:Monday' => 'Lundi']); $i18n->formatDate('l', '2024-01-01', 'fr'); // 'Lundi'

get($text, $lang)

I18n.php:76

Look up a single translation, with no placeholder substitution (translate for that).

function get(string $text, ?string $lang = null): string
$textstringthe message to translate.
$lang = nullnull | stringdefaults to the current language (setLanguage); resolved via findLanguage (so a close-enough loaded language, e.g. 'en' for a requested 'en-GB', is used if there's no exact match).
returnsstringthe translation, or $text itself if none is found.

getByPrefix($prefix, $lang)

I18n.php:88

Returns a list of translation with a given prefix, removing the prefix from the id

function getByPrefix(string $prefix, ?string $lang = null): array
$prefixstring
$lang = nullnull | string
returnsarray

isMDYFormat($lang)

I18n.php:283
function isMDYFormat(?string $lang = null): bool
$lang = nullnull | string
returnsboolwhether $lang (default: the current language) conventionally writes dates as month/day/year ($dateMDY).

load($lang, $translations)

I18n.php:64

Register (merging into any already-loaded for the same language) a set of message => translation pairs.

function load(string $lang, array $translations): void
$langstringsupports _ and -.
$translationsarraymessage => translation.
returnsvoid
$i18n->load('fr', ['Hello' => 'Bonjour']);

matchLanguage($range, $lang)

I18n.php:203

RFC 4647 §3.3.2, the exact matching algorithm, transcribed step by step:

function matchLanguage(string $range, string $lang): bool1. Split both the extended language range and the language tag into subtag lists (split on '-'). Two subtags match if they are equal case-insensitively, or the range's subtag is the wildcard '*'. 2. Compare the first subtag of each list. If they don't match, the overall match fails. Otherwise advance both pointers. 3. While there are more subtags left in the range's list: A. If the range's current subtag is '*', advance the range pointer only, and continue. B. Else if there are no more subtags in the tag's list, fail. C. Else if the range's current subtag matches the tag's current subtag, advance both pointers and continue. D. Else if the tag's current subtag is a "singleton" (a single letter or digit), fail. E. Else, advance the tag pointer only, and continue. 4. When the range's list is exhausted, the match succeeds.
$rangestring
$langstring
returnsbool

onInc($event)

I18n.php:127
#[MMO\core\attributes\Listen('MMO\\core\\events\\Inc')] function onInc(MMO\core\events\Inc $event): void
See
Inc event handler: registers a hook so the manifest also loads its translations for the current language once included.
$eventInc
returnsvoid

setLanguage($lang)

I18n.php:139

Set language.

function setLanguage(string $lang): voidWARNING: PHP use _ instead of -
$langstringsupport _ and -
returnsvoid

splitLanguage($lang)

I18n.php:179

Match the full input against the RFC 5646 §2.1 'langtag' production:

function splitLanguage(string $lang): ?arraylangtag = language ["-" script] ["-" region] *("-" variant) *("-" extension) ["-" privateuse] language = 2*3ALPHA ["-" extlang] / 4ALPHA / 5*8ALPHA extlang = 3ALPHA *2("-" 3ALPHA) script = 4ALPHA region = 2ALPHA / 3DIGIT variant = 5*8alphanum / (DIGIT 3alphanum) extension = singleton 1*("-" (2*8alphanum)) singleton = alphanum, excluding "x" / "X" privateuse = "x" 1*("-" (1*8alphanum)) Returns the regex match array, or null if the whole string doesn't conform.
$langstring
returnsnull | array

translate($message, $context, $prefix, $lang)

I18n.php:110

Translate a message and its context

function translate( string $message, array $context = [], string $prefix = '', ?string $lang = null ): string
$messagestringthe message (with/without placeholders)
$context = []arraydata to inject into the placeholders (also translated)
$prefix = ''stringa prefix to add to every string to translate (ex: 'dates/')
$lang = nullnull | string
returnsstring

translateDate($date, $lang)

I18n.php:295

Translate the textual parts of an already-formatted date (e.g. month/day names), via translations loaded under the /dates: prefix (getByPrefix/load).

function translateDate(string $date, ?string $lang = null): string
$datestringan already-formatted date string, e.g. from formatDate/DateTime::format``.
$lang = nullnull | string
returnsstring$date with every matching /dates:-prefixed translation substituted in (strtr).

_($message, $context, $prefix, $lang)

staticI18n.php:54

Main entry point to translate a string

static function _( string $message, array $context = [], string $prefix = '', ?string $lang = null ): string
$messagestring
$context = []array
$prefix = ''string
$lang = nullnull | string
returnsstring
Inherited members (47)
ConstantsDeclared byDescription
ALERTiLogLevelAlert: Action must be taken immediately.
ALLiLogLevelMask for all
ALL_PLUSiLogLeveldebug everything
BACKTRACEiLogLevel
CRITICALiLogLevelCritical: Critical conditions.
DEBUGiLogLevelDebug: Detailed debug information.
DEEPiLogLeveldebug everything with arguments
EMERGENCYiLogLevelEmergency: System is unusable.
ERRORiLogLevelError: Runtime errors that do not require immediate action but should typically be logged and monitored.
ERROR_PLUSiLogLevelMask for all error
FATALiLogLevelMask for all fatal error
INFOiLogLevelInfo: Interesting events.
INFO_PLUSiLogLevelMask for all errors/warning/notices/info
MEMORYiLogLevelinclude backtrace object
NOTICEiLogLevelNotice: Normal but significant events.
NOTICE_PLUSiLogLevelMask for all errors/warning/notices/info
TRACEiLogLevelinclude backtrace as origins
WARNINGiLogLevelWarning: Exceptional occurrences that are not errors.
WARNING_PLUSiLogLevelMask for all errors and warnings
PropertiesDeclared byDescription
verbosityaModule
MethodsDeclared byDescription
alert($message, $context)aModuleAction must be taken immediately.
backtraces($message, $context)aModuleForce-log a message at INFO level together with a full stack trace/memory snapshot (iLogLevel::BACKTRACE), bypassing the verbosity mask entirely (any non-zero verbosity is enough to trigger it).
Configure($config)aModuleMerge $config into this module's configuration in the shared Configuration.
critical($message, $context)aModuleCritical conditions. No-op if CRITICAL is currently filtered out by the verbosity mask.
debug($message, $context)aModuleDetailed debug information.
debugName()aModuleHuman-readable identifier used as the default {origin} in this instance's log messages.
emergency($message, $context)aModuleSystem is unusable. No-op if EMERGENCY is currently filtered out by the verbosity mask.
error($message, $context)aModuleRuntime errors that do not require immediate action but should typically be logged and monitored.
getVerbosity()aModule
info($message, $context)aModuleInteresting events. No-op if INFO is currently filtered out by the verbosity mask.
Instantiate($config)aModule(Re)create the shared instance immediately, configured with $config merged into the class's existing config.
IsShared()aModule
log($level, $message, $context)aModuleLogs with an arbitrary level.
notice($message, $context)aModuleNormal but significant events.
offsetExists($key)aModule\ArrayAccess::offsetExists -- tells whether $key is a declared property at all, regardless of whether it is currently readable/writable through array access.
offsetGet($key)aModule\ArrayAccess::offsetGet -- reads a property directly, unless it has a #[Serialize(serialize: 'methodName')] attribute, in which case that zero-argument method is called instead (e.g.
offsetSet($key, $value)aModule\ArrayAccess::offsetSet -- sets a property directly, unless it has a #[Serialize(unserialize: 'methodName')] attribute, in which case that method is called with $value instead (e.g.
offsetUnset($key)aModule\ArrayAccess::offsetUnset -- equivalent to $module[$key] = null.
serialize($path, $serializer)aModule\MMO\core\interfaces\iSerializable::serialize -- exports every property (via
setVerbosity($verb)aModuleSet the current verbosity mask, replacing whatever it was before.
Shared()aModuleGet the shared instance, creating (and configuring) it on first call.
testVerbosity($level)aModuleTest whether a given level (or set of levels) is currently enabled by this instance's verbosity mask.
traitUnshare($serialize)aModuleRemove the shared instance, so the next Shared() call creates a fresh one.
unserialize($data, $path, $instances)aModule\MMO\core\interfaces\iSerializable::unserialize -- merges $data onto DefaultConfig, runs it through the (possibly overridden) constructor, then applies each resulting value to its property (via
Unshare($serialize)aModuleLike tSingleton::Unshare, but also disconnects this module's instance from every event it was listening to (via Event::DisconnectListeners) before dropping it.
warning($message, $context)aModuleExceptional occurrences that are not errors.
__construct($config)aModuleprotected constructor, so it is not possible to do new Module(...) called by the serialization process just before being injected with data.