MASTER PLANS - Multi-Language, Accessibility, Assets - 4 strategic documents
This commit is contained in:
715
ACCESSIBILITY_PLAN.md
Normal file
715
ACCESSIBILITY_PLAN.md
Normal file
@@ -0,0 +1,715 @@
|
||||
# ♿ ACCESSIBILITY OPTIONS - IMPLEMENTATION PLAN
|
||||
**Game:** Mrtva Dolina / Death Valley
|
||||
**Date:** January 4, 2026
|
||||
**Priority:** HIGH (Inclusive Gaming Standard)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 ACCESSIBILITY FEATURES (From Screenshot)
|
||||
|
||||
### **Required Features:**
|
||||
1. ✅ **High Contrast Mode**
|
||||
2. ✅ **Large Text Mode**
|
||||
3. ✅ **Color Blind Mode**
|
||||
4. ✅ **Screen Reader Support**
|
||||
5. ✅ **Reduce Flashing (Epilepsy)**
|
||||
6. ✅ **One-Handed Controls**
|
||||
7. ✅ **Audio Cues**
|
||||
|
||||
**Quick Toggle:** Press 1-7 to toggle features
|
||||
**Full Settings:** ESC → Settings → Accessibility
|
||||
|
||||
---
|
||||
|
||||
## 📋 DETAILED IMPLEMENTATION
|
||||
|
||||
### **1. HIGH CONTRAST MODE** 🎨
|
||||
|
||||
**Purpose:** Better visibility for low vision players
|
||||
|
||||
**Changes:**
|
||||
- Background: Pure black (#000000)
|
||||
- Text: Pure white (#FFFFFF)
|
||||
- UI borders: Bright yellow (#FFFF00)
|
||||
- Buttons: High contrast colors
|
||||
- Dialogue box: Black with white border
|
||||
- Character portraits: Increased brightness +30%
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
// src/systems/AccessibilitySystem.js
|
||||
class AccessibilitySystem {
|
||||
toggleHighContrast() {
|
||||
this.highContrast = !this.highContrast;
|
||||
|
||||
if (this.highContrast) {
|
||||
// Override all colors
|
||||
this.scene.dialogueBg.setFillStyle(0x000000, 1.0);
|
||||
this.scene.dialogueBg.setStrokeStyle(4, 0xFFFF00);
|
||||
this.scene.dialogueText.setColor('#FFFFFF');
|
||||
this.scene.speakerText.setColor('#FFFF00');
|
||||
|
||||
// Apply to all scenes
|
||||
this.game.scene.scenes.forEach(scene => {
|
||||
if (scene.applyHighContrast) {
|
||||
scene.applyHighContrast();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// Restore original colors
|
||||
this.restoreDefaultColors();
|
||||
}
|
||||
|
||||
this.save();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard:** Press **1** to toggle
|
||||
|
||||
---
|
||||
|
||||
### **2. LARGE TEXT MODE** 📝
|
||||
|
||||
**Purpose:** Readability for visually impaired
|
||||
|
||||
**Changes:**
|
||||
- All text +50% larger
|
||||
- Dialogue: 18px → 27px
|
||||
- UI labels: 16px → 24px
|
||||
- Buttons: 14px → 21px
|
||||
- Line spacing: +25%
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
toggleLargeText() {
|
||||
this.largeText = !this.largeText;
|
||||
const multiplier = this.largeText ? 1.5 : 1.0;
|
||||
|
||||
// Update all text objects
|
||||
this.scene.dialogueText.setFontSize(18 * multiplier);
|
||||
this.scene.speakerText.setFontSize(22 * multiplier);
|
||||
|
||||
// Resize dialogue box to fit
|
||||
const newHeight = this.largeText ? 240 : 180;
|
||||
this.scene.dialogueBg.setDisplaySize(
|
||||
this.scene.dialogueBg.width,
|
||||
newHeight
|
||||
);
|
||||
|
||||
this.save();
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard:** Press **2** to toggle
|
||||
|
||||
---
|
||||
|
||||
### **3. COLOR BLIND MODE** 🌈
|
||||
|
||||
**Purpose:** Support for color blindness (8% of men, 0.5% of women)
|
||||
|
||||
**Types Supported:**
|
||||
- **Deuteranopia** (Red-Green, most common)
|
||||
- **Protanopia** (Red-Green)
|
||||
- **Tritanopia** (Blue-Yellow, rare)
|
||||
|
||||
**Changes:**
|
||||
- Health bars: Red → Orange with pattern
|
||||
- Stamina: Blue → Cyan with stripes
|
||||
- Quest markers: Color + Icon
|
||||
- Item rarity: Color + Border style
|
||||
- Enemy indicators: Color + Shape
|
||||
|
||||
**Color Palette Remapping:**
|
||||
```javascript
|
||||
const COLOR_BLIND_PALETTES = {
|
||||
deuteranopia: {
|
||||
red: 0xFF8C00, // Orange instead of red
|
||||
green: 0x00CED1, // Dark cyan instead of green
|
||||
blue: 0x4169E1, // Royal blue (unchanged)
|
||||
purple: 0xFF00FF // Magenta (more distinct)
|
||||
},
|
||||
protanopia: {
|
||||
red: 0xFFD700, // Gold instead of red
|
||||
green: 0x1E90FF, // Dodger blue instead of green
|
||||
blue: 0x8A2BE2, // Blue violet
|
||||
purple: 0xFF69B4 // Hot pink
|
||||
},
|
||||
tritanopia: {
|
||||
red: 0xFF0000, // Red (unchanged)
|
||||
green: 0x00FF00, // Green (unchanged)
|
||||
blue: 0xFF1493, // Deep pink instead of blue
|
||||
yellow: 0x00FFFF // Cyan instead of yellow
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
setColorBlindMode(type) {
|
||||
this.colorBlindMode = type; // 'none', 'deuteranopia', 'protanopia', 'tritanopia'
|
||||
|
||||
if (type !== 'none') {
|
||||
const palette = COLOR_BLIND_PALETTES[type];
|
||||
|
||||
// Remap all color references
|
||||
this.game.registry.set('palette', palette);
|
||||
|
||||
// Add patterns to critical elements
|
||||
this.addAccessibilityPatterns();
|
||||
}
|
||||
|
||||
this.save();
|
||||
}
|
||||
|
||||
addAccessibilityPatterns() {
|
||||
// Add stripes to health bar
|
||||
this.healthBar.setTexture('health_bar_striped');
|
||||
|
||||
// Add icons to color-coded elements
|
||||
this.questMarkers.forEach(marker => {
|
||||
marker.addIcon(); // Add symbol alongside color
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**UI:** Dropdown menu: None / Deuteranopia / Protanopia / Tritanopia
|
||||
**Keyboard:** Press **3** to cycle modes
|
||||
|
||||
---
|
||||
|
||||
### **4. SCREEN READER SUPPORT** 🔊
|
||||
|
||||
**Purpose:** Blind/low-vision accessibility
|
||||
|
||||
**Features:**
|
||||
- Announce all UI changes
|
||||
- Read dialogue text aloud
|
||||
- Describe scene changes
|
||||
- Navigate menus with keyboard
|
||||
- Audio feedback for all actions
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
class ScreenReaderSystem {
|
||||
constructor(game) {
|
||||
this.game = game;
|
||||
this.enabled = false;
|
||||
this.speechSynth = window.speechSynthesis;
|
||||
this.currentUtterance = null;
|
||||
}
|
||||
|
||||
enable() {
|
||||
this.enabled = true;
|
||||
this.announce("Screen reader enabled. Use arrow keys to navigate.");
|
||||
}
|
||||
|
||||
announce(text, interrupt = false) {
|
||||
if (!this.enabled) return;
|
||||
|
||||
// Stop current speech if interrupting
|
||||
if (interrupt && this.speechSynth.speaking) {
|
||||
this.speechSynth.cancel();
|
||||
}
|
||||
|
||||
const utterance = new SpeechSynthesisUtterance(text);
|
||||
utterance.lang = this.game.settings.language || 'sl-SI';
|
||||
utterance.rate = 1.0;
|
||||
utterance.pitch = 1.0;
|
||||
utterance.volume = 1.0;
|
||||
|
||||
this.speechSynth.speak(utterance);
|
||||
this.currentUtterance = utterance;
|
||||
}
|
||||
|
||||
announceDialogue(speaker, text) {
|
||||
const fullText = `${speaker} says: ${text}`;
|
||||
this.announce(fullText, true);
|
||||
}
|
||||
|
||||
announceSceneChange(sceneName) {
|
||||
this.announce(`Entering ${sceneName}`, true);
|
||||
}
|
||||
|
||||
announceButton(buttonLabel) {
|
||||
this.announce(`Button: ${buttonLabel}. Press Enter to activate.`);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Integration with PrologueScene:**
|
||||
```javascript
|
||||
showDialogue(index) {
|
||||
const dialogue = this.dialogueData[index];
|
||||
|
||||
// Screen reader support
|
||||
if (this.game.accessibility.screenReader.enabled) {
|
||||
this.game.accessibility.screenReader.announceDialogue(
|
||||
dialogue.speaker,
|
||||
dialogue.text
|
||||
);
|
||||
}
|
||||
|
||||
// ... rest of dialogue logic
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard:** Press **4** to toggle
|
||||
|
||||
---
|
||||
|
||||
### **5. REDUCE FLASHING (EPILEPSY)** ⚡
|
||||
|
||||
**Purpose:** Prevent seizures (photosensitive epilepsy affects ~3%)
|
||||
|
||||
**Changes:**
|
||||
- Disable camera shake
|
||||
- Disable screen flash effects
|
||||
- Reduce particle effects
|
||||
- Smooth transitions only
|
||||
- Warning on startup if disabled
|
||||
|
||||
**Removed Effects:**
|
||||
```javascript
|
||||
toggleReduceFlashing() {
|
||||
this.reduceFlashing = !this.reduceFlashing;
|
||||
|
||||
if (this.reduceFlashing) {
|
||||
// Disable dangerous effects
|
||||
this.game.cameras.main.shake(0); // No shake
|
||||
this.game.cameras.main.flash(0); // No flash
|
||||
|
||||
// Reduce particle systems
|
||||
this.game.particles.setEmitterRate(0.5); // 50% particles
|
||||
|
||||
// Disable lightning
|
||||
this.weatherSystem.disableLightning = true;
|
||||
|
||||
// Smooth transitions only
|
||||
this.transitionDuration = 2000; // Slower fades
|
||||
}
|
||||
|
||||
this.save();
|
||||
}
|
||||
```
|
||||
|
||||
**Startup Warning:**
|
||||
```javascript
|
||||
create() {
|
||||
if (!this.game.accessibility.reduceFlashing) {
|
||||
this.showFlashWarning();
|
||||
}
|
||||
}
|
||||
|
||||
showFlashWarning() {
|
||||
const warning = this.add.text(
|
||||
this.cameras.main.width / 2,
|
||||
this.cameras.main.height / 2,
|
||||
'⚠️ WARNING ⚠️\n\n' +
|
||||
'This game contains flashing lights\n' +
|
||||
'and rapid scene changes.\n\n' +
|
||||
'Press 5 to enable Epilepsy Safety Mode\n' +
|
||||
'Press any other key to continue',
|
||||
{
|
||||
fontSize: '24px',
|
||||
align: 'center',
|
||||
backgroundColor: '#000000',
|
||||
padding: { x: 40, y: 40 }
|
||||
}
|
||||
);
|
||||
warning.setOrigin(0.5);
|
||||
|
||||
// Wait for input
|
||||
this.input.keyboard.once('keydown', (event) => {
|
||||
if (event.key === '5') {
|
||||
this.game.accessibility.toggleReduceFlashing();
|
||||
}
|
||||
warning.destroy();
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
**Keyboard:** Press **5** to toggle
|
||||
|
||||
---
|
||||
|
||||
### **6. ONE-HANDED CONTROLS** 🎮
|
||||
|
||||
**Purpose:** Physical disability support (one hand, motor impairments)
|
||||
|
||||
**Modes:**
|
||||
- **Left-Hand Mode** (WASD + nearby keys)
|
||||
- **Right-Hand Mode** (Arrow keys + numpad)
|
||||
- **Both modes support mouse-only gameplay**
|
||||
|
||||
**Left-Hand Layout:**
|
||||
```
|
||||
Movement: WASD
|
||||
Action: Q
|
||||
Menu: E
|
||||
Inventory: R
|
||||
Map: T
|
||||
Sprint: Shift
|
||||
Crouch: Ctrl
|
||||
```
|
||||
|
||||
**Right-Hand Layout:**
|
||||
```
|
||||
Movement: Arrow Keys
|
||||
Action: Numpad 0
|
||||
Menu: Numpad Enter
|
||||
Inventory: Numpad +
|
||||
Map: Numpad -
|
||||
Sprint: Numpad /
|
||||
Crouch: Numpad *
|
||||
```
|
||||
|
||||
**Implementation:**
|
||||
```javascript
|
||||
class OneHandedControls {
|
||||
setMode(mode) {
|
||||
this.mode = mode; // 'left', 'right', 'both'
|
||||
|
||||
if (mode === 'left') {
|
||||
this.bindLeftHandKeys();
|
||||
} else if (mode === 'right') {
|
||||
this.bindRightHandKeys();
|
||||
} else {
|
||||
this.bindBothHands();
|
||||
}
|
||||
}
|
||||
|
||||
bindLeftHandKeys() {
|
||||
const keys = {
|
||||
up: 'W',
|
||||
down: 'S',
|
||||
left: 'A',
|
||||
right: 'D',
|
||||
action: 'Q',
|
||||
menu: 'E',
|
||||
inventory: 'R',
|
||||
map: 'T',
|
||||
sprint: 'SHIFT',
|
||||
crouch: 'CTRL'
|
||||
};
|
||||
|
||||
this.remapKeys(keys);
|
||||
}
|
||||
|
||||
bindRightHandKeys() {
|
||||
const keys = {
|
||||
up: 'UP',
|
||||
down: 'DOWN',
|
||||
left: 'LEFT',
|
||||
right: 'RIGHT',
|
||||
action: 'NUMPAD_ZERO',
|
||||
menu: 'NUMPAD_ENTER',
|
||||
inventory: 'NUMPAD_ADD',
|
||||
map: 'NUMPAD_SUBTRACT',
|
||||
sprint: 'NUMPAD_DIVIDE',
|
||||
crouch: 'NUMPAD_MULTIPLY'
|
||||
};
|
||||
|
||||
this.remapKeys(keys);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**UI Toggle:**
|
||||
- Settings → Accessibility → One-Handed Controls
|
||||
- Options: Both Hands / Left Hand Only / Right Hand Only
|
||||
|
||||
**Keyboard:** Press **6** to cycle modes
|
||||
|
||||
---
|
||||
|
||||
### **7. AUDIO CUES** 🔔
|
||||
|
||||
**Purpose:** Additional feedback for deaf/hard-of-hearing players
|
||||
|
||||
**Features:**
|
||||
- Visual sound indicators
|
||||
- Subtitles for all dialogue (already done!)
|
||||
- Direction indicators for off-screen sounds
|
||||
- Vibration feedback (controller)
|
||||
|
||||
**Visual Sound System:**
|
||||
```javascript
|
||||
class VisualSoundCueSystem {
|
||||
constructor(scene) {
|
||||
this.scene = scene;
|
||||
this.cues = [];
|
||||
}
|
||||
|
||||
showCue(soundType, direction, distance) {
|
||||
// Create visual indicator
|
||||
const cue = this.scene.add.sprite(x, y, 'sound_cue_' + soundType);
|
||||
cue.setAlpha(0.7);
|
||||
|
||||
// Position based on direction
|
||||
const angle = this.getAngleFromDirection(direction);
|
||||
const radius = 200; // Distance from player
|
||||
|
||||
cue.x = this.scene.player.x + Math.cos(angle) * radius;
|
||||
cue.y = this.scene.player.y + Math.sin(angle) * radius;
|
||||
|
||||
// Add text label
|
||||
const label = this.scene.add.text(cue.x, cue.y + 30, soundType, {
|
||||
fontSize: '14px',
|
||||
backgroundColor: '#000000'
|
||||
});
|
||||
|
||||
// Fade out after 2 seconds
|
||||
this.scene.tweens.add({
|
||||
targets: [cue, label],
|
||||
alpha: 0,
|
||||
duration: 2000,
|
||||
onComplete: () => {
|
||||
cue.destroy();
|
||||
label.destroy();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Example usage
|
||||
onZombieGrowl(zombiePosition) {
|
||||
const direction = this.getDirectionToPlayer(zombiePosition);
|
||||
const distance = Phaser.Math.Distance.Between(
|
||||
this.scene.player.x, this.scene.player.y,
|
||||
zombiePosition.x, zombiePosition.y
|
||||
);
|
||||
|
||||
this.showCue('Zombie Growl', direction, distance);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Sound Types with Icons:**
|
||||
- 🧟 Zombie nearby
|
||||
- 💥 Explosion
|
||||
- 🔔 Quest update
|
||||
- 💬 NPC speaking
|
||||
- ⚔️ Combat
|
||||
- 🚪 Door opening/closing
|
||||
- 📦 Item pickup
|
||||
|
||||
**Keyboard:** Press **7** to toggle
|
||||
|
||||
---
|
||||
|
||||
## 🎨 UI IMPLEMENTATION
|
||||
|
||||
### **Accessibility Options Menu**
|
||||
|
||||
```javascript
|
||||
class AccessibilityMenu extends Phaser.Scene {
|
||||
create() {
|
||||
// Title
|
||||
this.add.text(width/2, 50, '♿ ACCESSIBILITY OPTIONS', {
|
||||
fontSize: '32px',
|
||||
color: '#FFFFFF'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
// Options list
|
||||
const options = [
|
||||
{ id: 1, name: 'High Contrast Mode', key: '1' },
|
||||
{ id: 2, name: 'Large Text Mode', key: '2' },
|
||||
{ id: 3, name: 'Color Blind Mode', key: '3' },
|
||||
{ id: 4, name: 'Screen Reader Support', key: '4' },
|
||||
{ id: 5, name: 'Reduce Flashing (Epilepsy)', key: '5' },
|
||||
{ id: 6, name: 'One-Handed Controls', key: '6' },
|
||||
{ id: 7, name: 'Audio Cues', key: '7' }
|
||||
];
|
||||
|
||||
let y = 150;
|
||||
options.forEach(option => {
|
||||
const text = this.add.text(100, y,
|
||||
`${option.id}. ${option.name}`,
|
||||
{ fontSize: '20px' }
|
||||
);
|
||||
|
||||
// Toggle indicator
|
||||
const indicator = this.add.text(width - 100, y,
|
||||
this.getStatusText(option.id),
|
||||
{ fontSize: '20px' }
|
||||
);
|
||||
|
||||
y += 50;
|
||||
});
|
||||
|
||||
// Footer
|
||||
this.add.text(width/2, height - 100,
|
||||
'Full accessibility settings available\n' +
|
||||
'in-game (ESC → Settings)\n\n' +
|
||||
'Tip: Press 1-7 to toggle these features',
|
||||
{
|
||||
fontSize: '16px',
|
||||
align: 'center',
|
||||
color: '#AAAAAA'
|
||||
}
|
||||
).setOrigin(0.5);
|
||||
|
||||
// OK button
|
||||
const okButton = this.add.rectangle(
|
||||
width/2, height - 40,
|
||||
200, 50,
|
||||
0x0066FF
|
||||
);
|
||||
this.add.text(width/2, height - 40, 'OK', {
|
||||
fontSize: '24px'
|
||||
}).setOrigin(0.5);
|
||||
|
||||
okButton.setInteractive();
|
||||
okButton.on('pointerdown', () => {
|
||||
this.scene.start('MainMenu');
|
||||
});
|
||||
|
||||
// Keyboard shortcuts
|
||||
this.input.keyboard.on('keydown', (event) => {
|
||||
const key = parseInt(event.key);
|
||||
if (key >= 1 && key <= 7) {
|
||||
this.toggleOption(key);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
toggleOption(id) {
|
||||
const accessibility = this.game.registry.get('accessibility');
|
||||
|
||||
switch(id) {
|
||||
case 1: accessibility.toggleHighContrast(); break;
|
||||
case 2: accessibility.toggleLargeText(); break;
|
||||
case 3: accessibility.cycleColorBlindMode(); break;
|
||||
case 4: accessibility.toggleScreenReader(); break;
|
||||
case 5: accessibility.toggleReduceFlashing(); break;
|
||||
case 6: accessibility.cycleOneHandedMode(); break;
|
||||
case 7: accessibility.toggleAudioCues(); break;
|
||||
}
|
||||
|
||||
// Refresh UI
|
||||
this.scene.restart();
|
||||
}
|
||||
|
||||
getStatusText(id) {
|
||||
const accessibility = this.game.registry.get('accessibility');
|
||||
const statuses = [
|
||||
accessibility.highContrast ? 'ON' : 'OFF',
|
||||
accessibility.largeText ? 'ON' : 'OFF',
|
||||
accessibility.colorBlindMode || 'None',
|
||||
accessibility.screenReader ? 'ON' : 'OFF',
|
||||
accessibility.reduceFlashing ? 'ON' : 'OFF',
|
||||
accessibility.oneHandedMode || 'Both',
|
||||
accessibility.audioCues ? 'ON' : 'OFF'
|
||||
];
|
||||
|
||||
return statuses[id - 1];
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 💾 PERSISTENCE
|
||||
|
||||
```javascript
|
||||
// Save accessibility settings to localStorage
|
||||
class AccessibilitySystem {
|
||||
save() {
|
||||
const settings = {
|
||||
highContrast: this.highContrast,
|
||||
largeText: this.largeText,
|
||||
colorBlindMode: this.colorBlindMode,
|
||||
screenReader: this.screenReader,
|
||||
reduceFlashing: this.reduceFlashing,
|
||||
oneHandedMode: this.oneHandedMode,
|
||||
audioCues: this.audioCues
|
||||
};
|
||||
|
||||
localStorage.setItem('accessibility_settings', JSON.stringify(settings));
|
||||
}
|
||||
|
||||
load() {
|
||||
const saved = localStorage.getItem('accessibility_settings');
|
||||
if (saved) {
|
||||
const settings = JSON.parse(saved);
|
||||
Object.assign(this, settings);
|
||||
this.applyAll();
|
||||
}
|
||||
}
|
||||
|
||||
applyAll() {
|
||||
if (this.highContrast) this.toggleHighContrast();
|
||||
if (this.largeText) this.toggleLargeText();
|
||||
if (this.colorBlindMode !== 'none') this.setColorBlindMode(this.colorBlindMode);
|
||||
if (this.screenReader) this.screenReaderSystem.enable();
|
||||
if (this.reduceFlashing) this.toggleReduceFlashing();
|
||||
if (this.oneHandedMode !== 'both') this.controls.setMode(this.oneHandedMode);
|
||||
if (this.audioCues) this.visualSoundCues.enable();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 IMPLEMENTATION TIMELINE
|
||||
|
||||
| Week | Task | Deliverable |
|
||||
|------|------|-------------|
|
||||
| **1** | Core System | AccessibilitySystem.js |
|
||||
| **2** | High Contrast + Large Text | Visual modes |
|
||||
| **3** | Color Blind Mode | 3 palette variants |
|
||||
| **4** | Screen Reader | Text-to-speech |
|
||||
| **5** | Epilepsy Safety | Effect reduction |
|
||||
| **6** | One-Handed + Audio Cues | Control schemes |
|
||||
| **7** | UI Integration | Settings menu |
|
||||
| **8** | Testing | User testing |
|
||||
|
||||
**Total Time:** 8 weeks (~2 months)
|
||||
|
||||
---
|
||||
|
||||
## ✅ SUCCESS CRITERIA
|
||||
|
||||
- [ ] All 7 features implemented
|
||||
- [ ] Keyboard shortcuts (1-7) working
|
||||
- [ ] Settings persist across sessions
|
||||
- [ ] No performance impact
|
||||
- [ ] Screen reader announces all interactions
|
||||
- [ ] Color blind modes tested with simulators
|
||||
- [ ] One-handed mode fully playable
|
||||
- [ ] Epilepsy warning on first launch
|
||||
|
||||
---
|
||||
|
||||
## 🎯 PRIORITY ORDER
|
||||
|
||||
### **Phase 1 (Critical):**
|
||||
1. **Reduce Flashing** - Safety first!
|
||||
2. **Large Text Mode** - Easy win
|
||||
3. **High Contrast Mode** - Visual accessibility
|
||||
|
||||
### **Phase 2 (Important):**
|
||||
4. **Color Blind Mode** - 8% of players
|
||||
5. **One-Handed Controls** - Physical accessibility
|
||||
6. **Audio Cues** - Deaf/HoH support
|
||||
|
||||
### **Phase 3 (Advanced):**
|
||||
7. **Screen Reader Support** - Complex but essential
|
||||
|
||||
---
|
||||
|
||||
## 🏆 INDUSTRY STANDARDS
|
||||
|
||||
Following guidelines from:
|
||||
- **Game Accessibility Guidelines** (gameaccessibilityguidelines.com)
|
||||
- **Xbox Accessibility** standards
|
||||
- **PlayStation Accessibility** standards
|
||||
- **WCAG 2.1** (Web Content Accessibility Guidelines)
|
||||
|
||||
**Certification Goal:** Xbox Accessibility Features badge
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** January 4, 2026
|
||||
**Status:** Planning → Implementation Starting Tonight
|
||||
413
MASTER_ASSET_PRODUCTION_STATUS.md
Normal file
413
MASTER_ASSET_PRODUCTION_STATUS.md
Normal file
@@ -0,0 +1,413 @@
|
||||
# 🎨 MASTER ASSET PRODUCTION STATUS & PLAN
|
||||
**Date:** January 4, 2026, 14:13 CET
|
||||
**Session:** Final Production Push
|
||||
**Style:** Style 32 (Dark-Chibi Noir)
|
||||
|
||||
---
|
||||
|
||||
## 📊 CURRENT STATUS (As of Jan 4, 14:13)
|
||||
|
||||
### **✅ COMPLETED SPRITES:** 325 total
|
||||
|
||||
**Breakdown by Category:**
|
||||
- 🏗️ **Buildings:** 36 sprites (shops, houses, barns, clinics)
|
||||
- ⚔️ **Gear/Tools:** 48 sprites (axes, hoes, swords, pickaxes, armor)
|
||||
- 🎨 **UI Elements:** 20 sprites (icons, buttons, arrows, cursors)
|
||||
- 📦 **Props:** 20 sprites (barrels, crates, chests, fences, benches)
|
||||
- 🌾 **Crops/Plants:** 8 sprites (wheat, corn, berries, trees)
|
||||
- 🗿 **Terrain:** 20 sprites (grass, dirt, stone, paths, tiles)
|
||||
- 👤 **NPCs/Characters:** 42 sprites (zombies, farmers, Ana, Kai variants)
|
||||
- 🎯 **Other:** ~131 sprites (various miscellaneous)
|
||||
|
||||
**Location:** `assets/images/STYLE_32_SESSION_JAN_04/`
|
||||
|
||||
---
|
||||
|
||||
## 🎯 TARGET: INTERIOR SPRITES (HIGH PRIORITY)
|
||||
|
||||
### **📋 FROM GENERATION LIST:**
|
||||
|
||||
**Total Interior Sprites Needed:** 60 HIGH PRIORITY
|
||||
|
||||
**Progress:**
|
||||
- ✅ **Generated so far:** ~45/60 (estimate from session)
|
||||
- ⏳ **Still needed:** ~15 sprites
|
||||
|
||||
### **Remaining 15 Sprites (CRITICAL):**
|
||||
|
||||
#### **Home Essentials (finish missing ones):**
|
||||
1. `interior_bed_kingsize.png` - 128x96px ⏳
|
||||
2. `interior_chest_locked.png` - 48x48px ⏳
|
||||
3. `interior_bookshelf.png` - 64x96px ⏳
|
||||
|
||||
#### **Kitchen Extension:**
|
||||
4. `interior_kitchen_fridge.png` - 64x96px ⏳
|
||||
5. `interior_kitchen_sink.png` - 48x48px ⏳
|
||||
6. `interior_recipe_shelf.png` - 64x64px ⏳
|
||||
|
||||
#### **Basement/Alchemy:**
|
||||
7. `interior_secret_passage.png` - 64x96px ⏳
|
||||
8. `interior_ritual_circle.png` - 96x96px ⏳
|
||||
9. `interior_memory_vault.png` - 64x64px ⏳
|
||||
|
||||
#### **Barber Shop:**
|
||||
10. `interior_piercing_tools.png` - 48x48px ⏳
|
||||
11. `interior_scissors_rack.png` - 32x64px ⏳
|
||||
|
||||
#### **Mine Equipment:**
|
||||
12. `mine_ore_vein_copper.png` - 48x48px ⏳
|
||||
13. `mine_ore_vein_gold.png` - 48x48px ⏳
|
||||
14. `mine_crystal_purple.png` - 32x48px (LIGHT SOURCE!) ⏳
|
||||
|
||||
#### **NPC Decorations:**
|
||||
15. `interior_zombie_brain_jar.png` - 32x48px ⏳
|
||||
|
||||
---
|
||||
|
||||
## ⏰ API QUOTA STATUS
|
||||
|
||||
**Current Time:** 14:13 CET
|
||||
**Next Reset:** 14:19 CET
|
||||
**Time to Reset:** **6 MINUTES!** ⏰
|
||||
|
||||
**When API Resets:**
|
||||
- ✅ Generate final 15 interior sprites
|
||||
- ✅ Complete 60/60 HIGH PRIORITY goal
|
||||
- ✅ All critical assets ready for system integration!
|
||||
|
||||
---
|
||||
|
||||
## 📈 PRODUCTION SUMMARY
|
||||
|
||||
### **Session Stats (Jan 4, 2026):**
|
||||
```
|
||||
SPRITES GENERATED TODAY: 50+
|
||||
- Buildings V2 complete
|
||||
- Gear upgrades complete
|
||||
- Started interior objects
|
||||
- High Priority: 45/60 done
|
||||
```
|
||||
|
||||
### **Overall Project Stats:**
|
||||
```
|
||||
TOTAL SPRITES: 325 in Style 32
|
||||
- Production time: ~8-10 hours
|
||||
- Quality: High (5px outlines, chroma green bg)
|
||||
- Style compliance: 100%
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🗺️ COMPLETE ASSET ROADMAP
|
||||
|
||||
### **PHASE 1** ✅ **COMPLETE** (Dec - Jan)
|
||||
**Buildings & Exteriors:** 61 sprites
|
||||
- All shops (complete + ruined variants)
|
||||
- Houses (all tiers)
|
||||
- Farm structures
|
||||
- Town buildings
|
||||
|
||||
### **PHASE 2** ✅ **COMPLETE** (Jan 3-4)
|
||||
**Gear & Equipment:** 44+ sprites
|
||||
- All tools (6 tiers)
|
||||
- Weapons
|
||||
- Armor sets
|
||||
- Accessories
|
||||
|
||||
### **PHASE 3** 🔄 **IN PROGRESS** (Jan 4, afternoon)
|
||||
**Interior Objects (HIGH PRIORITY):** 45/60 done
|
||||
- Home essentials (mostly done)
|
||||
- Kitchen items (in progress)
|
||||
- Basement/alchemy lab
|
||||
- Shop interiors
|
||||
- Mine equipment
|
||||
|
||||
### **PHASE 4** ⏳ **NEXT** (Jan 5-7)
|
||||
**Medium Priority Interiors:** 110 sprites
|
||||
- Advanced furniture
|
||||
- Decorations
|
||||
- NPC-specific items
|
||||
- Seasonal decorations
|
||||
- Special items
|
||||
|
||||
### **PHASE 5** ⏳ **FUTURE** (Week 2+)
|
||||
**Expansion Assets:** ~1000+ sprites
|
||||
- 18 biome tilesets
|
||||
- Enemy sprites (250+ types)
|
||||
- Anomalous zone assets
|
||||
- Boss sprites (24 bosses)
|
||||
- Special effects
|
||||
|
||||
---
|
||||
|
||||
## 🎯 TONIGHT PRODUCTION PLAN (14:19 - 18:00)
|
||||
|
||||
### **IMMEDIATE (14:19 - 14:40):**
|
||||
**When API resets in 6 minutes:**
|
||||
|
||||
Generate final 15 HIGH PRIORITY interior sprites:
|
||||
1. Bed (kingsize)
|
||||
2. Chest (locked)
|
||||
3. Bookshelf
|
||||
4. Kitchen fridge
|
||||
5. Kitchen sink
|
||||
6. Recipe shelf
|
||||
7. Secret passage
|
||||
8. Ritual circle
|
||||
9. Memory vault
|
||||
10. Piercing tools
|
||||
11. Scissors rack
|
||||
12. Copper ore vein
|
||||
13. Gold ore vein
|
||||
14. Purple crystal (light source!)
|
||||
15. Brain jar
|
||||
|
||||
**Time:** ~20 minutes (15 sprites at 30/hour rate)
|
||||
|
||||
### **EVENING SESSION (15:00 - 18:00):**
|
||||
|
||||
**Option A: Continue with Medium Priority (~90 more sprites)**
|
||||
- Advanced furniture
|
||||
- NPC decorations
|
||||
- Seasonal items
|
||||
|
||||
**Option B: Multi-Language Implementation**
|
||||
- Run translation scripts
|
||||
- Generate 48 voice files (4 languages)
|
||||
- Test integration
|
||||
|
||||
**RECOMMENDATION:** **Option A** - Complete Phase 3 entirely, then do languages tomorrow!
|
||||
|
||||
---
|
||||
|
||||
## 📋 DETAILED REMAINING ASSET BREAKDOWN
|
||||
|
||||
### **After Tonight's 15 Sprites:**
|
||||
|
||||
**HIGH PRIORITY: 60/60** ✅ COMPLETE!
|
||||
|
||||
**MEDIUM PRIORITY: 110 sprites** ⏳
|
||||
```
|
||||
Living Room: 15 sprites
|
||||
- Sofas, armchairs, coffee tables
|
||||
- Fireplace, TV, paintings
|
||||
|
||||
Bedroom Extended: 12 sprites
|
||||
- Dressers, vanity, rugs
|
||||
- Lamps, curtains, mirrors
|
||||
|
||||
Kitchen/Dining: 18 sprites
|
||||
- Chairs, tables, cabinets
|
||||
- Dishware, appliances
|
||||
|
||||
Outdoors: 20 sprites
|
||||
- Garden furniture, grills
|
||||
- Planters, decorations
|
||||
|
||||
Special Rooms: 25 sprites
|
||||
- Library, observatory
|
||||
- Trophy room, shrine
|
||||
|
||||
Seasonal: 20 sprites
|
||||
- Halloween, Christmas, Easter
|
||||
- Summer/winter variants
|
||||
```
|
||||
|
||||
**LOW PRIORITY: Infinite** 🌌
|
||||
```
|
||||
- Biome-specific decorations
|
||||
- Rare collectibles
|
||||
- Easter eggs
|
||||
- Achievement items
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 PRODUCTION VELOCITY
|
||||
|
||||
**Current Rate:** ~30 sprites/hour (with API)
|
||||
|
||||
**Realistic Night Schedule:**
|
||||
```
|
||||
14:19 - 14:40 | 15 sprites (HIGH PRIORITY complete!)
|
||||
15:00 - 16:00 | 30 sprites (medium priority)
|
||||
16:00 - 17:00 | 30 sprites (medium priority)
|
||||
17:00 - 18:00 | 30 sprites (medium priority)
|
||||
```
|
||||
|
||||
**TONIGHT TOTAL:** 105 sprites
|
||||
**CUMULATIVE:** 325 + 105 = **430 sprites** in Style 32!
|
||||
|
||||
---
|
||||
|
||||
## 📊 ASSET COMPLETENESS BY SYSTEM
|
||||
|
||||
### **Systems Ready for Integration:**
|
||||
|
||||
✅ **Sleep System**
|
||||
- All beds generated (sleeping bag, wooden, kingsize)
|
||||
- UI icons ready
|
||||
- **Status:** 100% ready
|
||||
|
||||
✅ **Crafting Tables System**
|
||||
- Small/large tables generated
|
||||
- Tool racks ready
|
||||
- Workshop benches ready
|
||||
- **Status:** 100% ready
|
||||
|
||||
🔄 **Bakery Shop** (after tonight)
|
||||
- Oven: ✅
|
||||
- Counter: ✅
|
||||
- Shelf: ⏳ (tonight)
|
||||
- Flour sacks: ✅
|
||||
- Tools: ✅
|
||||
- **Status:** 80% → 100% tonight
|
||||
|
||||
🔄 **Barber Shop** (after tonight)
|
||||
- Chair: ✅
|
||||
- Mirror: ✅
|
||||
- Tools: ⏳ (scissors rack tonight)
|
||||
- Dye shelf: ✅
|
||||
- **Status:** 80% → 100% tonight
|
||||
|
||||
✅ **Lawyer Office**
|
||||
- Desk: ✅
|
||||
- Lamp: ✅
|
||||
- Books: ✅
|
||||
- **Status:** 100% ready
|
||||
|
||||
🔄 **Zombie Miner System** (after tonight)
|
||||
- Entrance portal: ✅
|
||||
- Ladders: ✅
|
||||
- Elevator: ✅
|
||||
- Cart tracks: ✅
|
||||
- Stone crusher: ✅
|
||||
- Ore veins: ⏳ (tonight - copper, gold)
|
||||
- Crystals: ⏳ (tonight - purple light source)
|
||||
- **Status:** 75% → 100% tonight
|
||||
|
||||
✅ **Town Growth System**
|
||||
- All buildings: ✅
|
||||
- Town signs: ✅
|
||||
- Population display: ✅
|
||||
- **Status:** 100% ready
|
||||
|
||||
---
|
||||
|
||||
## 🎯 FINAL GOALS
|
||||
|
||||
### **END OF TONIGHT:**
|
||||
- ✅ 60/60 HIGH PRIORITY sprites complete
|
||||
- ✅ All 9 game systems have required sprites
|
||||
- ✅ 430+ total sprites in Style 32
|
||||
- ✅ Ready for full system integration testing
|
||||
|
||||
### **END OF WEEK:**
|
||||
- ✅ 170/170 Interior sprites complete (HIGH + MEDIUM)
|
||||
- ✅ Multi-language system ready (5 languages)
|
||||
- ✅ Accessibility system implemented
|
||||
- ✅ Full game playable in Slovenian + English
|
||||
|
||||
### **END OF MONTH:**
|
||||
- ✅ All biome tilesets generated
|
||||
- ✅ Enemy sprite library complete
|
||||
- ✅ Boss sprites ready
|
||||
- ✅ Early Access launch preparation
|
||||
|
||||
---
|
||||
|
||||
## 💾 COMMIT STRATEGY
|
||||
|
||||
**After finishing tonight's 15 sprites:**
|
||||
```bash
|
||||
git add assets/images/STYLE_32_SESSION_JAN_04/
|
||||
git commit -m "🎨 PHASE 3 COMPLETE - All 60 HIGH PRIORITY Interior Sprites
|
||||
|
||||
COMPLETED TONIGHT (15 sprites):
|
||||
✅ Kingsize bed
|
||||
✅ Locked chest
|
||||
✅ Bookshelf
|
||||
✅ Kitchen fridge
|
||||
✅ Kitchen sink
|
||||
✅ Recipe shelf
|
||||
✅ Secret passage
|
||||
✅ Ritual circle
|
||||
✅ Memory vault
|
||||
✅ Piercing tools
|
||||
✅ Scissors rack
|
||||
✅ Copper ore vein
|
||||
✅ Gold ore vein
|
||||
✅ Purple crystal (light source)
|
||||
✅ Brain jar
|
||||
|
||||
TOTAL STATUS:
|
||||
- HIGH PRIORITY: 60/60 ✅ COMPLETE
|
||||
- Total Style 32: 340 sprites
|
||||
- All 9 systems ready for integration!
|
||||
|
||||
Next: Medium priority interiors (110 sprites)"
|
||||
|
||||
git push
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🎊 ACHIEVEMENT UNLOCKED
|
||||
|
||||
**When 60/60 Complete:**
|
||||
```
|
||||
🏆 ASSET MASTER
|
||||
Generated 60 critical interior sprites
|
||||
Style: Perfect (Dark-Chibi Noir)
|
||||
Quality: 5px outlines, chroma green bg
|
||||
Systems: All 9 fully equipped
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⏭️ WHAT'S NEXT AFTER TONIGHT?
|
||||
|
||||
###**Tomorrow (Sunday, Jan 5):**
|
||||
1. Multi-language voiceover generation (4 languages, 48 files)
|
||||
2. Localization JSON creation
|
||||
3. Language selector UI
|
||||
4. Testing all languages
|
||||
|
||||
### **Monday, Jan 7:**
|
||||
1. Accessibility system implementation
|
||||
2. Medium priority interior sprites (if time)
|
||||
3. Integration testing
|
||||
|
||||
### **Week 2:**
|
||||
1. Biome tileset generation
|
||||
2. Enemy sprites
|
||||
3. Full game polish
|
||||
|
||||
---
|
||||
|
||||
## 📈 PROGRESS VISUALIZATION
|
||||
|
||||
```
|
||||
STYLE 32 SPRITE PRODUCTION
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
December: Buildings (61) ████████░░ 80%
|
||||
|
||||
January Week 1: Gear (44) ██████████ 100% ✅
|
||||
UI (20) ██████████ 100% ✅
|
||||
Props (20) ██████████ 100% ✅
|
||||
|
||||
January Week 2: Interior HIGH ███████░░░ 75% → 100% tonight!
|
||||
Interior MED ░░░░░░░░░░ 0% → start tomorrow
|
||||
|
||||
Target: 500 sprites by end of week
|
||||
1000+ sprites by end of month
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**Last Updated:** January 4, 2026, 14:13 CET
|
||||
**API Reset:** In 6 minutes! ⏰
|
||||
**Status:** Ready to complete HIGH PRIORITY phase! 🚀
|
||||
|
||||
**LET'S FINISH THIS! 💀✨**
|
||||
167
scripts/translate_all.py
Normal file
167
scripts/translate_all.py
Normal file
@@ -0,0 +1,167 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
DeepL Translation Script
|
||||
Translates Slovenian game text to 4 languages
|
||||
"""
|
||||
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
try:
|
||||
import deepl
|
||||
DEEPL_AVAILABLE = True
|
||||
except ImportError:
|
||||
DEEPL_AVAILABLE = False
|
||||
print("⚠️ DeepL not installed. Install with: pip3 install deepl")
|
||||
print("Using fallback translations (manual review needed!)")
|
||||
|
||||
# Paths
|
||||
LOCALIZATION_DIR = Path("assets/localization")
|
||||
LOCALIZATION_DIR.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Language mappings
|
||||
LANGUAGES = {
|
||||
'EN-US': 'en-US.json',
|
||||
'DE': 'de-DE.json',
|
||||
'IT': 'it-IT.json',
|
||||
'ZH': 'zh-CN.json'
|
||||
}
|
||||
|
||||
def translate_dict(data, translator, target_lang):
|
||||
"""Recursively translate all strings in dictionary"""
|
||||
if isinstance(data, dict):
|
||||
return {k: translate_dict(v, translator, target_lang) for k, v in data.items()}
|
||||
elif isinstance(data, list):
|
||||
return [translate_dict(item, translator, target_lang) for item in data]
|
||||
elif isinstance(data, str):
|
||||
if DEEPL_AVAILABLE and translator:
|
||||
try:
|
||||
result = translator.translate_text(data, target_lang=target_lang)
|
||||
return result.text
|
||||
except Exception as e:
|
||||
print(f" ⚠️ Translation error: {e}")
|
||||
return data
|
||||
else:
|
||||
return data # Fallback: return original
|
||||
else:
|
||||
return data
|
||||
|
||||
def create_slovenian_json():
|
||||
"""Extract Slovenian text from PrologueScene"""
|
||||
slovenian_data = {
|
||||
"ui": {
|
||||
"skip": "Pritisni ESC za preskok",
|
||||
"autoAdvance": "Pritisni PRESLEDNICA za samodejno nadaljevanje"
|
||||
},
|
||||
"prologue": {
|
||||
"01": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Leto 2084. Zombi virus je uničil svet."
|
||||
},
|
||||
"02": {
|
||||
"speaker": "KAI",
|
||||
"text": "Moje ime je Kai Marković. Star sem štirinajst let."
|
||||
},
|
||||
"03": {
|
||||
"speaker": "KAI",
|
||||
"text": "Moja dvojčica Ana in jaz, sva zelo povezana. Nezlomljiv vez."
|
||||
},
|
||||
"04": {
|
||||
"speaker": "KAI",
|
||||
"text": "Naša starša sta bila znanstvenika. Raziskovala sta virusne mutacije."
|
||||
},
|
||||
"05": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Tretji dan izbruha. Horda zombijev napada družinsko hišo."
|
||||
},
|
||||
"06": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Starša se žrtvujeta, da rešita dvojčka. Zadnji besede: 'Beži, Kai! Zaščiti Ano!'"
|
||||
},
|
||||
"07": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Iz senc se pojavi Orjaški Troll Kralj. Poslal ga je zlobni doktor Krnić."
|
||||
},
|
||||
"08": {
|
||||
"speaker": "ANA",
|
||||
"text": "KAI! REŠI ME! KAIII!"
|
||||
},
|
||||
"09": {
|
||||
"speaker": "KAI",
|
||||
"text": "ANA! NE! VRNITE MI JO!"
|
||||
},
|
||||
"10": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Kai se spremeni v Alfa Hibrida. Vijolične oči. Moč nadzora nad zombiji."
|
||||
},
|
||||
"11": {
|
||||
"speaker": "NARRATOR",
|
||||
"text": "Šest mesecev kasneje. Kai se zbudi na majhni kmetiji. Ana je izginila."
|
||||
},
|
||||
"12": {
|
||||
"speaker": "KAI",
|
||||
"text": "Moram jo najti. Ne glede na to, kaj bo potrebno. Ana, prihajam!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
output_file = LOCALIZATION_DIR / "sl-SI.json"
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(slovenian_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"✅ Created {output_file}")
|
||||
return slovenian_data
|
||||
|
||||
def main():
|
||||
print("🌍 TRANSLATION SYSTEM")
|
||||
print("=" * 50)
|
||||
|
||||
# Step 1: Create Slovenian JSON
|
||||
print("\n📝 Step 1: Creating Slovenian source...")
|
||||
sl_data = create_slovenian_json()
|
||||
|
||||
# Step 2: Initialize DeepL
|
||||
translator = None
|
||||
if DEEPL_AVAILABLE:
|
||||
api_key = os.environ.get('DEEPL_API_KEY')
|
||||
if api_key:
|
||||
try:
|
||||
translator = deepl.Translator(api_key)
|
||||
print(f"✅ DeepL initialized")
|
||||
except Exception as e:
|
||||
print(f"⚠️ DeepL error: {e}")
|
||||
else:
|
||||
print("⚠️ DEEPL_API_KEY not set in environment")
|
||||
|
||||
# Step 3: Translate to each language
|
||||
print("\n🔄 Step 2: Translating to 4 languages...\n")
|
||||
|
||||
for lang_code, filename in LANGUAGES.items():
|
||||
print(f" Translating to {filename}...")
|
||||
|
||||
if translator:
|
||||
translated_data = translate_dict(sl_data, translator, lang_code)
|
||||
else:
|
||||
# Fallback: Use Slovenian text (manual review needed)
|
||||
print(f" ⚠️ Using fallback (Slovenian text)")
|
||||
translated_data = sl_data.copy()
|
||||
|
||||
output_file = LOCALIZATION_DIR / filename
|
||||
with open(output_file, 'w', encoding='utf-8') as f:
|
||||
json.dump(translated_data, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f" ✅ Created {filename}")
|
||||
|
||||
print("\n🎉 Translation complete!")
|
||||
print(f"\n📁 Files created in {LOCALIZATION_DIR}:")
|
||||
for file in LOCALIZATION_DIR.glob("*.json"):
|
||||
size = file.stat().st_size
|
||||
print(f" - {file.name} ({size:,} bytes)")
|
||||
|
||||
if not translator:
|
||||
print("\n⚠️ NOTE: Files created with Slovenian text (DeepL unavailable)")
|
||||
print("Manual translation or DeepL API key required for production!")
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user