51 public const FALLBACK_LANGUAGE =
"eng";
61 $path = \pocketmine\LOCALE_DATA_PATH;
65 $allFiles = scandir($path, SCANDIR_SORT_NONE);
67 if($allFiles !==
false){
68 $files = array_filter($allFiles,
function(
string $filename) :
bool{
69 return str_ends_with($filename,
".ini");
74 foreach($files as $file){
76 $code = explode(
".", $file, limit: 2)[0];
78 if(isset($strings[KnownTranslationKeys::LANGUAGE_NAME])){
79 $result[$code] = $strings[KnownTranslationKeys::LANGUAGE_NAME];
81 }
catch(LanguageNotFoundException $e){
90 throw new LanguageNotFoundException(
"Language directory $path does not exist or is not a directory");
93 protected string $langName;
98 protected array $lang = [];
103 protected array $fallbackLang = [];
108 public function __construct(
string $lang, ?
string $path =
null,
string $fallback = self::FALLBACK_LANGUAGE){
109 $this->langName = strtolower($lang);
112 $path = \pocketmine\LOCALE_DATA_PATH;
115 $this->lang = self::loadLang($path, $this->langName);
116 $this->fallbackLang = self::loadLang($path, $fallback);
119 public function getName() : string{
123 public function getLang() : string{
124 return $this->langName;
131 protected static function loadLang(
string $path,
string $languageCode) : array{
132 $file = Path::join($path, $languageCode .
".ini");
133 if(file_exists($file)){
134 $strings = array_map(
'stripcslashes', Utils::assumeNotFalse(parse_ini_file($file,
false, INI_SCANNER_RAW),
"Missing or inaccessible required resource files"));
135 if(count($strings) > 0){
143 private function getUsedParameterCount(
string $rawString,
int $given) : int{
145 for($i = 0; $i < $given; $i++){
146 if(str_contains($rawString,
"{%$i}")){
150 return $highestIndex + 1;
156 public function translateString(
string $str, array $params = [], ?
string $onlyPrefix =
null,
int &$untranslatedParameterCount = 0) : string{
157 $baseText = $this->internalGet($str);
158 $parameterCount = count($params);
159 if($baseText !==
null){
160 if($onlyPrefix !==
null && !str_starts_with($str, $onlyPrefix)){
161 $untranslatedParameterCount = $this->getUsedParameterCount($baseText, $parameterCount);
165 $baseText = $this->parseTranslation($str, $onlyPrefix, $parameterCount);
166 $untranslatedParameterCount = $parameterCount;
169 foreach(Utils::promoteKeys($params) as $i => $p){
170 $replacement = $p instanceof Translatable ? $this->translate($p) : (string) $p;
171 $baseText = str_replace(
"{%$i}", $replacement, $baseText);
177 public function translate(Translatable $c) : string{
178 $baseText = $this->internalGet($c->getText());
179 if($baseText ===
null){
180 $baseText = $this->parseTranslation($c->getText());
183 foreach(Utils::promoteKeys($c->getParameters()) as $i => $p){
184 $replacement = $p instanceof Translatable ? $this->translate($p) : $p;
185 $baseText = str_replace(
"{%$i}", $replacement, $baseText);
191 protected function internalGet(
string $id) : ?string{
192 return $this->lang[$id] ?? $this->fallbackLang[$id] ?? null;
195 public function get(
string $id) : string{
196 return $this->internalGet($id) ?? $id;
207 private function replaceTranslationKey(
string $replaceString, ?
string $onlyPrefix,
int &$untranslatedParameterCount,
int $givenParameterCount) : string{
208 if(($t = $this->internalGet(substr($replaceString, 1))) !== null){
209 if($onlyPrefix !==
null && strpos($replaceString, $onlyPrefix) !== 1){
210 $newString = $replaceString;
211 $untranslatedParameterCount = max($untranslatedParameterCount, $this->getUsedParameterCount($t, $givenParameterCount));
216 $newString = $replaceString;
217 $untranslatedParameterCount = $givenParameterCount;
235 protected function parseTranslation(
string $text, ?
string $onlyPrefix =
null,
int &$parameterCount = 0) : string{
236 $givenParameterCount = $parameterCount;
237 $untranslatedParameterCount = 0;
240 $replaceString =
null;
242 $len = strlen($text);
243 for($i = 0; $i < $len; ++$i){
245 if($replaceString !==
null){
248 ($ord >= 0x30 && $ord <= 0x39)
249 || ($ord >= 0x41 && $ord <= 0x5a)
250 || ($ord >= 0x61 && $ord <= 0x7a) ||
251 $c ===
"." || $c ===
"-"
253 $replaceString .= $c;
255 $newString .= $this->replaceTranslationKey($replaceString, $onlyPrefix, $untranslatedParameterCount, $givenParameterCount);
256 $replaceString =
null;
271 if($replaceString !==
null){
272 $newString .= $this->replaceTranslationKey($replaceString, $onlyPrefix, $untranslatedParameterCount, $givenParameterCount);
275 $parameterCount = $untranslatedParameterCount;