PHP Classes

File: docs/proposals/Translations.md

Recommend this page to a friend!
  Packages of Matthew Asham   Binkterm PHP   docs/proposals/Translations.md   Download  
File: docs/proposals/Translations.md
Role: Auxiliary data
Content type: text/markdown
Description: Auxiliary data
Class: Binkterm PHP
Bulletin board system based on the Web
Author: By
Last change:
Date: 5 days ago
Size: 13,006 bytes
 

Contents

Class file image Download

?# Translation Support Plan

> Draft notice: This proposal is a draft generated by AI and may not have been reviewed for accuracy.

Goal

Add multi-language support (i18n/l10n) to the web application without a destabilizing rewrite, while preserving current behavior for English users.

Current State Summary

  • Translation framework is installed and wired into Twig/PHP/JS.
  • Catalogs are namespaced and loaded from `config/i18n/<locale>/<namespace>.php` (currently `common.php`, `errors.php`).
  • API error responses now generally use structured error payloads with `error_code` + `error`.
  • Client-side translation helper (`window.t`) and lazy catalog loading (`/api/i18n/catalog`) are active.
  • Hardcoded user-facing strings still exist in some templates/pages, but core shared/user flows have substantial coverage now.

Scope

  • In scope: - Web UI (Twig templates, inline JS, `public_html/js/*.js`). - API response localization strategy. - Date/time/number formatting consistency by locale. - Locale selection, persistence, and fallback. - Translation key management, extraction, QA, and CI checks.
  • Out of scope (initial rollout): - Translating user-generated content (messages, posts, ads). - Auto-translation services. - BinkP protocol payload text. - ~~Telnet/BinkP protocol payload text~~ ?? Telnet daemon localization was added to scope and completed in 1.8.6. All user-facing strings in the telnet server (shell menus, message editor, echomail/netmail browsers, polls, shoutbox, door launcher) are translated via the `terminalserver` catalog namespace.

Guiding Principles

  • Preserve API stability while introducing localization safely.
  • Prefer stable message/error codes over localized server text in APIs.
  • Keep English as fallback for all missing keys.
  • Ship incrementally by page area, not by ??convert everything at once?.

Proposed Architecture

  1. Locale Resolution - Resolution order: 1. User setting (`user_settings.locale`) when authenticated. 2. Cookie/session locale for anonymous users. 3. `Accept-Language` best match. 4. Default locale (`en`). - Persist locale choice in user settings and anonymous cookie.
  2. Translation Source of Truth - Store catalogs in `config/i18n/<locale>.php` (associative arrays) or JSON files. - Naming convention for keys: - `nav.login`, `nav.logout` - `dashboard.loading_shouts` - `errors.auth.invalid_credentials` - `admin.users.not_found`
  3. Server Translator Service - Add `src/I18n/Translator.php`: - `translate($key, array $params = [], ?string $locale = null): string` - fallback chain locale -> base locale -> `en` -> key. - Add Twig function/filter: - `t('key')` and optional parameter interpolation.
  4. Frontend Translator - Expose active locale and subset dictionaries via: - global `window.i18n` - or `/api/i18n/catalog?ns=...`. - Add JS helper: - `t(key, params)` for UI strings. - Move repeated strings from inline JS to keys.
  5. API Message Strategy - Replace direct English API error text with: - machine code (`error_code`) - optional default English message for backward compatibility window. - Frontend maps `error_code` -> localized text. - Keep transitional compatibility: - return both fields for one release cycle: `{ error_code, error }`.
  6. Formatting Standardization - Centralize date/time formatting in JS helper using user locale + timezone. - Replace hardcoded `en-US` and direct string literals in relative-time functions with keyed translations.

Data Model Changes

  1. Migration - Add `locale` column to `user_settings` (default `en`). - Optional: keep existing `date_format` temporarily; map to locale where possible.
  2. Backward Compatibility - If no `locale` set, infer from current `date_format` where feasible: - `en-US` -> `en-US` - `en-GB` -> `en-GB` - etc. - Fall back to `en`.

Implementation Phases

