Reorganize PHP internals and static assets

Move shared PHP code into private/, move JavaScript files into js/, and block direct access to private/. Remove unused API key and cache artifacts from the working tree.
This commit is contained in:
www-data
2026-05-26 11:32:36 +02:00
parent 97f23260ed
commit 2f2e8869d6
30 changed files with 48 additions and 48 deletions
+205
View File
@@ -0,0 +1,205 @@
<?php
/*
* languagestore.php
*
* Small SQLite-backed translation module.
*
* Database:
* data/racket-sandbox.sqlite
*/
class LanguageStoreException extends Exception
{
}
class LanguageStore
{
private $db;
private $fallbackLanguage;
public function __construct($dbFile, $fallbackLanguage = 'en')
{
$dir = dirname($dbFile);
if (!is_dir($dir)) {
if (!mkdir($dir, 0700, true)) {
throw new LanguageStoreException('Could not create database directory: ' . $dir);
}
}
$this->fallbackLanguage = $this->safeLanguage($fallbackLanguage);
$this->db = new PDO('sqlite:' . $dbFile);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->exec('PRAGMA journal_mode = WAL');
$this->db->exec('PRAGMA busy_timeout = 5000');
$this->init();
}
private function init()
{
$this->db->exec(
'CREATE TABLE IF NOT EXISTS app_translations (
id INTEGER PRIMARY KEY AUTOINCREMENT,
translation_key TEXT NOT NULL,
language TEXT NOT NULL,
text TEXT NOT NULL,
created_at INTEGER NOT NULL,
updated_at INTEGER NOT NULL,
UNIQUE(translation_key, language)
)'
);
$this->db->exec(
'CREATE INDEX IF NOT EXISTS idx_app_translations_language
ON app_translations(language, translation_key)'
);
}
public function translate($key, $language, $fallback = null)
{
$key = $this->safeKey($key);
$language = $this->safeLanguage($language);
$text = $this->findText($key, $language);
if ($text !== null) {
return $text;
}
if ($language !== $this->fallbackLanguage) {
$text = $this->findText($key, $this->fallbackLanguage);
if ($text !== null) {
return $text;
}
}
return $fallback !== null ? (string)$fallback : $key;
}
public function setTranslation($key, $language, $text)
{
$key = $this->safeKey($key);
$language = $this->safeLanguage($language);
$text = (string)$text;
$now = time();
$stmt = $this->db->prepare(
'INSERT INTO app_translations
(translation_key, language, text, created_at, updated_at)
VALUES
(:translation_key, :language, :text, :created_at, :updated_at)
ON CONFLICT(translation_key, language)
DO UPDATE SET text = excluded.text, updated_at = excluded.updated_at'
);
$stmt->execute(array(
':translation_key' => $key,
':language' => $language,
':text' => $text,
':created_at' => $now,
':updated_at' => $now,
));
}
public function seedDefaults($translations)
{
foreach ($translations as $key => $byLanguage) {
foreach ($byLanguage as $language => $text) {
if (!$this->hasTranslation($key, $language)) {
$this->setTranslation($key, $language, $text);
}
}
}
}
public function listTranslations($language = null)
{
if ($language !== null && $language !== '') {
$stmt = $this->db->prepare(
'SELECT translation_key, language, text, created_at, updated_at
FROM app_translations
WHERE language = :language
ORDER BY translation_key'
);
$stmt->execute(array(':language' => $this->safeLanguage($language)));
} else {
$stmt = $this->db->query(
'SELECT translation_key, language, text, created_at, updated_at
FROM app_translations
ORDER BY translation_key, language'
);
}
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
public function supportedLanguages()
{
return array('en', 'nl');
}
public function languageLabel($language)
{
if ($language === 'nl') {
return 'Nederlands';
}
if ($language === 'en') {
return 'English';
}
return $language;
}
private function findText($key, $language)
{
$stmt = $this->db->prepare(
'SELECT text
FROM app_translations
WHERE translation_key = :translation_key
AND language = :language'
);
$stmt->execute(array(
':translation_key' => $key,
':language' => $language,
));
$row = $stmt->fetch(PDO::FETCH_ASSOC);
return $row ? (string)$row['text'] : null;
}
private function hasTranslation($key, $language)
{
return $this->findText($this->safeKey($key), $this->safeLanguage($language)) !== null;
}
private function safeKey($key)
{
$key = trim((string)$key);
if ($key === '') {
throw new LanguageStoreException('Translation key is required.');
}
if (strlen($key) > 160) {
throw new LanguageStoreException('Translation key is too long.');
}
return $key;
}
private function safeLanguage($language)
{
$language = strtolower(trim((string)$language));
if (!preg_match('/^[a-z][a-z0-9_-]{1,15}$/', $language)) {
throw new LanguageStoreException('Invalid language code.');
}
return $language;
}
}