diff --git a/Admin/admin.php b/Admin/admin.php index 0c51f70d..f7ce709b 100644 --- a/Admin/admin.php +++ b/Admin/admin.php @@ -38,7 +38,8 @@ include_once("../GameEngine/Admin/csrf.php"); // ─── CORE INCLUDES ─────────────────────────────────────────────────────────── include_once("../GameEngine/config.php"); include_once("../GameEngine/Database.php"); -include_once("../GameEngine/Lang/" . LANG . ".php"); +require_once __DIR__ . "/../GameEngine/Lang/loader.php"; +tz_load_language(LANG); include_once("../GameEngine/Admin/database.php"); // Helperul care descopera pachetele grafice din gpack/ (tz_available_gpacks). @@ -982,4 +983,4 @@ body.app #menu li.sub ul li a:hover{color:#d97706!important} - \ No newline at end of file + diff --git a/GameEngine/BBCode.php b/GameEngine/BBCode.php index 6cbd0964..2ee40be6 100755 --- a/GameEngine/BBCode.php +++ b/GameEngine/BBCode.php @@ -21,7 +21,8 @@ ################################################################################# include_once("config.php"); -include_once("Lang/" . LANG . ".php"); +require_once __DIR__ . "/Lang/loader.php"; +tz_load_language(LANG); /** * Ensure input exists (avoid undefined variable issues) @@ -207,4 +208,4 @@ $bbcoded = preg_replace_callback( $bbcoded = preg_replace('/\[message\]/', '', $bbcoded); $bbcoded = preg_replace('/\[\/message\]/', '', $bbcoded); -?> \ No newline at end of file +?> diff --git a/GameEngine/Lang/loader.php b/GameEngine/Lang/loader.php new file mode 100644 index 00000000..ba33b552 --- /dev/null +++ b/GameEngine/Lang/loader.php @@ -0,0 +1,53 @@ + \ No newline at end of file +?> diff --git a/Templates/Ajax/mapscroll.tpl b/Templates/Ajax/mapscroll.tpl index e622e71d..6e45e622 100644 --- a/Templates/Ajax/mapscroll.tpl +++ b/Templates/Ajax/mapscroll.tpl @@ -25,7 +25,8 @@ ========================= */ session_start(); include_once('GameEngine/config.php'); -include_once("GameEngine/Lang/". LANG. ".php"); +require_once dirname(__DIR__, 2) . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); include_once("GameEngine/Generator.php"); include_once("GameEngine/Database.php"); @@ -278,4 +279,4 @@ while ($donnees = mysqli_fetch_assoc($result2)) { $i++; } -echo '[['. $map_js. ']'; \ No newline at end of file +echo '[['. $map_js. ']'; diff --git a/Templates/Ajax/mapscroll2.tpl b/Templates/Ajax/mapscroll2.tpl index 97cea6a3..dd06e4da 100644 --- a/Templates/Ajax/mapscroll2.tpl +++ b/Templates/Ajax/mapscroll2.tpl @@ -25,7 +25,8 @@ ========================= */ session_start(); include_once('GameEngine/config.php'); -include_once("GameEngine/Lang/". LANG. ".php"); +require_once dirname(__DIR__, 2) . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); include_once("GameEngine/Generator.php"); include_once("GameEngine/Database.php"); @@ -279,4 +280,4 @@ while ($donnees = mysqli_fetch_assoc($result2)) { $i++; } -echo '[['. $map_js. ']'; \ No newline at end of file +echo '[['. $map_js. ']'; diff --git a/anleitung.php b/anleitung.php index 6c448ca3..6dff856f 100644 --- a/anleitung.php +++ b/anleitung.php @@ -24,7 +24,8 @@ use App\Utils\AccessLogger; include_once("GameEngine/config.php"); include_once("GameEngine/Database.php"); -include_once("GameEngine/Lang/".LANG.".php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); AccessLogger::logRequest(); ?> @@ -163,4 +164,4 @@ include("Templates/Anleitung/4.tpl"); } - \ No newline at end of file + diff --git a/impressum.php b/impressum.php index cb9fbe37..4710bdfb 100644 --- a/impressum.php +++ b/impressum.php @@ -13,7 +13,8 @@ use App\Utils\AccessLogger; include_once("GameEngine/config.php"); include_once("GameEngine/Database.php"); -include_once("GameEngine/Lang/".LANG.".php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); AccessLogger::logRequest(); ?> @@ -183,4 +184,4 @@ AccessLogger::logRequest(); - \ No newline at end of file + diff --git a/index.php b/index.php index 5db20296..bfd1027b 100644 --- a/index.php +++ b/index.php @@ -50,7 +50,8 @@ else } include_once "GameEngine/Database.php"; -include_once "GameEngine/Lang/".LANG.".php"; +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); AccessLogger::logRequest(); ?> diff --git a/install/include/accounts.php b/install/include/accounts.php index 43a6adf1..f6a8539c 100644 --- a/install/include/accounts.php +++ b/install/include/accounts.php @@ -35,7 +35,8 @@ include_once($configFile); include_once("../../GameEngine/Database.php"); include_once("../../GameEngine/Admin/database.php"); - include_once("../../GameEngine/Lang/" . LANG . ".php"); + require_once dirname(__DIR__, 2) . "/GameEngine/Lang/loader.php"; + tz_load_language(LANG); // update Admin details first $gameConfig = file_get_contents($configFile); @@ -113,4 +114,4 @@ $gameinstall = 0; header("Location: ../index.php?s=5"); -?> \ No newline at end of file +?> diff --git a/manual.php b/manual.php index ff63e37b..85b4edbf 100644 --- a/manual.php +++ b/manual.php @@ -22,14 +22,8 @@ ################################################################################# include_once("GameEngine/config.php"); -// Load the interface language for the manual templates. Since #186 these -// templates use translation constants (OVERVIEW, TRIBE1, ...) instead of -// hardcoded English; without a language file every constant would be -// undefined and PHP 8.3 would fatal, leaving the in-game help iframe blank. -// en.php is loaded last as an idempotent fallback (tz_def) to fill any key -// missing from the active language. -include_once("GameEngine/Lang/" . LANG . ".php"); -include_once("GameEngine/Lang/en.php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); ?> @@ -84,4 +78,4 @@ else { ?> - \ No newline at end of file + diff --git a/notification/index.php b/notification/index.php index 01052f60..9a596d7b 100644 --- a/notification/index.php +++ b/notification/index.php @@ -17,8 +17,9 @@ ################################################################################# include("../GameEngine/config.php"); -include("../GameEngine/Lang/".LANG.".php"); -include("lang/".LANG.".php"); +require_once dirname(__DIR__) . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); +include(__DIR__ . "/lang/".LANG.".php"); if(T4_COMING==true){ ?> @@ -161,4 +162,4 @@ if(T4_COMING==true){ header("Location: ../index.php"); exit; } -?> \ No newline at end of file +?> diff --git a/password.php b/password.php index 1f505dff..2f7882e3 100644 --- a/password.php +++ b/password.php @@ -28,7 +28,8 @@ if (!file_exists('var/installed') && @opendir('install')) { } include_once("GameEngine/config.php"); -include_once("GameEngine/Lang/" . LANG . ".php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); include_once("GameEngine/Database.php"); include_once("GameEngine/Mailer.php"); include_once("GameEngine/Generator.php"); diff --git a/spielregeln.php b/spielregeln.php index 476f0740..89e9f622 100644 --- a/spielregeln.php +++ b/spielregeln.php @@ -17,7 +17,8 @@ use App\Utils\AccessLogger; include_once("GameEngine/config.php"); include_once("GameEngine/Database.php"); -include_once("GameEngine/Lang/".LANG.".php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); AccessLogger::logRequest(); ?> @@ -219,4 +220,4 @@ Displaying battle reports or messages in public without consent of both concerne - \ No newline at end of file + diff --git a/terms.php b/terms.php index c98cb85d..e9622e35 100644 --- a/terms.php +++ b/terms.php @@ -24,7 +24,8 @@ use App\Utils\AccessLogger; include_once("GameEngine/config.php"); include_once("GameEngine/Database.php"); -include_once("GameEngine/Lang/".LANG.".php"); +require_once __DIR__ . "/GameEngine/Lang/loader.php"; +tz_load_language(LANG); AccessLogger::logRequest(); ?> @@ -196,4 +197,4 @@ AccessLogger::logRequest(); - \ No newline at end of file + diff --git a/tests/LanguageLoaderTest.php b/tests/LanguageLoaderTest.php new file mode 100644 index 00000000..60c957a0 --- /dev/null +++ b/tests/LanguageLoaderTest.php @@ -0,0 +1,152 @@ + [ + 'tribe' => 'Romans', + 'welcome' => 'Welcome to Test Server', + ], + 'fr' => [ + 'tribe' => 'Romains', + 'welcome' => 'Bienvenue sur le Test Server!', + ], + 'it' => [ + 'tribe' => 'Romani', + 'welcome' => 'Benvenuto in Test Server', + 'fallback' => ['TRIBE7', 'Egyptians'], + ], + 'ro' => [ + 'tribe' => 'Romani', + 'welcome' => 'Bine ai venit pe Test Server', + 'fallback' => ['PUBLIC_WELCOME_TO', 'Welcome to'], + ], + 'zh' => [ + 'tribe' => '罗马', + 'welcome' => '欢迎来到 Test Server', + 'fallback' => ['TRIBE7', 'Egyptians'], + ], + ]; + + if (!isset($expected[$language])) { + fail_test('Unknown test language: ' . $language); + } + + define('SERVER_NAME', 'Test Server'); + define('USRNM_MIN_LENGTH', 3); + define('PW_MIN_LENGTH', 6); + define('COMMENCE', 0); + define('TS_THRESHOLD', 0); + define('CRANNY_CAPACITY', 100); + define('SPEED', 1); + + require dirname(__DIR__) . '/GameEngine/Lang/loader.php'; + $loaded = tz_load_language($language); + + assert_same($expected[$language]['tribe'], TRIBE1, $language . ': selected constants must win'); + assert_same( + $expected[$language]['welcome'], + $loaded['index'][0][1] ?? null, + $language . ': selected $lang values must survive the English fallback' + ); + assert_same($loaded, $GLOBALS['lang'], $language . ': loader must publish the merged global array'); + + if (isset($expected[$language]['fallback'])) { + [$constant, $value] = $expected[$language]['fallback']; + assert_same($value, constant($constant), $language . ': missing constant must fall back to English'); + } + + $fixtureDirectory = __DIR__ . '/fixtures/languages'; + $fixture = tz_load_language('xx', $fixtureDirectory); + assert_same( + 'English fallback', + $fixture['nested']['fallback_only'] ?? null, + $language . ': missing nested array value must fall back to English' + ); + assert_same( + 'Localized shared', + $fixture['nested']['shared'] ?? null, + $language . ': localized nested array value must override English' + ); + assert_same( + 'Localized value', + $fixture['nested']['localized_only'] ?? null, + $language . ': localized-only nested array value must be preserved' + ); + assert_same( + 'English fallback', + LANGUAGE_LOADER_FIXTURE_FALLBACK, + $language . ': fixture constant must fall back to English' + ); + assert_same( + 'Localized value', + LANGUAGE_LOADER_FIXTURE_LOCALIZED, + $language . ': fixture localized constant must win' + ); + + $invalid = tz_load_language('../xx', $fixtureDirectory); + assert_same( + 'English shared', + $invalid['nested']['shared'] ?? null, + $language . ': invalid locale must use English' + ); + + echo "PASS {$language}\n"; +} + +if (isset($argv[1])) { + run_language_case($argv[1]); + exit(0); +} + +$languages = ['en', 'fr', 'it', 'ro', 'zh']; +foreach ($languages as $language) { + $pipes = []; + $process = proc_open( + [PHP_BINARY, __FILE__, $language], + [ + 1 => ['pipe', 'w'], + 2 => ['pipe', 'w'], + ], + $pipes, + __DIR__ + ); + + if (!is_resource($process)) { + fail_test('Unable to start test process for ' . $language); + } + + $stdout = stream_get_contents($pipes[1]); + $stderr = stream_get_contents($pipes[2]); + fclose($pipes[1]); + fclose($pipes[2]); + $status = proc_close($process); + + if ($status !== 0) { + fwrite(STDERR, $stderr !== '' ? $stderr : $stdout); + exit($status); + } + + echo $stdout; +} + +echo "PASS language loader: 5 locales\n"; diff --git a/tests/fixtures/languages/en.php b/tests/fixtures/languages/en.php new file mode 100644 index 00000000..2627ba6e --- /dev/null +++ b/tests/fixtures/languages/en.php @@ -0,0 +1,6 @@ +