i18n Manager — Technical Docs

Technical Documentation

Architecture, data model, API, front-end localization, and tooling reference for the i18n Manager.

Architecture overview

The i18n Manager is a classic server-rendered PHP application backed by PostgreSQL, with a jQuery/Foundation single-page-style front end. There is no build step or framework — the browser loads index.php, which talks to a set of small PHP endpoints under /api/ via AJAX. Each endpoint connects directly to PostgreSQL through a thin procedural wrapper and returns JSON.

Browser (index.php + jQuery)
   │  AJAX (GET/POST)
   ▼
/api/*.php  ──►  classes/class_pgsql_data.php  ──►  PostgreSQL
   │
   └─ JSON responses

Tech stack

LayerTechnology
ServerPHP (procedural), pg_* extension
DatabasePostgreSQL (hosted on Supabase)
Front-end libsjQuery 3.6, Foundation Sites 6.7.5, Font Awesome 6.4 (via CDN)
Stylingincludes/app.css
ToolingNode.js (the key scanner); no runtime npm dependencies
API docsOpenAPI 3.0, Swagger UI, ReDoc, Postman collection

Directory structure

i18n_management/
├── index.php                  # Dashboard SPA (markup + jQuery app)
├── pages/                     # Standalone HTML pages (docs + API UIs)
│   ├── swagger.html           # Swagger UI (interactive API docs)
│   ├── redoc.html             # ReDoc (read-only API docs)
│   ├── user-guide.html        # End-user documentation (+ .de.html)
│   ├── technical.html         # This page (+ .de.html)
│   └── integration.html       # Integration guide (+ .de.html)
├── api/
│   ├── languages_list.php
│   ├── language_toggle.php
│   ├── translations_list.php
│   ├── translations_i18n.php  # flat { key: {lang: value} } map
│   ├── translations_export.php
│   ├── translation_key_save.php
│   ├── translations_import.php
│   ├── translation_key_delete.php
│   └── openapi.json           # OpenAPI 3.0 spec
├── classes/
│   └── class_pgsql_data.php   # PostgreSQL access wrapper
├── includes/
│   ├── app.css                # Dashboard styles
│   └── docs.css               # Documentation styles
├── tools/
│   └── scan_i18n_keys.js      # Markup scanner + seed generator
├── hold_files/                # Reference SQL/JSON (schema, seeds, i18n.json)
├── seeds/                     # Generated seed output (optional)
└── postman/                   # Postman collection

Database schema

Defined in hold_files/schema.sql. Four tables plus triggers and indexes.

languages

ColumnTypeNotes
codeCHAR(2)PK — ISO 639-1 code (en, de…)
nameVARCHAR(64)Display name
is_activeBOOLEANDefault TRUE
created_atTIMESTAMPTZDefault NOW()

translation_keys

ColumnTypeNotes
idSERIALPK
keyVARCHAR(255)UNIQUE — e.g. home.welcome
namespaceVARCHAR(64)GENERATED ALWAYS AS split_part(key,'.',1) STORED
descriptionTEXTOptional translator hint
contains_htmlBOOLEANDefault FALSE
created_at / updated_atTIMESTAMPTZupdated_at maintained by trigger

translations

ColumnTypeNotes
idSERIALPK
key_idINTFK → translation_keys, ON DELETE CASCADE
language_codeCHAR(2)FK → languages, ON DELETE RESTRICT
valueTEXTThe translated string
is_reviewedBOOLEANDefault FALSE
created_at / updated_atTIMESTAMPTZMaintained by trigger
UNIQUE (key_id, language_code) — one row per key × language; enables upserts via ON CONFLICT.

translation_audit

Append-only log written by an AFTER UPDATE trigger on translations whenever a value changes: stores key_id, language_code, old_value, new_value, changed_by, changed_at.

Triggers & indexes

Data access layer

classes/class_pgsql_data.php is a procedural wrapper around the PHP pg_* functions. Endpoints include it via $_SERVER['DOCUMENT_ROOT'] and use these helpers:

FunctionPurpose
DB_Connect_Direct($write)Open a connection (read-write or read-only).
DB_Query($conn, $sql)Run a literal query.
DB_QueryParams($conn, $sql, $params)Parameterized query ($1, $2…) — use this to avoid SQL injection.
DB_FetchAssoc / DB_FetchAllFetch one row / all rows as associative arrays.
DB_Begin / DB_Commit / DB_RollbackTransaction control (used by save & import).
DB_AffectedRows / DB_FreeResult / DB_CloseRow count / cleanup.
Booleans: PostgreSQL returns booleans as the strings 't'/'f'. Endpoints normalize these to real booleans before encoding JSON (e.g. $lang['is_active'] === 't').

API endpoints

All endpoints return JSON (except the export download). The full request/response schema lives in openapi.json and is browsable via Swagger / ReDoc.

MethodEndpointParametersPurpose
GETlanguages_list.phpList languages.
POSTlanguage_toggle.phpcode, is_activeActivate/deactivate a language.
GETtranslations_list.phpnamespace, search, page, limitPaginated keys + translations.
GETtranslations_i18n.phpnamespace, search, formatFlat {key:{lang:value}} map (json or js).
GETtranslations_export.phplanguage*, format, namespace, searchDownloadable JSON/CSV.
POSTtranslation_key_save.phpid, key, description, contains_html, mark_reviewed, translationsCreate/update a key (transactional upsert).
POSTtranslations_import.phpdata, overwrite, is_reviewedBulk import a JSON map.
POSTtranslation_key_delete.phpidDelete a key (cascades).

* required

API conventions

Front-end localization engine

The dashboard localizes its own chrome from the same data it manages. On load it fetches translations_i18n.php into state.i18n (a flat {key:{lang:value}} map), builds the language switcher from the active languages, and applies translations.

UI elements opt in via data attributes:

AttributeEffect
data-i18n="key"Sets the element's text.
data-i18n-html="key"Sets the element's innerHTML (for values containing markup).
data-i18n-placeholder="key"Sets an input's placeholder.

applyI18n(lang) walks every tagged element, looks up state.i18n[key][lang], and updates it; missing keys/languages fall back to the existing (English) markup. The selected language is stored in localStorage and reflected on <html lang>.

Per-key auto-translation in the Add/Edit dialog calls Google's translate.googleapis.com/translate_a/single?client=gtx endpoint for each target language.

Key scanner — tools/scan_i18n_keys.js

A dependency-free Node script that scans source files for data-i18n* attributes, determines which keys are new, and generates ready-to-paste Bulk-Import JSON and idempotent seed SQL — optionally machine-translating the source text into every language.

# New keys vs. the seed/reference files
node tools/scan_i18n_keys.js

# Diff against the LIVE database instead of seed files
node tools/scan_i18n_keys.js --endpoint http://localhost/api/translations_i18n.php

# Machine-translate the source text into all languages
node tools/scan_i18n_keys.js --translate

# Write seeds/new_keys.{json,sql} instead of printing
node tools/scan_i18n_keys.js --out seeds

# Other flags: --all (ignore existing), --root DIR, --langs de,fr,es

Seeds & reference files

FilePurpose
hold_files/schema.sqlFull database schema (tables, triggers, indexes).
hold_files/seed_translations.sqlDemo content keys (nav.home, home.welcome…).
hold_files/seed_ui_chrome.sqlInterface chrome keys (tabs, buttons, table headers) in all languages.
hold_files/i18n.jsonReference flat map illustrating the export shape.

Seed files use a CTE (WITH ins_keys AS (INSERT … RETURNING)) and ON CONFLICT … DO UPDATE so they are idempotent and safe to re-run.

API documentation artifacts

Setup & deployment

  1. Serve the project root with PHP (the pg_* extension must be enabled). The app assumes it is served at the domain root — it references /includes/, /classes/, /api/ and $_SERVER['DOCUMENT_ROOT'].
  2. Provision a PostgreSQL database and apply hold_files/schema.sql.
  3. Seed reference data: seed_translations.sql and/or seed_ui_chrome.sql.
  4. Configure database connection details in classes/class_pgsql_data.php (see security note below).
  5. Open index.php. The CDN assets (Foundation, jQuery, Font Awesome, Swagger/ReDoc) require internet access.

Security notes

Hardcoded credentials: database host/user/password are currently hardcoded in classes/class_pgsql_data.php. Move them to environment variables and rotate the password — anything committed to git should be treated as compromised.