Status Update (March 6, 2026) ?? Shipped in 1.8.6

  • Phase 0 (Foundation): Completed - Translator + locale resolver added and wired through Twig `t()`. - Locale persistence/resolution path implemented. - JS i18n helper and lazy namespace loading endpoint implemented.
  • Phase 1 (Shared Shell/UI Chrome): Completed (with one intentional deferral) - `templates/base.twig`, `templates/shells/web/base.twig`, and `templates/shells/bbs-menu/base.twig` are localized. - `templates/old.base.twig` is intentionally deferred by project decision.
  • Phase 2 (High-Traffic User Pages): Completed - Localized pages include: dashboard, netmail, echomail, compose, settings, login, register, forgot/reset password, profile, user profile, about, 404, create poll, shoutbox, polls, shared message, and files. - `public_html/js/netmail.js` and `public_html/js/echomail.js` fully migrated to translation keys.
  • Phase 3 (API Error Code Migration): Completed - `apiError(error_code, error, ...)` pattern is in active use across all major endpoints. - Frontend consumers use `getApiErrorMessage(...)` broadly. - Legacy plain-text error responses normalized.
  • Phase 4 (Admin Surface): Completed - All high-use admin templates migrated to translation keys: users, dashboard, binkp, economy, polls, doors, file areas, chat/admin tooling, and configuration pages.
  • Phase 5 (Hardening and Cleanup): In Progress (advanced) - Validation scripts in place and passing: - `scripts/check_i18n_hardcoded_strings.php` - `scripts/check_i18n_error_keys.php` - Validation snapshot: `Detected hardcoded UI strings: 0`, `New violations: 0`, `Missing keys: 0`. - Playwright tests verify `es` catalog API structure and key parity with `en`. - Manual visual QA conducted against a checklist in both `en` and `es` on core user and admin flows. - Remaining: no automated Playwright smoke pass runs with the user locale set to `es` ?? secondary locale coverage is manual only.
  • Telnet Daemon Localization (added to scope): Completed - All user-facing strings in the telnet server translated via the `terminalserver` catalog namespace. - Daemon defaults to system locale pre-login, switches to user's saved locale after login. - Ships with `en` and `es` catalogs (`config/i18n/<locale>/terminalserver.php`).

Phase 0: Foundation (No User-Visible Language Changes)

  • Add translator service and locale resolver.
  • Wire translator into `Template` and Twig (`t()` helper).
  • Add `locale` setting support in backend and settings API.
  • Add JS i18n helper and dictionary loading.
  • Add `lang` variable to layout templates (`<html lang="{{ locale }}">`).
  • Acceptance: - App behavior unchanged in English. - Locale can be set and read end-to-end.

Phase 1: Shared Shell/UI Chrome

  • Convert common templates first: - `templates/base.twig` - `templates/shells/web/base.twig` - `templates/shells/bbs-menu/base.twig` - `templates/old.base.twig`
  • Convert global app JS status/error labels.
  • Acceptance: - Navigation/footer/common modals translated. - No missing key placeholders in shared layout.

Phase 2: High-Traffic User Pages

  • Convert: - `dashboard.twig`, `netmail.twig`, `echomail.twig`, `compose.twig`, `settings.twig`, `login/register` flows.
  • Replace inline alert/confirm/loading strings with keys.
  • Acceptance: - Core message workflows localized. - Date/time text uses locale consistently.

Phase 3: API Error Code Migration

  • Introduce `error_code` for major API endpoints: - auth, messaging, polls, shoutbox, files, admin.
  • Frontend consumes codes; fallback to legacy `error` during transition.
  • Acceptance: - No frontend dependency on raw English `error` strings.

Phase 4: Admin Surface

  • Convert admin templates and admin JS panels incrementally.
  • Prioritize high-use pages (users, dashboard, binkp config, stats).
  • Acceptance: - Admin core workflows localized.

Phase 5: Hardening and Cleanup

  • Remove deprecated English fallback fields in APIs (after compatibility window).
  • Add lint/check scripts: - detect hardcoded English in Twig/JS (allowlist exceptions). - detect missing translation keys.
  • Add snapshot/integration tests for at least `en` + one secondary locale.

