tractatus/FOOTER_I18N_DIAGNOSTIC_BRIEF.md
TheFlow 2298d36bed fix(submissions): restructure Economist package and fix article display
- Create Economist SubmissionTracking package correctly:
  * mainArticle = full blog post content
  * coverLetter = 216-word SIR— letter
  * Links to blog post via blogPostId
- Archive 'Letter to The Economist' from blog posts (it's the cover letter)
- Fix date display on article cards (use published_at)
- Target publication already displaying via blue badge

Database changes:
- Make blogPostId optional in SubmissionTracking model
- Economist package ID: 68fa85ae49d4900e7f2ecd83
- Le Monde package ID: 68fa2abd2e6acd5691932150

Next: Enhanced modal with tabs, validation, export

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-24 08:47:42 +13:00

293 lines
8.9 KiB
Markdown

# Footer i18n Translation Failure - Technical Diagnostic Brief
**Date**: 2025-10-22
**Issue**: Footer displays raw translation keys instead of translated text on /researcher.html and /leader.html
**Status**: BLOCKING - Prevents deployment
**Affected Pages**: /researcher.html, /leader.html
**Working Pages**: /index.html, /implementer.html
---
## Problem Statement
Footer component renders correctly but translations fail to apply. Elements display raw keys like `footer.about_heading` instead of "Tractatus Framework".
**Console Output Shows:**
```
[Footer] Applying translations...
[Footer] Footer exists: true
[Footer] Elements with data-i18n: 20
[Footer] Translations applied
[Footer] about_heading element text: footer.about_heading ← PROBLEM
```
**Expected**: `about_heading element text: Tractatus Framework`
---
## System Architecture
### Translation System
- **Library**: Custom i18n implementation (`/public/js/i18n-simple.js`)
- **Translation files**: `/public/locales/en/common.json`, `/public/locales/en/researcher.json`
- **Loading strategy**: Async fetch → shallow merge
- **Application method**: `window.I18n.applyTranslations()` replaces `innerHTML` of `[data-i18n]` elements
### Footer Component
- **File**: `/public/js/components/footer.js`
- **Type**: Dynamically inserted component (not in static HTML)
- **Initialization**: Event-based (waits for `i18nInitialized` event)
- **Cache version**: `?v=1761129862`
---
## Diagnostic Evidence
### 1. Translation Data Verified on Server
```bash
$ curl -s http://localhost:9000/locales/en/common.json | jq '.footer.about_heading'
"Tractatus Framework" ✓ CORRECT
$ curl -s http://localhost:9000/locales/en/researcher.json | jq 'has("footer")'
false ✓ CORRECT (footer removed to prevent overwrite)
```
### 2. Footer Rendering Confirmed
```javascript
[Footer] Footer exists: true
[Footer] Elements with data-i18n: 20
```
Footer HTML is in DOM with proper `data-i18n` attributes.
### 3. Translation Application Called
```javascript
[Footer] Translations applied
```
`window.I18n.applyTranslations()` executes without errors.
### 4. Translation Fails
```javascript
[Footer] about_heading element text: footer.about_heading
```
After `applyTranslations()`, element still contains raw key.
---
## Root Cause Hypothesis
**`window.I18n.t('footer.about_heading')` is returning the key instead of the translation.**
### i18n.t() Implementation
```javascript
// /public/js/i18n-simple.js:123-136
t(key) {
const keys = key.split('.');
let value = this.translations;
for (const k of keys) {
if (value && typeof value === 'object') {
value = value[k];
} else {
return key; // Return key if translation not found
}
}
return value || key;
}
```
**Possible failures:**
1. `this.translations` doesn't have `footer` key
2. `this.translations.footer` doesn't have `about_heading` key
3. Value is not a string (e.g., still an object)
4. Timing issue - translations not yet loaded when `applyTranslations()` called
---
## Critical Question
**What does `window.I18n.translations` contain when footer renders?**
### Diagnostic Step Required
**In browser console, run:**
```javascript
console.log('Full translations:', window.I18n.translations);
console.log('Footer object:', window.I18n.translations.footer);
console.log('about_heading:', window.I18n.translations.footer?.about_heading);
console.log('Test t():', window.I18n.t('footer.about_heading'));
```
**Expected output:**
```javascript
Full translations: {footer: {...}, page: {...}, header: {...}, sections: {...}}
Footer object: {about_heading: "Tractatus Framework", ...}
about_heading: "Tractatus Framework"
Test t(): "Tractatus Framework"
```
**If output differs**, this reveals where the data structure breaks.
---
## Working vs Broken Comparison
### Working Pages (index.html, implementer.html)
- **Script order**: `i18n-simple.js``language-selector.js``scroll-animations.js``page-transitions.js``footer.js`
- **Footer in page-specific JSON**: NO
- **Result**: Footer displays correctly
### Broken Pages (researcher.html, leader.html)
- **Script order**: `i18n-simple.js``language-selector.js``scroll-animations.js``page-transitions.js``version-manager.js``researcher-page.js``footer.js`
- **Footer in page-specific JSON**: Removed (was causing shallow merge issue)
- **Result**: Footer displays raw keys
**Key difference**: Extra scripts (`version-manager.js`, `researcher-page.js`) load between i18n and footer.
---
## Previous Fixes Attempted
### Fix 1: Consolidated footer translations (v1.1.8)
**Problem**: researcher.json and leader.json had partial `footer` objects that overwrote common.json footer during shallow merge.
**Action**: Moved all footer keys to common.json, removed from page-specific files.
**Result**: Data structure fixed on server, but translations still not applied.
### Fix 2: Event-based initialization (v1.1.9)
**Problem**: Race condition - footer rendering before i18n loaded.
**Action**: Wait for `i18nInitialized` event before rendering.
**Result**: Timing fixed (console confirms footer waits for i18n), but translations still fail.
### Fix 3: Removed polling (v1.2.0)
**Problem**: Polling logic triggered rate limiting (100 req/15min).
**Action**: Simplified to single event-based check.
**Result**: Rate limiting resolved, but core translation issue persists.
---
## Next Debugging Steps
### Step 1: Inspect Browser Runtime State
```javascript
// In browser console on /researcher.html after page load:
window.I18n.translations
window.I18n.t('footer.about_heading')
document.querySelector('footer [data-i18n="footer.about_heading"]').innerHTML
```
### Step 2: Compare Working Page
```javascript
// In browser console on /index.html after page load:
window.I18n.translations
window.I18n.t('footer.about_heading')
document.querySelector('footer [data-i18n="footer.about_heading"]').innerHTML
```
### Step 3: Check Translation Merge
```javascript
// Does researcher.json override common.json at runtime?
// Expected: common.json footer + researcher.json sections
// Actual: ???
```
---
## Potential Solutions
### Solution A: Deep Merge Instead of Shallow Merge
**Current code** (`i18n-simple.js:111`):
```javascript
this.translations = { ...commonTranslations, ...pageTranslations };
```
**Problem**: Shallow merge overwrites entire top-level objects.
**Fix**: Deep merge utility
```javascript
this.translations = deepMerge(commonTranslations, pageTranslations);
function deepMerge(target, source) {
const output = { ...target };
for (const key in source) {
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
output[key] = deepMerge(target[key] || {}, source[key]);
} else {
output[key] = source[key];
}
}
return output;
}
```
### Solution B: Namespace Separation
**Keep footer translations ONLY in common.json**
**Never put footer keys in page-specific files**
**Status**: Already implemented, but verify runtime state
### Solution C: Manual Footer Translation
**Instead of relying on global `applyTranslations()`:**
```javascript
applyFooterTranslations() {
const footerElements = document.querySelectorAll('footer [data-i18n]');
footerElements.forEach(el => {
const key = el.dataset.i18n;
const translation = window.I18n.t(key);
console.log(`Translating ${key} -> ${translation}`);
el.innerHTML = translation;
});
}
```
---
## File Locations
### Translation Files
- `/public/locales/en/common.json` - Contains full footer object
- `/public/locales/en/researcher.json` - No footer object (removed)
- `/public/locales/en/leader.json` - No footer object (removed)
### JavaScript Files
- `/public/js/i18n-simple.js` - Translation system core
- `/public/js/components/footer.js` - Footer component
- `/public/researcher.html:627` - Footer script tag with cache version
- `/public/leader.html:611` - Footer script tag with cache version
### Server Configuration
- `/src/server.js:77` - Rate limiting middleware (100 req/15min)
- `/src/middleware/rate-limit.middleware.js` - Rate limiter implementation
---
## Success Criteria
**Footer displays:**
```
Tractatus Framework (not footer.about_heading)
Documentation (not footer.documentation_heading)
Framework Docs (not footer.documentation_links.framework_docs)
...etc
```
**Console shows:**
```
[Footer] about_heading element text: Tractatus Framework
```
---
## Contact for Clarification
**Project Owner**: Review console output from browser
**Required**: Share `window.I18n.translations` object from browser console
**This will immediately reveal** if the problem is:
- Data not loaded (translations object missing footer)
- Data structure wrong (footer exists but wrong shape)
- Translation function broken (t() not navigating object correctly)
- Application timing (translations loaded but not applied)
---
**Priority**: HIGH - Blocking deployment
**Estimated Debug Time**: 15-30 minutes with browser console access
**Complexity**: LOW - Simple data flow issue, just need to see runtime state