# Plan de Implementación del Sistema de Traducción ## Objetivo Crear un sistema de traducción dinámico que permita cargar idiomas desde archivos JSON externos **sin modificar los textos hardcodeados existentes**. Los textos en el código bleiben en inglés como fallback por defecto. --- ## 1. Funcionamiento Clave ### 1.1 Comportamiento - **Sin carpeta de idiomas**: El juego funciona exactamente como ahora con los textos hardcodeados en inglés - **Con carpeta de idiomas**: Cuando el usuario selecciona un idioma, se carga el JSON y se traducen los textos disponibles - **Fallback**: Si una traducción no existe en el JSON, se usa el texto hardcodeado (inglés) - **Persistencia**: El idioma seleccionado se guarda en la configuración y se carga automáticamente al iniciar ### 1.2 Carpeta de Idiomas (opcional) ``` /lenguajes/ ├── Espanol.json ├── Portugues.json └── (otros idiomas).json ``` **Nota**: No se requiere English.json porque el inglés ya está hardcodeado en el código. --- ## 2. Archivos Creados | Archivo | Descripción | |---------|-------------| | `soh/soh/SohGui/LanguageManager.h` | Header del manager de idiomas | | `soh/soh/SohGui/LanguageManager.cpp` | Implementación del manager | | `lenguajes/Espanol.json` | Traducción español (~250 claves) | | `soh/soh/Localization.h` | Stub para compatibilidad de compilación | | `soh/soh/Localization.cpp` | Stub para compatibilidad de compilación | --- ## 3. Archivos Modificados | Archivo | Cambios | |---------|---------| | `soh/soh/SohGui/SohMenuSettings.cpp` | Agregar selector de idioma dinámico + carga automática al inicio | | `soh/soh/SohGui/SohMenu.cpp` | Aplicar traducción automática en AddWidget | --- ## 4. Ubicación de la Carpeta de Idiomas ### 4.1 Ubicaciones Buscadas El sistema busca la carpeta `lenguajes` en este orden: 1. **Directorio actual** (desde donde se ejecuta el juego) 2. **Directorio de datos de la app** (~/.local/share/com.shipofharkinian.soh/) ### 4.2 Cómo colocar los archivos ```bash # Opción 1: Copiar junto al ejecutable (después de compilar) cp -r lenguajes build-cmake/soh/ # Opción 2: En el directorio de datos de la app # Linux: ~/.local/share/com.shipofharkinian.soh/lenguajes/ ``` --- ## 5. Formato de Archivos JSON ### 5.1 Estructura del JSON ```json { "language": "Español", "strings": { "Settings": "Configuración", "Enhancements": "Mejoras", "Randomizer": "Randomizer", "Network": "Red", "Dev Tools": "Herramientas de Desarrollo", "Enabled": "Activado", "Disabled": "Desactivado", "Apply": "Aplicar", "Cancel": "Cancelar" } } ``` ### 5.2 Espanol.json (Completo) Ya incluye ~250 traducciones para los menús principales: - Settings (General, Graphics, Audio, Controls, etc.) - Enhancements - Randomizer - Network - Dev Tools --- ## 6. Implementación del LanguageManager ### 6.1 LanguageManager.h ```cpp class LanguageManager { public: static LanguageManager& Instance(); void Init(); void LoadLanguage(const std::string& languageName); std::string GetString(const std::string& key); std::vector GetAvailableLanguages(); std::string GetCurrentLanguage(); bool IsTranslationLoaded(); private: std::string currentLanguage; std::map translations; bool translationLoaded; void ScanLanguageFiles(); bool LoadJsonFile(const std::string& path); std::string GetLanguagesDirectory(); }; ``` ### 6.2 Lógica de Funcionamiento ```cpp std::string LanguageManager::GetString(const std::string& key) { // Si no hay traducción cargada, retorna el texto hardcodeado (ingles) if (!translationLoaded || translations.empty()) { return key; } // Busca la traducción auto it = translations.find(key); if (it != translations.end()) { return it->second; } // Si no encuentra la traducción, retorna el texto hardcodeado return key; } ``` ### 6.3 Funcionalidades Principales 1. **Escaneo de idiomas**: Al iniciar, escanea la carpeta `/lenguajes/` y detecta archivos `.json` 2. **Carga bajo demanda**: Solo carga el JSON cuando el usuario selecciona un idioma 3. **Fallback seguro**: Si no existe la traducción, retorna el texto hardcodeado 4. **Selector dinámico**: Genera opciones basadas en archivos encontrados (sin hardcodear nombres) 5. **Búsqueda dual**: Busca primero en directorio actual, luego en directorio de datos 6. **Persistencia**: Guarda el idioma seleccionado en CVAR y lo carga al iniciar --- ## 7. Ejemplo de Uso en el Código ### 7.1 Sin cambios en el código existente Los textos hardcodeados permanecen exactamente igual: ```cpp // El código queda igual, NO se cambia a L("key") AddWidget(path, "Settings", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Language", WIDGET_CVAR_COMBOBOX); AddWidget(path, "Enabled", WIDGET_CVAR_CHECKBOX); ``` ### 7.2 La traducción se aplica automáticamente En `SohMenu.cpp`, método `AddWidget`: ```cpp SidebarEntry& entry = sidebar.at(pathInfo.sidebarName); std::string translatedName = LanguageManager::Instance().GetString(widgetName); entry.columnWidgets.at(column).push_back({ .name = translatedName, .type = widgetType }); WidgetInfo& widget = entry.columnWidgets.at(column).back(); ``` ### 7.3 Cómo funciona - Los textos en el código bleiben en inglés - La función `GetString()` se llama al momento de mostrar el texto - Si hay una traducción cargada y existe la clave, retorna la traducción - Si no hay traducción o no existe la clave, retorna el texto original (hardcodeado) --- ## 8. Selector de Idioma ### 8.1 Ubicación En `SohMenuSettings.cpp` dentro del menú de configuración, sección "Languages" ### 8.2 Comportamiento - Muestra todos los archivos `.json` encontrados en `/lenguajes/` - El nombre del archivo (sin extensión) se muestra en el selector - Al seleccionar un idioma, carga el archivo JSON correspondiente - Por defecto (sin acción del usuario), funciona con textos hardcodeados - Incluye opción "None" para desactivar traducciones - **El idioma seleccionado se guarda y se carga automáticamente al iniciar el juego** ### 8.3 Widget implementado ```cpp // Variables estáticas para mantener los strings vivos static std::map uiLanguageOptionsStr = { }; static std::map uiLanguageOptions = { }; // Inicialización con carga automática del idioma guardado static void InitUILanguages() { LanguageManager::Instance().Init(); uiLanguageOptionsStr.clear(); uiLanguageOptions.clear(); uiLanguageOptionsStr[0] = "None"; uiLanguageOptions[0] = "None"; int32_t idx = 1; for (const auto& lang : LanguageManager::Instance().GetAvailableLanguages()) { if (!lang.empty()) { uiLanguageOptionsStr[idx] = lang; uiLanguageOptions[idx] = uiLanguageOptionsStr[idx].c_str(); idx++; } } // Cargar idioma guardado automáticamente int32_t savedLang = CVarGetInteger(CVAR_SETTING("UILanguage"), 0); if (savedLang > 0 && savedLang < (int32_t)LanguageManager::Instance().GetAvailableLanguages().size() + 1) { std::string langName = LanguageManager::Instance().GetAvailableLanguages()[savedLang - 1]; LanguageManager::Instance().LoadLanguage(langName); } } // Widget del selector AddWidget(path, "UI Translation", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_SETTING("UILanguage")) .Callback([](WidgetInfo& info) { // Guarda y carga el idioma seleccionado }) .Options(ComboboxOptions() .ComboMap(uiLanguageOptions) .Tooltip("Select the UI translation language...")); ``` --- ## 9. Proceso de Implementación (Completado) ### Fase 1: Fundamentos ✅ 1. Crear `LanguageManager.h/cpp` 2. Crear estructura de carpetas `/lenguajes/` ### Fase 2: Integración ✅ 3. Modificar `SohMenuSettings.cpp` para agregar selector de idioma dinámico 4. Modificar `SohMenu.cpp` para aplicar traducción automática 5. Crear `Localization.h/cpp` (stubs para compilación) 6. **Corregir problema de strings temporales** (usar map estático para mantener strings vivos) 7. **Agregar carga automática del idioma guardado al iniciar** ### Fase 3: Pruebas ✅ 8. Crear `Espanol.json` de prueba 9. Probar cambio de idioma 10. Corregir rutas de búsqueda 11. Verificar persistencia del idioma seleccionado --- ## 10. Notas Importantes - ✅ **Textos hardcodeados permanecen**: El código no se modifica, los textos en inglés quedan como están - ✅ **Sin carpeta de idiomas funciona igual**: Si no existe la carpeta, el juego funciona exactamente como antes - ✅ **Fallback automático**: Si falta una traducción, se muestra el texto original - ✅ **Selector dinámico**: Los nombres de idiomas vienen de los archivos JSON, no están hardcodeados - ✅ **Fácil agregar idiomas**: Solo hay que crear un nuevo archivo `.json` en la carpeta - ✅ **Persistencia**: El idioma seleccionado se guarda y se carga automáticamente al iniciar - ⚠️ **Ubicación de carpeta**: La carpeta `lenguajes` debe estar junto al ejecutable o en el directorio de datos de la app --- ## 11. Cómo Compilar y Ejecutar ```bash # 1. Compilar el proyecto cmake -H. -Bbuild-cmake -GNinja cmake --build build-cmake -j$(nproc) # 2. Copiar carpeta de idiomas junto al ejecutable cp -r lenguajes build-cmake/soh/ # 3. Ejecutar desde la raíz del proyecto ./build-cmake/soh/soh.elf ``` --- ## 12. Estado de Implementación ✅ COMPLETADO ### Implementación Base ✅ - [x] LanguageManager.h/cpp - Carga de idiomas desde JSON - [x] Selector de idioma dinámico en Settings - [x] Aplicación automática de traducciones en AddWidget - [x] Persistencia del idioma seleccionado (guardado/cargado al inicio) - [x] Traducciones para menús principales (Settings, Enhancements, Randomizer, Network, DevTools) - [x] Fix de strings temporales en dropdowns - [x] Fix de compilación en Menu.cpp (SohGui::LanguageManager) - [x] Compilación exitosa ### Pendientes / Mejoras Futuras - [ ] Agregar más traducciones al JSON (actualmente ~250 claves) - [ ] Crear archivo Portugues.json - [ ] Posibilidad de recargar traducciones en tiempo real sin cerrar el juego --- ## 13. Archivos de Referencia con Textos Hardcodeados ### SohGui (ya analizados) - `SohMenu_hardcoded.txt` - `SohMenuSettings_hardcoded.txt` - `SohMenuEnhancements_hardcoded.txt` - `SohMenuRandomizer_hardcoded.txt` - `SohMenuNetwork_hardcoded.txt` - `SohMenuDevTools_hardcoded.txt` - `SohMenuBar_hardcoded.txt` - `ResolutionEditor_hardcoded.txt` --- ## 14. Diferencias con el Plan Anterior | Aspecto | Plan Anterior | Plan Actual | |---------|---------------|--------------| | English.json | Obligatorio | No necesario (hardcodeado es fallback) | | Reemplazo de textos | Sí, en todos los archivos | No, solo agregar función GetString | | Archivos a modificar | 8+ archivos | 2 archivos (SohMenuSettings.cpp, SohMenu.cpp) | | Funcionamiento sin carpeta | Requiere English.json | Funciona igual que antes | | Stub Localization | No previsto | Necesario para compilar | | Persistencia de idioma | No implementada | ✅ Guardado y cargado automáticamente | | Strings en selector | Implementación con problemas | ✅ Solucionado con map estático |