You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pixelfed/app/Util/Localization/Localization.php

145 lines
3.8 KiB
PHP

<?php
namespace App\Util\Localization;
use Illuminate\Support\Arr;
class Localization
{
/**
* Legacy two-letter (and legacy region) language codes mapped to the
* current locale-coded folder names under lang/.
* Added September 2026 - DELETE AFTER COMMUNICATION WITH ADMINS
*
* @var array<string, string>
*/
const LEGACY_LOCALE_MAP = [
'af' => 'af-ZA',
'ar' => 'ar-SA',
'bn' => 'bn-BD',
'bs' => 'bs-BA',
'ca' => 'ca-ES',
'cs' => 'cs-CZ',
'cy' => 'cy-GB',
'da' => 'da-DK',
'de' => 'de-DE',
'el' => 'el-GR',
'en' => 'en-US',
'eo' => 'eo-UY',
'es' => 'es-ES',
'eu' => 'eu-ES',
'fa' => 'fa-IR',
'fi' => 'fi-FI',
'fr' => 'fr-FR',
'gd' => 'gd-GB',
'gl' => 'gl-ES',
'he' => 'he-IL',
'hi' => 'hi-IN',
'hr' => 'hr-HR',
'hu' => 'hu-HU',
'id' => 'id-ID',
'it' => 'it-IT',
'ja' => 'ja-JP',
'ko' => 'ko-KR',
'me' => 'me-ME',
'mk' => 'mk-MK',
'ms' => 'ms-MY',
'nl' => 'nl-NL',
'no' => 'no-NO',
'oc' => 'oc-FR',
'pl' => 'pl-PL',
'pt' => 'pt-PT',
'ro' => 'ro-RO',
'ru' => 'ru-RU',
'sk' => 'sk-SK',
'sr' => 'sr-CS',
'sv' => 'sv-SE',
'th' => 'th-TH',
'tr' => 'tr-TR',
'uk' => 'uk-UA',
'vi' => 'vi-VN',
'zh-cn' => 'zh-CN',
'zh-tw' => 'zh-TW',
];
/**
* Normalize a configured locale to a current locale-coded value.
*
* Empty input returns "en-US"; a known legacy short code is mapped;
* anything else (already-current or custom codes) is returned unchanged so
* the framework's own fallback_locale still applies. Always returns a
* plain string, so it is safe to use in config values that get cached.
*/
public static function normalizeLocale(?string $locale): string
{
$locale = is_string($locale) ? trim($locale) : '';
if ($locale === '') {
return 'en-US';
}
return self::LEGACY_LOCALE_MAP[strtolower($locale)] ?? $locale;
}
/**
* List of available UI language codes.
*
* Reads the static manifest generated by `php artisan i18n:export`
* (lang/locales.json) so the list is deterministic and never served from
* a runtime cache that can go stale. Falls back to scanning the lang/
* directory if the manifest is missing.
*
* @return array<int, string>
*/
public static function languages()
{
return static::localesFromManifest() ?? static::localesFromScan();
}
/**
* Full locale metadata (code, name, nativeName) from the manifest,
* sorted by display name. Empty array if the manifest is missing.
*
* @return array<int, array{code: string, name: string, nativeName: string}>
*/
public static function locales(): array
{
$path = lang_path('locales.json');
if (! is_file($path)) {
return [];
}
$data = json_decode((string) file_get_contents($path), true);
return is_array($data) ? $data : [];
}
/**
* @return array<int, string>|null
*/
protected static function localesFromManifest(): ?array
{
$locales = static::locales();
if ($locales === []) {
return null;
}
return array_values(array_filter(array_map(
fn ($l) => $l['code'] ?? null,
$locales
)));
}
/**
* @return array<int, string>
*/
protected static function localesFromScan(): array
{
$dir = lang_path();
return Arr::flatten(array_diff(scandir($dir), ['..', '.', 'vendor', '.DS_Store', 'locales.json']));
}
}