773 lines
20 KiB
PHP
773 lines
20 KiB
PHP
<?php
|
||
|
||
/*********************************************************************
|
||
* AUTHOR: Clemens Schwaighofer
|
||
* CREATED: 2004/11/18
|
||
* VERSION: 3.0.0
|
||
* RELEASED LICENSE: GNU GPL 3
|
||
* SHORT DESCRIPTION:
|
||
* init class for gettext. Original was just a function &
|
||
* var setting include for wordpress.
|
||
* I changed that to a class to be more portable with my style of coding
|
||
* VERSION 3.0 (2022/4) removes all old folder layout and uses standard gettext
|
||
* PUBLIC METHODS
|
||
* __ : returns string (translated or original if not found)
|
||
* __n : plural string
|
||
* __p : string with context
|
||
* __np: string with context and plural
|
||
*
|
||
* HISTORY:
|
||
* 2022/4/15 (cs) drop all old folder layout support, new folder base
|
||
* in locale with standard gettext layout of
|
||
* locale/LC_MESSAGES/domain.mo
|
||
* 2005/10/17 (cs) made an on the fly switch method (reload of lang)
|
||
*********************************************************************/
|
||
|
||
declare(strict_types=1);
|
||
|
||
namespace CoreLibs\Language;
|
||
|
||
use CoreLibs\Language\Core\FileReader;
|
||
use CoreLibs\Language\Core\GetTextReader;
|
||
|
||
class L10n
|
||
{
|
||
/** @var string the default fallback encoding if nothing is set */
|
||
public const DEFAULT_CHARSET = 'UTF-8';
|
||
/** @var string the current locale */
|
||
private string $locale = '';
|
||
/** @var string the SET locale as WHERE the domain file is */
|
||
private string $locale_set = '';
|
||
/** @var string the default selected/active domain */
|
||
private string $domain = '';
|
||
/** @var string encoding, as from locale or set from outside */
|
||
private string $override_encoding = self::DEFAULT_CHARSET;
|
||
/** @var string encoding set during the parse Locale */
|
||
private string $encoding = '';
|
||
/** @var array<string,array<string,GetTextReader>> locale > domain = translator */
|
||
private array $domains = [];
|
||
/** @var array<string,string> bound paths for domains */
|
||
private array $paths = ['' => './'];
|
||
|
||
// files
|
||
/** @var string the full path to the mo file to loaded */
|
||
private string $mofile = '';
|
||
/** @var string base path to search level */
|
||
private string $base_locale_path = '';
|
||
/** @var string dynamic set path to where the mo file is actually */
|
||
private string $base_content_path = '';
|
||
|
||
// errors
|
||
/** @var bool if load of mo file was unsuccessful */
|
||
private bool $load_failure = false;
|
||
|
||
// object holders
|
||
/** @var FileReader|bool reader class for file reading, false for short circuit */
|
||
private FileReader|bool $input = false;
|
||
/** @var GetTextReader reader class for MO data */
|
||
private GetTextReader|null $l10n = null;
|
||
/**
|
||
* @static
|
||
* @var L10n self class
|
||
*/
|
||
private static L10n $instance;
|
||
|
||
/**
|
||
* class constructor call for language getstring
|
||
* if locale is not empty will load translation
|
||
* else getTranslator needs to be called
|
||
*
|
||
* @param string $locale language name, default empty string
|
||
* will return self instance
|
||
* @param string $domain override CONTENT_PATH . $encoding name for mo file
|
||
* @param string $path path, if empty fallback on default internal path
|
||
* @param string $encoding Optional encoding, should be set if locale has
|
||
* no encoding, defaults to UTF-8
|
||
*/
|
||
public function __construct(
|
||
string $locale = '',
|
||
string $domain = '',
|
||
string $path = '',
|
||
string $encoding = ''
|
||
) {
|
||
// auto load language only if at least locale and domain is set
|
||
// New: path must be set too, or we fall through
|
||
if (!empty($locale) && !empty($domain) && empty($path)) {
|
||
/** @deprecated if locale and domain are set, path must be set too */
|
||
trigger_error(
|
||
'Empty path parameter is no longer allowed if locale and domain are set',
|
||
E_USER_DEPRECATED
|
||
);
|
||
}
|
||
if (!empty($locale) && !empty($domain) && !empty($path)) {
|
||
// check hack if domain and path is switched
|
||
// Note this can be removed in future versions
|
||
if (strstr($domain, DIRECTORY_SEPARATOR) !== false) {
|
||
/** @deprecated domain must be 2nd and path must be third parameter */
|
||
trigger_error(
|
||
'L10n constructor parameter switch is no longer supported. domain is 2nd, path is 3rd parameter',
|
||
E_USER_DEPRECATED
|
||
);
|
||
$_domain = $path;
|
||
$path = $domain;
|
||
$domain = $_domain;
|
||
}
|
||
$this->getTranslator($locale, $domain, $path, $encoding);
|
||
}
|
||
}
|
||
|
||
/**
|
||
* Returns the singleton L10n object.
|
||
* For function wrapper use
|
||
*
|
||
* @return L10n object
|
||
*/
|
||
public static function getInstance(): L10n
|
||
{
|
||
if (empty(self::$instance)) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* Loads global localization functions.
|
||
* prefixed with double underscore
|
||
* eg: gettext -> __gettext
|
||
*/
|
||
public static function loadFunctions(): void
|
||
{
|
||
require_once __DIR__ . '/l10n_functions.php';
|
||
}
|
||
|
||
/**
|
||
* loads the mo file base on path, locale and domain set
|
||
*
|
||
* @param string $locale language name, if not set, try previous set
|
||
* @param string $domain set name for mo file, if not set, try previous set
|
||
* @param string $path path, if not set try to get from paths array, else self
|
||
* @param string $override_encoding if locale does not env encoding set, use this one
|
||
* @return GetTextReader the main gettext reader object
|
||
*/
|
||
public function getTranslator(
|
||
string $locale = '',
|
||
string $domain = '',
|
||
string $path = '',
|
||
string $override_encoding = '',
|
||
): GetTextReader {
|
||
// set local if not from parameter
|
||
if (empty($locale)) {
|
||
$locale = $this->locale;
|
||
}
|
||
// set domain if not given
|
||
if (empty($domain)) {
|
||
$domain = $this->domain;
|
||
}
|
||
// override encoding for unset
|
||
if (!empty($override_encoding)) {
|
||
$this->override_encoding = $override_encoding;
|
||
}
|
||
// store old settings
|
||
$old_mofile = $this->mofile;
|
||
$old_lang = $this->locale;
|
||
$old_lang_set = $this->locale_set;
|
||
$old_domain = $this->domain;
|
||
$old_encoding = $this->encoding;
|
||
$old_base_locale_path = $this->base_locale_path;
|
||
$old_base_content_path = $this->base_content_path;
|
||
|
||
// if path is a dir
|
||
// 1) from a previous set domain
|
||
// 2) from method option as is
|
||
// 3) fallback if BASE/INCLUDES/LOCALE set
|
||
// 4) current dir
|
||
if (!empty($this->paths[$domain]) && is_dir($this->paths[$domain])) {
|
||
$this->base_locale_path = $this->paths[$domain];
|
||
} elseif (is_dir($path)) {
|
||
$this->base_locale_path = $path;
|
||
} elseif (
|
||
defined('BASE') && defined('INCLUDES') && defined('LOCALE')
|
||
) {
|
||
/** @deprecated Do not use this anymore, define path on class load */
|
||
trigger_error(
|
||
'parameter $path must be set. Setting via BASE, INCLUDES and LOCALE constants is deprecated',
|
||
E_USER_DEPRECATED
|
||
);
|
||
// set fallback base path if constant set
|
||
$this->base_locale_path = BASE . INCLUDES . LOCALE;
|
||
} else {
|
||
$this->base_locale_path = './';
|
||
}
|
||
// now we loop over lang compositions to get the base path
|
||
// then we check
|
||
$locales = $this->listLocales($locale);
|
||
$encoding = $this->getEncodingFromLocale($locale);
|
||
foreach ($locales as $_locale) {
|
||
$this->base_content_path = $_locale . DIRECTORY_SEPARATOR
|
||
. 'LC_MESSAGES' . DIRECTORY_SEPARATOR;
|
||
$this->mofile = $this->base_locale_path
|
||
. $this->base_content_path
|
||
. $domain . '.mo';
|
||
if (file_exists($this->mofile)) {
|
||
$this->locale_set = $_locale;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// check if get a readable mofile
|
||
if (is_readable($this->mofile)) {
|
||
// locale and domain current wanted
|
||
$this->locale = $locale;
|
||
$this->encoding = $encoding;
|
||
$this->domain = $domain;
|
||
// set empty domains path with current locale
|
||
if (empty($this->domains[$locale])) {
|
||
$this->domains[$locale] = [];
|
||
}
|
||
// store current base path (without locale, etc)
|
||
if (empty($this->paths[$domain])) {
|
||
$this->paths[$domain] = $this->base_locale_path;
|
||
}
|
||
// file reader and mo reader
|
||
$this->input = new FileReader($this->mofile);
|
||
$this->l10n = new GetTextReader($this->input);
|
||
// if short circuit is true, we failed to have a translator loaded
|
||
$this->load_failure = $this->l10n->getShortCircuit();
|
||
// below is not used at the moment, but can be to avoid reloading
|
||
$this->domains[$this->locale][$domain] = $this->l10n;
|
||
} elseif (!empty($old_mofile)) {
|
||
// mo file not readable
|
||
$this->load_failure = true;
|
||
// else fall back to the old ones
|
||
$this->mofile = $old_mofile;
|
||
$this->locale = $old_lang;
|
||
$this->locale_set = $old_lang_set;
|
||
$this->encoding = $old_encoding;
|
||
$this->domain = $old_domain;
|
||
$this->base_locale_path = $old_base_locale_path;
|
||
$this->base_content_path = $old_base_content_path;
|
||
} else {
|
||
// mo file not readable, no previous mo file set, set short circuit
|
||
$this->load_failure = true;
|
||
// dummy
|
||
$this->l10n = new GetTextReader($this->input);
|
||
}
|
||
// if this is still null here, we abort
|
||
if ($this->l10n === null) {
|
||
throw new \RuntimeException(
|
||
"Could not create CoreLibs\Language\Core\GetTextReader object",
|
||
E_USER_ERROR
|
||
);
|
||
}
|
||
return $this->l10n;
|
||
}
|
||
|
||
/**
|
||
* return current set GetTextReader or return the one for given
|
||
* domain name if set
|
||
* This can be used to access all the public methods from the
|
||
* GetTextReader
|
||
*
|
||
* @param string $domain optional domain name
|
||
* @return GetTextReader
|
||
*/
|
||
public function getTranslatorClass(string $domain = ''): GetTextReader
|
||
{
|
||
if (!empty($domain) && !empty($this->domains[$this->locale][$domain])) {
|
||
return $this->domains[$this->locale][$domain];
|
||
}
|
||
// if null return short circuit version
|
||
if ($this->l10n === null) {
|
||
return new GetTextReader($this->input);
|
||
}
|
||
return $this->l10n;
|
||
}
|
||
|
||
/**
|
||
* Extract encoding from Locale, or fallback to override one if not set
|
||
*
|
||
* @param string $locale
|
||
* @return string
|
||
*/
|
||
private function getEncodingFromLocale(string $locale): string
|
||
{
|
||
// extract charset from $locale
|
||
// if not set get override encoding
|
||
preg_match('/(?:\\.(?P<charset>[-A-Za-z0-9_]+))/', $locale, $matches);
|
||
return $matches['charset'] ?? $this->override_encoding;
|
||
}
|
||
|
||
/**
|
||
* Get the local as array same to the GetLocale::setLocale return
|
||
* This does not set from outside, but only what is set in the l10n class
|
||
*
|
||
* @return array{locale: string, lang: string, lang_short: string, domain: string, encoding: string, path: string}
|
||
*/
|
||
public function getLocaleAsArray(): array
|
||
{
|
||
$locale = L10n::parseLocale($this->getLocale());
|
||
return [
|
||
'locale' => $this->getLocale(),
|
||
'lang' => ($locale['lang'] ?? '')
|
||
. (!empty($locale['country']) ? '_' . $locale['country'] : ''),
|
||
'lang_short' => $locale['lang'] ?? '',
|
||
'domain' => $this->getDomain(),
|
||
'encoding' => $this->getEncoding(),
|
||
'path' => $this->getBaseLocalePath(),
|
||
];
|
||
}
|
||
|
||
/**
|
||
* parse the locale string for further processing
|
||
*
|
||
* @param string $locale Locale to parse
|
||
* @return array<string,string|null> array with lang, country, charset, modifier
|
||
*/
|
||
public static function parseLocale(string $locale = ''): array
|
||
{
|
||
preg_match(
|
||
// language code
|
||
'/^(?P<lang>[a-z]{2,3})'
|
||
// country code
|
||
. '(?:_(?P<country>[A-Z]{2}))?'
|
||
// charset
|
||
. '(?:\\.(?P<charset>[-A-Za-z0-9_]+))?'
|
||
// @ modifier
|
||
. '(?:@(?P<modifier>[-A-Za-z0-9_]+))?$/',
|
||
$locale,
|
||
$matches
|
||
);
|
||
return [
|
||
'lang' => $matches['lang'] ?? null,
|
||
'country' => $matches['country'] ?? null,
|
||
'charset' => $matches['charset'] ?? null,
|
||
'modifier' => $matches['modifier'] ?? null,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* original:
|
||
* vendor/phpmyadmin/motranslator/src/Loader.php
|
||
*
|
||
* Returns array with all possible locale combinations based on the
|
||
* given locale name
|
||
*
|
||
* I.e. for sr_CS.UTF-8@latin, look through all of
|
||
* sr_CS.UTF-8@latin, sr_CS@latin, sr@latin, sr_CS.UTF-8, sr_CS, sr.
|
||
*
|
||
* @param string $locale Locale string
|
||
* @return array<string> List of locale path parts that can be possible
|
||
*/
|
||
public static function listLocales(string $locale): array
|
||
{
|
||
$locale_list = [];
|
||
|
||
if (empty($locale)) {
|
||
return $locale_list;
|
||
}
|
||
// is matching regex
|
||
$locale_detail = L10n::parseLocale($locale);
|
||
// all null = nothing mached, return locale as is
|
||
if ($locale_detail === array_filter($locale_detail, 'is_null')) {
|
||
return [$locale];
|
||
}
|
||
// write to innteral vars
|
||
$lang = $locale_detail['lang'];
|
||
$country = $locale_detail['country'];
|
||
$charset = $locale_detail['charset'];
|
||
$modifier = $locale_detail['modifier'];
|
||
// we need to add all possible cominations from not null set
|
||
// entries to the list, from longest to shortest
|
||
// %s_%s.%s@%s (lang _ country . encoding @ suffix)
|
||
// %s_%s@%s (lang _ country @ suffix)
|
||
// %s@%s (lang @ suffix)
|
||
// %s_%s.%s (lang _ country . encoding)
|
||
// %s_%s (lang _ country)
|
||
// %s (lang)
|
||
|
||
// if lang is set
|
||
if ($lang) {
|
||
// modifier group
|
||
if ($modifier) {
|
||
if ($country) {
|
||
if ($charset) {
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s_%s.%s@%s', $lang, $country, $charset, $modifier)
|
||
);
|
||
}
|
||
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s_%s@%s', $lang, $country, $modifier)
|
||
);
|
||
} elseif ($charset) {
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s.%s@%s', $lang, $charset, $modifier)
|
||
);
|
||
}
|
||
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s@%s', $lang, $modifier)
|
||
);
|
||
}
|
||
// country group
|
||
if ($country) {
|
||
if ($charset) {
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s_%s.%s', $lang, $country, $charset)
|
||
);
|
||
}
|
||
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s_%s', $lang, $country)
|
||
);
|
||
} elseif ($charset) {
|
||
array_push(
|
||
$locale_list,
|
||
sprintf('%s.%s', $lang, $charset)
|
||
);
|
||
}
|
||
// lang only
|
||
array_push($locale_list, $lang);
|
||
}
|
||
|
||
// If the locale name doesn't match POSIX style, just include it as-is.
|
||
if (!in_array($locale, $locale_list)) {
|
||
array_push($locale_list, $locale);
|
||
}
|
||
|
||
return $locale_list;
|
||
}
|
||
|
||
/**
|
||
* tries to detect the locale set in the following order:
|
||
* - globals: LOCALE
|
||
* - globals: LANG
|
||
* - env: LC_ALL
|
||
* - env: LC_MESSAGES
|
||
* - env: LANG
|
||
* if nothing set, returns 'en' as default
|
||
*
|
||
* @return string
|
||
*/
|
||
public static function detectLocale(): string
|
||
{
|
||
// globals
|
||
foreach (['LOCALE', 'LANG'] as $global) {
|
||
if (!empty($GLOBALS[$global])) {
|
||
return $GLOBALS[$global];
|
||
}
|
||
}
|
||
// enviroment
|
||
foreach (['LC_ALL', 'LC_MESSAGES', 'LANG'] as $env) {
|
||
$locale = getenv($env);
|
||
if ($locale !== false && !empty($locale)) {
|
||
return $locale;
|
||
}
|
||
}
|
||
return 'en';
|
||
}
|
||
|
||
/************
|
||
* INTERNAL VAR SET/GET
|
||
*/
|
||
|
||
/**
|
||
* Sets the path for a domain.
|
||
* must be set before running getTranslator (former l10nReloadMOfile)
|
||
*
|
||
* @param string $domain Domain name
|
||
* @param string $path Path where to find locales
|
||
*/
|
||
public function setTextDomain(string $domain, string $path): void
|
||
{
|
||
$this->paths[$domain] = $path;
|
||
}
|
||
|
||
/**
|
||
* return set path for given domain
|
||
* if not found return false
|
||
*
|
||
* @param string $domain
|
||
* @return string|bool
|
||
*/
|
||
public function getTextDomain(string $domain)
|
||
{
|
||
return $this->paths[$domain] ?? false;
|
||
}
|
||
|
||
/**
|
||
* sets the default domain.
|
||
*
|
||
* @param string $domain Domain name
|
||
*/
|
||
public function setDomain(string $domain): void
|
||
{
|
||
$this->domain = $domain;
|
||
}
|
||
|
||
/**
|
||
* return current set domain name
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getDomain(): string
|
||
{
|
||
return $this->domain;
|
||
}
|
||
|
||
/**
|
||
* sets a requested locale.
|
||
*
|
||
* @param string $locale Locale name
|
||
* @return string Set or current locale
|
||
*/
|
||
public function setLocale(string $locale): string
|
||
{
|
||
if (!empty($locale)) {
|
||
$this->locale = $locale;
|
||
}
|
||
return $this->locale;
|
||
}
|
||
|
||
/**
|
||
* get current set locale (want locale)
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getLocale(): string
|
||
{
|
||
return $this->locale;
|
||
}
|
||
|
||
/**
|
||
* current set locale where mo file is located
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getLocaleSet(): string
|
||
{
|
||
return $this->locale_set;
|
||
}
|
||
|
||
/**
|
||
* Set override encoding
|
||
*
|
||
* @param string $encoding
|
||
* @return void
|
||
*/
|
||
public function setOverrideEncoding(string $encoding): void
|
||
{
|
||
$this->override_encoding = $encoding;
|
||
}
|
||
|
||
/**
|
||
* return current set override encoding
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getOverrideEncoding(): string
|
||
{
|
||
return $this->override_encoding;
|
||
}
|
||
|
||
/**
|
||
* Current set encoding
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getEncoding(): string
|
||
{
|
||
return $this->encoding;
|
||
}
|
||
|
||
/**
|
||
* get current set language
|
||
*
|
||
* @return string current set language string
|
||
* @deprecated Use getLocale()
|
||
*/
|
||
public function __getLang(): string
|
||
{
|
||
return $this->getLocale();
|
||
}
|
||
|
||
/**
|
||
* get current set mo file
|
||
*
|
||
* @return string current set mo language file
|
||
*/
|
||
public function getMoFile(): string
|
||
{
|
||
return $this->mofile;
|
||
}
|
||
|
||
/**
|
||
* get current set mo file
|
||
*
|
||
* @return string current set mo language file
|
||
* @deprecated Use getMoFile()
|
||
*/
|
||
public function __getMoFile(): string
|
||
{
|
||
return $this->getMoFile();
|
||
}
|
||
|
||
/**
|
||
* get the current base path in which we search
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getBaseLocalePath(): string
|
||
{
|
||
return $this->base_locale_path;
|
||
}
|
||
|
||
/**
|
||
* the path below the base path to where the mo file is located
|
||
*
|
||
* @return string
|
||
*/
|
||
public function getBaseContentPath(): string
|
||
{
|
||
return $this->base_content_path;
|
||
}
|
||
|
||
/**
|
||
* get the current load error status
|
||
* if true then the mo file failed to load
|
||
*
|
||
* @return bool
|
||
*/
|
||
public function getLoadError(): bool
|
||
{
|
||
return $this->load_failure;
|
||
}
|
||
|
||
/************
|
||
* TRANSLATION METHODS
|
||
*/
|
||
|
||
/**
|
||
* translates a string and returns translated text
|
||
*
|
||
* @param string $text text to translate
|
||
* @return string translated text
|
||
*/
|
||
public function __(string $text): string
|
||
{
|
||
// fallback passthrough
|
||
if ($this->l10n === null) {
|
||
return $text;
|
||
}
|
||
return $this->l10n->translate($text);
|
||
}
|
||
|
||
/**
|
||
* prints translated string out to the screen
|
||
* @param string $text text to translate
|
||
* @return void has no return
|
||
* @deprecated use echo __() instead
|
||
*/
|
||
public function __e(string $text): void
|
||
{
|
||
// fallback passthrough
|
||
if ($this->l10n === null) {
|
||
echo $text;
|
||
return;
|
||
}
|
||
echo $this->l10n->translate($text);
|
||
}
|
||
|
||
/**
|
||
* Return the plural form.
|
||
*
|
||
* @param string $single string for single word
|
||
* @param string $plural string for plural word
|
||
* @param int $number number value
|
||
* @return string translated plural string
|
||
*/
|
||
public function __n(string $single, string $plural, int $number): string
|
||
{
|
||
// in case nothing got set yet, this is fallback
|
||
if ($this->l10n === null) {
|
||
return $number > 1 ? $plural : $single;
|
||
}
|
||
return $this->l10n->ngettext($single, $plural, $number);
|
||
}
|
||
|
||
/**
|
||
* context translation via msgctxt
|
||
*
|
||
* @param string $context context string
|
||
* @param string $text text to translate
|
||
* @return string
|
||
*/
|
||
public function __p(string $context, string $text): string
|
||
{
|
||
if ($this->l10n === null) {
|
||
return $text;
|
||
}
|
||
return $this->l10n->pgettext($context, $text);
|
||
}
|
||
|
||
/**
|
||
* context translation via msgctxt
|
||
*
|
||
* @param string $context context string
|
||
* @param string $single string for single word
|
||
* @param string $plural string for plural word
|
||
* @param int $number number value
|
||
* @return string
|
||
*/
|
||
public function __np(string $context, string $single, string $plural, int $number): string
|
||
{
|
||
if ($this->l10n === null) {
|
||
return $number > 1 ? $plural : $single;
|
||
}
|
||
return $this->l10n->npgettext($context, $single, $plural, $number);
|
||
}
|
||
|
||
// alias functions to mimic gettext calls
|
||
|
||
/**
|
||
* alias for gettext,
|
||
* calls __
|
||
*
|
||
* @param string $text
|
||
* @return string
|
||
* @deprecated Use __()
|
||
*/
|
||
public function gettext(string $text): string
|
||
{
|
||
return $this->__($text);
|
||
}
|
||
|
||
/**
|
||
* alias for ngettext
|
||
* calls __n
|
||
*
|
||
* @param string $single
|
||
* @param string $plural
|
||
* @param int $number
|
||
* @return string
|
||
* @deprecated Use __n()
|
||
*/
|
||
public function ngettext(string $single, string $plural, int $number): string
|
||
{
|
||
return $this->__n($single, $plural, $number);
|
||
}
|
||
|
||
// TODO: dgettext(string $domain, string $message): string
|
||
// TODO: dngettext(string $domain, string $singular, string $plural, int $count): string
|
||
// TODO: dpgettext(string $domain, string $message, int $category): string
|
||
// TODO: dpngettext(string $domain, string $singular, string $plural, int $count, int $category): string
|
||
}
|
||
|
||
// __END__
|