- 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>
233 lines
5.2 KiB
Markdown
233 lines
5.2 KiB
Markdown
# Translation Approach for Task 19: Te Reo Māori & Multilanguage Support
|
|
|
|
**Date:** October 12, 2025
|
|
**Status:** Planning - To be implemented after Task 12 (API Documentation)
|
|
**Priority:** CRITICAL (Values - Te Tiriti commitment)
|
|
|
|
---
|
|
|
|
## Translation Service: DeepL
|
|
|
|
**Decision:** Use DeepL API for translation services instead of i18next manual translations
|
|
|
|
### Deployment Architecture
|
|
|
|
**Local Development (Port 9000):**
|
|
- DeepL API integration in Node.js
|
|
- Local environment variable: `DEEPL_API_KEY`
|
|
- Translation caching in MongoDB
|
|
|
|
**Production Server (vps-93a693da.vps.ovh.net):**
|
|
- DeepL API integration deployed
|
|
- Secure environment variable configuration
|
|
- Production translation cache
|
|
|
|
---
|
|
|
|
## Implementation Plan
|
|
|
|
### 1. DeepL API Integration
|
|
|
|
```javascript
|
|
// Example integration approach
|
|
const deepl = require('deepl-node');
|
|
|
|
const translator = new deepl.Translator(process.env.DEEPL_API_KEY);
|
|
|
|
async function translateContent(text, targetLang) {
|
|
const result = await translator.translateText(text, null, targetLang);
|
|
return result.text;
|
|
}
|
|
```
|
|
|
|
### 2. Language Selector UI
|
|
|
|
- **Location:** Navigation bar (all pages)
|
|
- **Languages:** English (en), Te Reo Māori (mi)
|
|
- **Storage:** Browser localStorage + cookie
|
|
- **Behavior:** Persist selection across sessions
|
|
|
|
### 3. Translation Cache
|
|
|
|
**MongoDB Collection:** `translations`
|
|
|
|
```javascript
|
|
{
|
|
source_text: String,
|
|
source_lang: String,
|
|
target_lang: String,
|
|
translated_text: String,
|
|
verified: Boolean,
|
|
created_at: Date,
|
|
updated_at: Date
|
|
}
|
|
```
|
|
|
|
**Benefits:**
|
|
- Reduces API calls
|
|
- Faster page loads
|
|
- Cost optimization
|
|
- Human verification workflow
|
|
|
|
### 4. Priority Pages for Translation
|
|
|
|
**Phase 1 (Task 19):**
|
|
- Homepage (index.html)
|
|
- About/Values page (about.html)
|
|
- Core Framework Documentation
|
|
|
|
**Phase 2 (Future):**
|
|
- All audience paths (researcher, implementer, advocate)
|
|
- Interactive demos
|
|
- Blog posts
|
|
- API documentation
|
|
|
|
### 5. Māori Language Consultation
|
|
|
|
**Critical:** Despite using DeepL, Māori language consultation is REQUIRED
|
|
|
|
**Why:**
|
|
- Cultural appropriateness review
|
|
- Technical term accuracy
|
|
- Idiomatic expressions
|
|
- Values alignment
|
|
|
|
**Process:**
|
|
1. DeepL generates initial translation
|
|
2. Store in database as `verified: false`
|
|
3. Submit to Māori language consultant
|
|
4. Update with corrections
|
|
5. Mark as `verified: true`
|
|
|
|
### 6. Rate Limiting & Cost Management
|
|
|
|
**DeepL API Limits:**
|
|
- Free tier: 500,000 characters/month
|
|
- Pro tier: Pay per character
|
|
|
|
**Strategy:**
|
|
- Cache all translations
|
|
- Batch translation requests
|
|
- Monitor character usage
|
|
- Set monthly budget alerts
|
|
|
|
---
|
|
|
|
## Technical Implementation
|
|
|
|
### Routes
|
|
|
|
```javascript
|
|
// POST /api/translate
|
|
// Translate text to specified language
|
|
router.post('/translate', async (req, res) => {
|
|
const { text, targetLang } = req.body;
|
|
|
|
// Check cache first
|
|
const cached = await Translation.findOne({
|
|
source_text: text,
|
|
target_lang: targetLang,
|
|
verified: true
|
|
});
|
|
|
|
if (cached) {
|
|
return res.json({ translation: cached.translated_text });
|
|
}
|
|
|
|
// Call DeepL API
|
|
const translation = await translateContent(text, targetLang);
|
|
|
|
// Store in cache
|
|
await Translation.create({
|
|
source_text: text,
|
|
source_lang: 'en',
|
|
target_lang: targetLang,
|
|
translated_text: translation,
|
|
verified: false
|
|
});
|
|
|
|
res.json({ translation });
|
|
});
|
|
```
|
|
|
|
### Frontend Integration
|
|
|
|
```javascript
|
|
// Language selector component
|
|
class LanguageSelector {
|
|
constructor() {
|
|
this.currentLang = localStorage.getItem('lang') || 'en';
|
|
}
|
|
|
|
async switchLanguage(lang) {
|
|
localStorage.setItem('lang', lang);
|
|
await this.translatePage(lang);
|
|
}
|
|
|
|
async translatePage(lang) {
|
|
if (lang === 'en') {
|
|
// Reload original content
|
|
location.reload();
|
|
return;
|
|
}
|
|
|
|
// Translate all translatable elements
|
|
const elements = document.querySelectorAll('[data-translate]');
|
|
for (const el of elements) {
|
|
const originalText = el.textContent;
|
|
const translation = await this.translate(originalText, lang);
|
|
el.textContent = translation;
|
|
}
|
|
}
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## Configuration
|
|
|
|
### Environment Variables
|
|
|
|
**Development (.env):**
|
|
```bash
|
|
DEEPL_API_KEY=your_dev_key
|
|
DEEPL_API_FREE_TIER=true
|
|
TRANSLATION_CACHE_ENABLED=true
|
|
```
|
|
|
|
**Production (remote):**
|
|
```bash
|
|
DEEPL_API_KEY=your_prod_key
|
|
DEEPL_API_FREE_TIER=false
|
|
TRANSLATION_CACHE_ENABLED=true
|
|
TRANSLATION_VERIFICATION_REQUIRED=true
|
|
```
|
|
|
|
---
|
|
|
|
## Success Criteria
|
|
|
|
- [ ] DeepL API integrated on both dev and production
|
|
- [ ] Language selector functional in navigation
|
|
- [ ] Translation caching working
|
|
- [ ] Homepage, About, Core Docs translated to Te Reo Māori
|
|
- [ ] Māori language consultant review completed
|
|
- [ ] All translations marked as verified
|
|
- [ ] User preference persists across sessions
|
|
- [ ] Performance: <500ms translation load time (cached)
|
|
|
|
---
|
|
|
|
## Notes
|
|
|
|
- **Cost Estimate:** Homepage + About + Core Docs ≈ 50,000 characters
|
|
- **DeepL Free Tier:** Sufficient for Phase 1
|
|
- **Future:** Add more languages (Samoan, Tongan, Cook Islands Māori, etc.)
|
|
- **Accessibility:** Ensure `lang` attribute updated on `<html>` tag
|
|
- **SEO:** Consider separate URLs vs. client-side switching
|
|
|
|
---
|
|
|
|
**To Be Implemented:** After Task 12 (API Documentation) completion
|
|
**Dependencies:** DeepL API account, MongoDB translations collection, Māori language consultant
|
|
**Timeline:** 5-7 days estimated
|