- 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>
8.9 KiB
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()replacesinnerHTMLof[data-i18n]elements
Footer Component
- File:
/public/js/components/footer.js - Type: Dynamically inserted component (not in static HTML)
- Initialization: Event-based (waits for
i18nInitializedevent) - Cache version:
?v=1761129862
Diagnostic Evidence
1. Translation Data Verified on Server
$ 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
[Footer] Footer exists: true
[Footer] Elements with data-i18n: 20
Footer HTML is in DOM with proper data-i18n attributes.
3. Translation Application Called
[Footer] Translations applied
window.I18n.applyTranslations() executes without errors.
4. Translation Fails
[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
// /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:
this.translationsdoesn't havefooterkeythis.translations.footerdoesn't haveabout_headingkey- Value is not a string (e.g., still an object)
- 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:
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:
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
// 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
// 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
// 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):
this.translations = { ...commonTranslations, ...pageTranslations };
Problem: Shallow merge overwrites entire top-level objects.
Fix: Deep merge utility
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():
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