Work Breakdown (Concrete File Targets)

  1. Core - `src/Template.php` (inject locale and `t()` support) - `src/MessageHandler.php` (persist/read locale in user settings) - new `src/I18n/Translator.php` - new `src/I18n/LocaleResolver.php`
  2. Routes/API - `routes/api-routes.php` (error code rollout) - `routes/admin-routes.php` (error code rollout) - `routes/door-routes.php`, `src/Auth.php` (auth/error consistency) - `routes/web-routes.php` (manifest strings and locale-aware metadata)
  3. Templates - Shared bases first, then user pages, then admin pages.
  4. Frontend JS - `public_html/js/app.js` - `public_html/js/netmail.js` - `public_html/js/echomail.js` - page-local inline scripts in Twig templates.

Risks and Mitigations

  1. Risk: Massive key churn and merge conflicts. - Mitigation: namespace keys by feature and convert by phase.
  2. Risk: API clients break if `error` format changes. - Mitigation: transition period with both `error_code` and `error`.
  3. Risk: Missing keys shipped to production. - Mitigation: build-time key validation + runtime fallback logging.
  4. Risk: UI overflow/layout regressions in longer languages. - Mitigation: targeted visual QA on nav, buttons, modals, tables.

Testing Strategy

  • Unit tests: - translator fallback and interpolation. - locale resolver precedence.
  • Integration tests: - login/settings locale persistence. - key pages render in `en` and secondary locale.
  • API tests: - verify `error_code` contract and fallback.
  • Manual QA: - dashboard, compose, message listing, admin user actions.

Definition of Done (Project-Level)

  • Locale selectable and persisted.
  • Shared UI + core user flows localized.
  • API uses stable error codes (with migration complete).
  • No critical hardcoded English in in-scope pages.
  • CI checks for missing keys/hardcoded strings active.

Effort Estimate

  • Foundation + shared UI: medium.
  • Core user pages + JS conversion: medium-high.
  • API error-code migration + admin conversion + test hardening: high.
  • Overall: high effort, low-to-medium operational risk if phased as above.

Rollout Recommendation

  1. Ship `en` + one additional locale first.
  2. Enable locale switch behind feature flag for internal testing.
  3. Expand locale coverage after API code migration stabilizes.

Phase 6: System Scripts and Daemons

CLI scripts and daemons in scripts/ generally fall into three categories for i18n purposes:

User-facing daemons (output seen directly by end users ?? highest priority): - src/TelnetServer/ ?? Completed in 1.8.6 via terminalserver catalog namespace.

Interactive sysop tools (output seen by the person running the command): - scripts/user-manager.php ?? interactive user management CLI - scripts/binkp_status.php ?? status display - scripts/who.php ?? who's online - scripts/admin_client.php ?? admin CLI client

Background daemons and cron scripts (output goes to log files, sysop-facing only): - scripts/binkp_server.php, scripts/binkp_scheduler.php, scripts/binkp_poll.php - scripts/admin_daemon.php, scripts/mrc_daemon.php, scripts/gemini_daemon.php - scripts/process_packets.php, scripts/echomail_maintenance.php, and other maintenance scripts - scripts/setup.php, scripts/upgrade.php

Current Status: Not started. Background daemon log output is sysop/developer-facing and is low priority for translation. Interactive sysop tools are moderate priority. No script outside the telnet daemon currently uses the i18n Translator class.

Phase 7: Legacy API Error Field Retirement

API responses currently return both error_code (the stable translation key) and error (a plain English fallback string) for backward compatibility. Once all frontend consumers have been confirmed to use error_code exclusively via getApiErrorMessage(...), the plain error field can be retired.

Current Status: Deferred ?? compatibility window still open.

This is intentionally deferred until there is confidence that no external integrations or older client versions rely on the plain error text. Suggested criteria before retiring: - At least one full release cycle has passed since error_code became universal. - A grep confirms no frontend code matches on raw English error strings. - Release notes call out the removal explicitly.

Immediate Next Steps (From Current Status)

  1. Keep CI guardrails green (`check_i18n_hardcoded_strings.php`, `check_i18n_error_keys.php`) on every PR adding new UI or API work.
  2. Add a Playwright smoke pass that sets the user locale to `es` and verifies core pages render without raw i18n keys (completes Phase 5).
  3. Legacy `error` field retirement is tracked in Phase 7 ?? no action needed until the compatibility window is judged closed.