478 lines
16 KiB
JavaScript
478 lines
16 KiB
JavaScript
/**
|
|
* LOCALIZATION SYSTEM
|
|
* Multi-language support with JSON translation files
|
|
*/
|
|
class LocalizationSystem {
|
|
constructor() {
|
|
this.currentLang = 'en'; // Default language
|
|
this.translations = {};
|
|
this.supportedLanguages = ['slo', 'en', 'de', 'it', 'cn'];
|
|
|
|
// Load from localStorage
|
|
const savedLang = localStorage.getItem('novafarma_language');
|
|
if (savedLang && this.supportedLanguages.includes(savedLang)) {
|
|
this.currentLang = savedLang;
|
|
} else {
|
|
// AUTO-DETECT OS LANGUAGE (first launch)
|
|
this.currentLang = this.detectOSLanguage();
|
|
console.log(`🌍 Auto-detected language: ${this.getCurrentLanguageName()}`);
|
|
localStorage.setItem('novafarma_language', this.currentLang);
|
|
}
|
|
|
|
this.loadTranslations();
|
|
}
|
|
|
|
/**
|
|
* AUTO-DETECT OS LANGUAGE
|
|
*/
|
|
detectOSLanguage() {
|
|
// Get browser/system language
|
|
const browserLang = navigator.language || navigator.userLanguage || 'en';
|
|
const langCode = browserLang.toLowerCase().split('-')[0]; // e.g. 'en-US' → 'en'
|
|
|
|
console.log(`🖥️ System language detected: ${browserLang} (${langCode})`);
|
|
|
|
// Map to supported language
|
|
const langMap = {
|
|
'sl': 'slo', // Slovenian
|
|
'en': 'en', // English
|
|
'de': 'de', // German
|
|
'it': 'it', // Italian
|
|
'zh': 'cn', // Chinese
|
|
'cn': 'cn' // Chinese (alternative)
|
|
};
|
|
|
|
const detected = langMap[langCode] || 'en';
|
|
console.log(`✅ Mapped to game language: ${detected}`);
|
|
|
|
return detected;
|
|
}
|
|
|
|
/**
|
|
* GET CURRENT LANGUAGE NAME
|
|
*/
|
|
getCurrentLanguageName() {
|
|
return this.getLanguageName(this.currentLang);
|
|
}
|
|
|
|
loadTranslations() {
|
|
// Embedded translations (inline for simplicity)
|
|
this.translations = {
|
|
'slo': {
|
|
// UI
|
|
'ui.inventory': 'Inventar',
|
|
'ui.crafting': 'Izdelovanje',
|
|
'ui.health': 'Zdravje',
|
|
'ui.hunger': 'Lakota',
|
|
'ui.oxygen': 'Kisik',
|
|
'ui.day': 'Dan',
|
|
'ui.season': 'Letni čas',
|
|
'ui.hp': 'ZDR',
|
|
'ui.hun': 'LAK',
|
|
'ui.h2o': 'H2O',
|
|
'ui.xp': 'IZK',
|
|
'ui.lv': 'NIV',
|
|
|
|
// Items
|
|
'item.wood': 'Les',
|
|
'item.stone': 'Kamen',
|
|
'item.seeds': 'Semena',
|
|
'item.wheat': 'Pšenica',
|
|
'item.corn': 'Koruza',
|
|
|
|
// Actions
|
|
'action.plant': 'Posadi',
|
|
'action.harvest': 'Požanji',
|
|
'action.craft': 'Izdelaj',
|
|
'action.build': 'Zgradi',
|
|
|
|
// Seasons
|
|
'season.spring': 'Pomlad',
|
|
'season.summer': 'Poletje',
|
|
'season.autumn': 'Jesen',
|
|
'season.winter': 'Zima',
|
|
|
|
// Pause Menu
|
|
'pause.title': 'PAVZA',
|
|
'pause.resume': '▶ Nadaljuj',
|
|
'pause.save': '💾 Shrani igro',
|
|
'pause.settings': '⚙️ Nastavitve',
|
|
'pause.quit': '🚪 Izhod v meni',
|
|
'pause.hint': 'Pritisni ESC za nadaljevanje',
|
|
|
|
// Messages
|
|
'msg.demo_end': 'Demo končan! Hvala za igranje.',
|
|
'msg.freezing': '❄️ Zmrzuješ!',
|
|
'msg.overheating': '🔥 Pregrevanje!'
|
|
},
|
|
'en': {
|
|
// UI
|
|
'ui.inventory': 'Inventory',
|
|
'ui.crafting': 'Crafting',
|
|
'ui.health': 'Health',
|
|
'ui.hunger': 'Hunger',
|
|
'ui.oxygen': 'Oxygen',
|
|
'ui.day': 'Day',
|
|
'ui.season': 'Season',
|
|
'ui.hp': 'HP',
|
|
'ui.hun': 'HUN',
|
|
'ui.h2o': 'H2O',
|
|
'ui.xp': 'XP',
|
|
'ui.lv': 'LV',
|
|
|
|
// Items
|
|
'item.wood': 'Wood',
|
|
'item.stone': 'Stone',
|
|
'item.seeds': 'Seeds',
|
|
'item.wheat': 'Wheat',
|
|
'item.corn': 'Corn',
|
|
|
|
// Actions
|
|
'action.plant': 'Plant',
|
|
'action.harvest': 'Harvest',
|
|
'action.craft': 'Craft',
|
|
'action.build': 'Build',
|
|
|
|
// Seasons
|
|
'season.spring': 'Spring',
|
|
'season.summer': 'Summer',
|
|
'season.autumn': 'Autumn',
|
|
'season.winter': 'Winter',
|
|
|
|
// Pause Menu
|
|
'pause.title': 'PAUSED',
|
|
'pause.resume': '▶ Resume',
|
|
'pause.save': '💾 Save Game',
|
|
'pause.settings': '⚙️ Settings',
|
|
'pause.quit': '🚪 Quit to Menu',
|
|
'pause.hint': 'Press ESC to resume',
|
|
|
|
// Messages
|
|
'msg.demo_end': 'Demo Ended! Thanks for playing.',
|
|
'msg.freezing': '❄️ Freezing!',
|
|
'msg.overheating': '🔥 Overheating!'
|
|
},
|
|
'de': {
|
|
// UI
|
|
'ui.inventory': 'Inventar',
|
|
'ui.crafting': 'Handwerk',
|
|
'ui.health': 'Gesundheit',
|
|
'ui.hunger': 'Hunger',
|
|
'ui.oxygen': 'Sauerstoff',
|
|
'ui.day': 'Tag',
|
|
'ui.season': 'Jahreszeit',
|
|
'ui.hp': 'LP',
|
|
'ui.hun': 'HUN',
|
|
'ui.h2o': 'H2O',
|
|
'ui.xp': 'EP',
|
|
'ui.lv': 'ST',
|
|
|
|
// Items
|
|
'item.wood': 'Holz',
|
|
'item.stone': 'Stein',
|
|
'item.seeds': 'Samen',
|
|
'item.wheat': 'Weizen',
|
|
'item.corn': 'Mais',
|
|
|
|
// Actions
|
|
'action.plant': 'Pflanzen',
|
|
'action.harvest': 'Ernten',
|
|
'action.craft': 'Herstellen',
|
|
'action.build': 'Bauen',
|
|
|
|
// Seasons
|
|
'season.spring': 'Frühling',
|
|
'season.summer': 'Sommer',
|
|
'season.autumn': 'Herbst',
|
|
'season.winter': 'Winter',
|
|
|
|
// Pause Menu
|
|
'pause.title': 'PAUSIERT',
|
|
'pause.resume': '▶ Fortsetzen',
|
|
'pause.save': '💾 Spiel speichern',
|
|
'pause.settings': '⚙️ Einstellungen',
|
|
'pause.quit': '🚪 Zum Menü',
|
|
'pause.hint': 'ESC drücken zum Fortsetzen',
|
|
|
|
// Messages
|
|
'msg.demo_end': 'Demo beendet! Danke fürs Spielen.',
|
|
'msg.freezing': '❄️ Du erfrierst!',
|
|
'msg.overheating': '🔥 Überhitzung!'
|
|
},
|
|
'it': {
|
|
// UI
|
|
'ui.inventory': 'Inventario',
|
|
'ui.crafting': 'Artigianato',
|
|
'ui.health': 'Salute',
|
|
'ui.hunger': 'Fame',
|
|
'ui.oxygen': 'Ossigeno',
|
|
'ui.day': 'Giorno',
|
|
'ui.season': 'Stagione',
|
|
'ui.hp': 'PS',
|
|
'ui.hun': 'FAM',
|
|
'ui.h2o': 'H2O',
|
|
'ui.xp': 'ESP',
|
|
'ui.lv': 'LIV',
|
|
|
|
// Items
|
|
'item.wood': 'Legno',
|
|
'item.stone': 'Pietra',
|
|
'item.seeds': 'Semi',
|
|
'item.wheat': 'Grano',
|
|
'item.corn': 'Mais',
|
|
|
|
// Actions
|
|
'action.plant': 'Pianta',
|
|
'action.harvest': 'Raccogli',
|
|
'action.craft': 'Crea',
|
|
'action.build': 'Costruisci',
|
|
|
|
// Seasons
|
|
'season.spring': 'Primavera',
|
|
'season.summer': 'Estate',
|
|
'season.autumn': 'Autunno',
|
|
'season.winter': 'Inverno',
|
|
|
|
// Pause Menu
|
|
'pause.title': 'IN PAUSA',
|
|
'pause.resume': '▶ Riprendi',
|
|
'pause.save': '💾 Salva gioco',
|
|
'pause.settings': '⚙️ Impostazioni',
|
|
'pause.quit': '🚪 Esci al menu',
|
|
'pause.hint': 'Premi ESC per riprendere',
|
|
|
|
// Messages
|
|
'msg.demo_end': 'Demo terminata! Grazie per aver giocato.',
|
|
'msg.freezing': '❄️ Stai congelando!',
|
|
'msg.overheating': '🔥 Surriscaldamento!'
|
|
},
|
|
'cn': {
|
|
// UI
|
|
'ui.inventory': '库存',
|
|
'ui.crafting': '制作',
|
|
'ui.health': '健康',
|
|
'ui.hunger': '饥饿',
|
|
'ui.oxygen': '氧气',
|
|
'ui.day': '天数',
|
|
'ui.season': '季节',
|
|
'ui.hp': '生命',
|
|
'ui.hun': '饥饿',
|
|
'ui.h2o': '水',
|
|
'ui.xp': '经验',
|
|
'ui.lv': '等级',
|
|
|
|
// Items
|
|
'item.wood': '木材',
|
|
'item.stone': '石头',
|
|
'item.seeds': '种子',
|
|
'item.wheat': '小麦',
|
|
'item.corn': '玉米',
|
|
|
|
// Actions
|
|
'action.plant': '种植',
|
|
'action.harvest': '收获',
|
|
'action.craft': '制作',
|
|
'action.build': '建造',
|
|
|
|
// Seasons
|
|
'season.spring': '春天',
|
|
'season.summer': '夏天',
|
|
'season.autumn': '秋天',
|
|
'season.winter': '冬天',
|
|
|
|
// Pause Menu
|
|
'pause.title': '暂停',
|
|
'pause.resume': '▶ 继续',
|
|
'pause.save': '💾 保存游戏',
|
|
'pause.settings': '⚙️ 设置',
|
|
'pause.quit': '🚪 退出到菜单',
|
|
'pause.hint': '按ESC继续',
|
|
|
|
// Messages
|
|
'msg.demo_end': '演示结束!感谢游玩。',
|
|
'msg.freezing': '❄️ 你在冻僵!',
|
|
'msg.overheating': '🔥 过热!'
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Get translated string
|
|
* @param {string} key - Translation key (e.g. 'ui.inventory')
|
|
* @param {string} fallback - Fallback text if translation missing
|
|
*/
|
|
t(key, fallback = key) {
|
|
const lang = this.translations[this.currentLang];
|
|
if (lang && lang[key]) {
|
|
return lang[key];
|
|
}
|
|
|
|
// Fallback to English
|
|
const enLang = this.translations['en'];
|
|
if (enLang && enLang[key]) {
|
|
return enLang[key];
|
|
}
|
|
|
|
return fallback;
|
|
}
|
|
|
|
/**
|
|
* LOAD INTRO TEXTS FROM JSON
|
|
*/
|
|
async loadIntroTexts() {
|
|
try {
|
|
const response = await fetch('assets/localization.json');
|
|
const data = await response.json();
|
|
|
|
// Merge intro texts into translations
|
|
for (const lang in data) {
|
|
if (!this.translations[lang]) {
|
|
this.translations[lang] = {};
|
|
}
|
|
|
|
// Add intro polaroid texts
|
|
if (data[lang].intro_polaroids) {
|
|
this.translations[lang].intro_polaroids = data[lang].intro_polaroids;
|
|
}
|
|
|
|
// Add menu texts
|
|
if (data[lang].menu) {
|
|
Object.assign(this.translations[lang], data[lang].menu);
|
|
}
|
|
|
|
// Add title/subtitle
|
|
if (data[lang].game_title) {
|
|
this.translations[lang].game_title = data[lang].game_title;
|
|
}
|
|
if (data[lang].subtitle) {
|
|
this.translations[lang].subtitle = data[lang].subtitle;
|
|
}
|
|
}
|
|
|
|
console.log('✅ Intro texts loaded from JSON');
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('⚠️ Could not load localization.json:', error);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* GET INTRO POLAROID TEXT
|
|
*/
|
|
getIntroText(polaroidKey) {
|
|
const lang = this.translations[this.currentLang];
|
|
if (lang && lang.intro_polaroids && lang.intro_polaroids[polaroidKey]) {
|
|
return lang.intro_polaroids[polaroidKey];
|
|
}
|
|
|
|
// Fallback to English
|
|
const enLang = this.translations['en'];
|
|
if (enLang && enLang.intro_polaroids && enLang.intro_polaroids[polaroidKey]) {
|
|
return enLang.intro_polaroids[polaroidKey];
|
|
}
|
|
|
|
return polaroidKey; // Return key if not found
|
|
}
|
|
|
|
/**
|
|
* GET VOICE FILE PATH (switches language folder)
|
|
*/
|
|
getVoicePath(character, index, format = 'mp3') {
|
|
const langSuffix = this.currentLang === 'en' ? 'en' : 'sl';
|
|
|
|
// Map language codes to voice folders
|
|
if (this.currentLang === 'slo') {
|
|
// Slovenian voices in /sl/ folder
|
|
return `assets/audio/voiceover/sl/${character}_${String(index).padStart(2, '0')}.${format}`;
|
|
} else {
|
|
// English voices (default for DE, IT, CN too)
|
|
return `assets/audio/voiceover/en/${character}_en_${String(index).padStart(2, '0')}.${format}`;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* CHECK IF VOICE FILE EXISTS FOR CURRENT LANGUAGE
|
|
*/
|
|
hasVoiceForLanguage(character, index) {
|
|
// Slovenian and English have full voiceovers
|
|
// Other languages fall back to English
|
|
return this.currentLang === 'slo' || this.currentLang === 'en';
|
|
}
|
|
|
|
/**
|
|
* Set current language
|
|
*/
|
|
setLanguage(langCode) {
|
|
if (this.supportedLanguages.includes(langCode)) {
|
|
this.currentLang = langCode;
|
|
localStorage.setItem('novafarma_language', langCode);
|
|
console.log(`🌍 Language changed to: ${langCode}`);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
getCurrentLanguage() {
|
|
return this.currentLang;
|
|
}
|
|
|
|
getSupportedLanguages() {
|
|
return this.supportedLanguages.map(code => ({
|
|
code,
|
|
name: this.getLanguageName(code)
|
|
}));
|
|
}
|
|
|
|
getLanguageName(code) {
|
|
const names = {
|
|
'slo': 'Slovenščina',
|
|
'en': 'English',
|
|
'de': 'Deutsch',
|
|
'it': 'Italiano',
|
|
'cn': '中文'
|
|
};
|
|
return names[code] || code;
|
|
}
|
|
/**
|
|
* INJECT EXTERNAL JSON DATA (loaded via Phaser)
|
|
*/
|
|
setExternalData(data) {
|
|
try {
|
|
// Merge intro texts into translations
|
|
for (const lang in data) {
|
|
if (!this.translations[lang]) {
|
|
this.translations[lang] = {};
|
|
}
|
|
|
|
// Add intro polaroid texts
|
|
if (data[lang].intro_polaroids) {
|
|
this.translations[lang].intro_polaroids = data[lang].intro_polaroids;
|
|
}
|
|
|
|
// Add menu texts
|
|
if (data[lang].menu) {
|
|
Object.assign(this.translations[lang], data[lang].menu);
|
|
}
|
|
|
|
// Add title/subtitle
|
|
if (data[lang].game_title) {
|
|
this.translations[lang].game_title = data[lang].game_title;
|
|
}
|
|
if (data[lang].subtitle) {
|
|
this.translations[lang].subtitle = data[lang].subtitle;
|
|
}
|
|
}
|
|
|
|
console.log('✅ Localization data injected successfully');
|
|
return true;
|
|
} catch (error) {
|
|
console.warn('⚠️ Could not inject localization data:', error);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Global instance
|
|
window.LocalizationSystem = LocalizationSystem;
|