Files
opensourcepos/tests/LanguageFilesTest.php
BudsieBuds 9a304c0183 refactor(i18n): standardize language catalogs, enforce dynamic fallback, and add test coverage
- Replace untranslated English strings with empty fallback ('') across all non-English catalogs and sort keys alphabetically (closes #4783, closes #4660)
- Restore missing Sales.no_sales_to_display in English while preserving existing translations (closes #4794, references #4637, #4795)
- Register Georgian (ka) and Swahili (sw-KE, sw-TZ) locales, normalize Croatian to hr per BCP 47, and fix datepicker long month keys (partially addresses #4476, references #3891)
- Hardcode invariant brand constants (OSPOS, website, copyrights) in login/footer views, remove from catalogs, and standardize Latin OSPOS branding
- Fix English catalog typos and maintain consistent British English variants in en-GB
- Add LanguageFilesTest with 4,418 automated tests for syntax, key parity, sorting, and bidirectional locale registrations
- Update AGENTS.md with localization guidelines and development best practices
2026-11-04 15:23:50 +01:00

130 lines
4.4 KiB
PHP

<?php
namespace Tests;
use CodeIgniter\Test\CIUnitTestCase;
use ParseError;
use PHPUnit\Framework\Attributes\DataProvider;
class LanguageFilesTest extends CIUnitTestCase
{
/**
* @var array<string, list<string>>
*/
private static array $englishKeysCache = [];
/**
* @return array<string, array{0: string}>
*/
public static function languageFileProvider(): array
{
$cases = [];
$languageRoot = realpath(__DIR__ . '/../app/Language');
foreach (glob($languageRoot . '/*', GLOB_ONLYDIR) as $localeDir) {
foreach (glob($localeDir . '/*.php') as $file) {
$cases[substr($file, strlen($languageRoot) + 1)] = [$file];
}
}
return $cases;
}
#[DataProvider('languageFileProvider')]
public function testLanguageFile_ParsesToArray(string $file): void
{
try {
$result = include $file;
} catch (ParseError $parseError) {
$this->fail("Failed to parse {$file}: {$parseError->getMessage()}");
}
$this->assertIsArray($result, "{$file} must return an array");
}
#[DataProvider('languageFileProvider')]
public function testLanguageFile_MatchesEnglishKeys(string $file): void
{
$baseName = basename($file);
if (! isset(self::$englishKeysCache[$baseName])) {
$enFile = realpath(__DIR__ . '/../app/Language/en/' . $baseName);
self::$englishKeysCache[$baseName] = array_keys(include $enFile);
}
$currentKeys = array_keys(include $file);
$expectedKeys = self::$englishKeysCache[$baseName];
$extra = array_diff($currentKeys, $expectedKeys);
$this->assertEmpty($extra, "{$file} has extra keys: " . implode(', ', $extra));
$missing = array_diff($expectedKeys, $currentKeys);
$this->assertEmpty($missing, "{$file} is missing keys: " . implode(', ', $missing));
}
#[DataProvider('languageFileProvider')]
public function testLanguageFile_RequiresAlphabeticalOrder(string $file): void
{
$baseName = basename($file);
if ($baseName === 'Calendar.php') {
$this->assertTrue(true);
return;
}
$keys = array_keys(include $file);
$sortedKeys = $keys;
sort($sortedKeys, SORT_STRING);
$this->assertSame($sortedKeys, $keys, "Keys in {$file} must be sorted alphabetically");
}
#[DataProvider('languageFileProvider')]
public function testLanguageFile_RejectsDuplicateKeys(string $file): void
{
$content = file_get_contents($file);
$lines = explode("\n", $content);
$seen = [];
$duplicates = [];
foreach ($lines as $line) {
if (preg_match('/^\s*\'([^\']+)\'\s*=>/', $line, $matches)) {
$key = $matches[1];
if (isset($seen[$key])) {
$duplicates[] = $key;
}
$seen[$key] = true;
}
}
$this->assertEmpty($duplicates, "Duplicate keys in {$file}: " . implode(', ', $duplicates));
}
public function testSupportedLocales_MatchesLanguageDirectories(): void
{
$appConfig = config('Config\App');
$languageRoot = realpath(__DIR__ . '/../app/Language');
$dirs = array_map('basename', glob($languageRoot . '/*', GLOB_ONLYDIR));
$missing = array_diff($dirs, $appConfig->supportedLocales);
$this->assertEmpty($missing, 'Config\App::$supportedLocales missing directories: ' . implode(', ', $missing));
$extra = array_diff($appConfig->supportedLocales, $dirs);
$this->assertEmpty($extra, 'Config\App::$supportedLocales has non-existent directories: ' . implode(', ', $extra));
}
public function testGetLanguages_MatchesLanguageDirectories(): void
{
helper('locale');
$languages = get_languages();
$registeredLocales = array_map(static fn ($key) => explode(':', $key)[0], array_keys($languages));
$languageRoot = realpath(__DIR__ . '/../app/Language');
$dirs = array_map('basename', glob($languageRoot . '/*', GLOB_ONLYDIR));
$missing = array_diff($dirs, $registeredLocales);
$this->assertEmpty($missing, 'locale_helper::get_languages() missing directories: ' . implode(', ', $missing));
$extra = array_diff($registeredLocales, $dirs);
$this->assertEmpty($extra, 'locale_helper::get_languages() has non-existent directories: ' . implode(', ', $extra));
}
}