mirror of
https://github.com/Shadowss/TravianZ.git
synced 2026-08-09 12:51:01 +00:00
Centralize language loading with English fallback (#361)
This commit is contained in:
+3
-2
@@ -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}
|
||||
</div><!-- #lmidall -->
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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);
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Load one interface language and fill its gaps from English.
|
||||
*
|
||||
* Language files define constants through tz_def(), so loading the selected
|
||||
* locale first preserves translated constants while English defines only the
|
||||
* missing ones. Their legacy $lang arrays need an explicit merge because those
|
||||
* assignments are not guarded.
|
||||
*/
|
||||
if (!function_exists('tz_language_array_from_file')) {
|
||||
function tz_language_array_from_file($file)
|
||||
{
|
||||
$lang = [];
|
||||
require $file;
|
||||
|
||||
return is_array($lang) ? $lang : [];
|
||||
}
|
||||
}
|
||||
|
||||
if (!function_exists('tz_load_language')) {
|
||||
function tz_load_language($language, $languageDirectory = __DIR__)
|
||||
{
|
||||
global $lang;
|
||||
|
||||
$language = strtolower(trim((string) $language));
|
||||
if (!preg_match('/\A[a-z_]+\z/', $language)) {
|
||||
$language = 'en';
|
||||
}
|
||||
|
||||
$languageDirectory = rtrim($languageDirectory, '/\\');
|
||||
$englishFile = $languageDirectory . '/en.php';
|
||||
if (!is_file($englishFile)) {
|
||||
throw new RuntimeException('English language fallback not found: ' . $englishFile);
|
||||
}
|
||||
|
||||
$selectedFile = $languageDirectory . '/' . $language . '.php';
|
||||
if (!is_file($selectedFile)) {
|
||||
$selectedFile = $englishFile;
|
||||
}
|
||||
|
||||
$localized = tz_language_array_from_file($selectedFile);
|
||||
if ($selectedFile === $englishFile) {
|
||||
$lang = $localized;
|
||||
return $lang;
|
||||
}
|
||||
|
||||
$english = tz_language_array_from_file($englishFile);
|
||||
$lang = array_replace_recursive($english, $localized);
|
||||
|
||||
return $lang;
|
||||
}
|
||||
}
|
||||
@@ -60,12 +60,8 @@ include_once("Form.php");
|
||||
include_once("Generator.php");
|
||||
include_once("Multisort.php");
|
||||
include_once("Ranking.php");
|
||||
include_once("Lang/" . LANG . ".php");
|
||||
// en.php is an idempotent fallback (tz_def only defines missing keys); loading
|
||||
// it after the player language guarantees every interface constant is defined,
|
||||
// so a key missing from a translation degrades to English instead of a PHP 8.3
|
||||
// "undefined constant" fatal (blank page). See issue #189.
|
||||
include_once("Lang/en.php");
|
||||
require_once __DIR__ . "/Lang/loader.php";
|
||||
tz_load_language(LANG);
|
||||
include_once("Logging.php");
|
||||
include_once("Message.php");
|
||||
include_once("Alliance.php");
|
||||
@@ -585,4 +581,4 @@ if (!empty($_SESSION['id_user'])) {
|
||||
$message = new Message;
|
||||
$user = new User((int)$_SESSION['id_user'], $database);
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
@@ -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. ']';
|
||||
echo '[['. $map_js. ']';
|
||||
|
||||
@@ -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. ']';
|
||||
echo '[['. $map_js. ']';
|
||||
|
||||
+3
-2
@@ -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();
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
@@ -163,4 +164,4 @@ include("Templates/Anleitung/4.tpl"); }
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+3
-2
@@ -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();
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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();
|
||||
?>
|
||||
|
||||
@@ -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");
|
||||
|
||||
?>
|
||||
?>
|
||||
|
||||
+3
-9
@@ -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);
|
||||
?>
|
||||
|
||||
<html>
|
||||
@@ -84,4 +78,4 @@ else {
|
||||
?>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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){
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
@@ -161,4 +162,4 @@ if(T4_COMING==true){
|
||||
header("Location: ../index.php");
|
||||
exit;
|
||||
}
|
||||
?>
|
||||
?>
|
||||
|
||||
+2
-1
@@ -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");
|
||||
|
||||
+3
-2
@@ -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();
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
@@ -219,4 +220,4 @@ Displaying battle reports or messages in public without consent of both concerne
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -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();
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
<?php
|
||||
|
||||
error_reporting(E_ALL);
|
||||
ini_set('display_errors', '1');
|
||||
|
||||
function fail_test($message)
|
||||
{
|
||||
throw new RuntimeException($message);
|
||||
}
|
||||
|
||||
function assert_same($expected, $actual, $message)
|
||||
{
|
||||
if ($expected !== $actual) {
|
||||
fail_test(
|
||||
$message
|
||||
. "\nExpected: " . var_export($expected, true)
|
||||
. "\nActual: " . var_export($actual, true)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function run_language_case($language)
|
||||
{
|
||||
$expected = [
|
||||
'en' => [
|
||||
'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";
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
tz_def('LANGUAGE_LOADER_FIXTURE_FALLBACK', 'English fallback');
|
||||
|
||||
$lang['nested']['fallback_only'] = 'English fallback';
|
||||
$lang['nested']['shared'] = 'English shared';
|
||||
Vendored
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
tz_def('LANGUAGE_LOADER_FIXTURE_LOCALIZED', 'Localized value');
|
||||
|
||||
$lang['nested']['localized_only'] = 'Localized value';
|
||||
$lang['nested']['shared'] = 'Localized shared';
|
||||
+2
-1
@@ -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();
|
||||
?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
||||
|
||||
Reference in New Issue
Block a user