Files
Shiip-of-Hakinian-Espanol/PLAN_TRADUCCION.md
nickpons666 ca5c80fc47 Add translation support for Randomizer UI components
- Add LanguageManager integration to Plandomizer.cpp, randomizer_check_tracker.cpp,
  randomizer_item_tracker.cpp, and randomizer_entrance_tracker.cpp
- Add ~100 new translation keys to Espanol.json for Randomizer UI
- Include lenguajes folder in AppImage/DEB packaging via CMakeLists.txt
- Update PLAN_TRADUCCION.md with Randomizer translation status and packaging info
2026-04-03 17:51:28 -06:00

13 KiB

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
soh/soh/Enhancements/randomizer/Plandomizer.cpp Traducir textos de UI con LanguageManager
soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp Traducir textos de UI con LanguageManager
soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp Traducir textos de UI con LanguageManager
soh/soh/Enhancements/randomizer/randomizer_entrance_tracker.cpp Traducir textos de UI con LanguageManager
CMakeLists.txt Incluir carpeta lenguajes en AppImage/DEB

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

# 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/

4.3 Empaquetado en AppImage/DEB

La carpeta lenguajes se incluye automáticamente en los paquetes AppImage y DEB gracias a la directiva de instalación en CMakeLists.txt:

install(DIRECTORY "${CMAKE_SOURCE_DIR}/lenguajes/" DESTINATION ./lenguajes COMPONENT ship OPTIONAL)

Esto significa que al generar un AppImage con cpack, la carpeta lenguajes con todos los archivos .json se incluirá automáticamente dentro del paquete, sin necesidad de copiarla manualmente. El flag OPTIONAL evita errores de compilación si la carpeta no existe.


5. Formato de Archivos JSON

5.1 Estructura del 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

class LanguageManager {
public:
    static LanguageManager& Instance();
    
    void Init();
    void LoadLanguage(const std::string& languageName);
    std::string GetString(const std::string& key);
    std::vector<std::string> GetAvailableLanguages();
    std::string GetCurrentLanguage();
    bool IsTranslationLoaded();
    
private:
    std::string currentLanguage;
    std::map<std::string, std::string> translations;
    bool translationLoaded;
    
    void ScanLanguageFiles();
    bool LoadJsonFile(const std::string& path);
    std::string GetLanguagesDirectory();
};

6.2 Lógica de Funcionamiento

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:

// 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:

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

// Variables estáticas para mantener los strings vivos
static std::map<int32_t, std::string> uiLanguageOptionsStr = { };
static std::map<int32_t, const char*> 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

  1. Modificar SohMenuSettings.cpp para agregar selector de idioma dinámico
  2. Modificar SohMenu.cpp para aplicar traducción automática
  3. Crear Localization.h/cpp (stubs para compilación)
  4. Corregir problema de strings temporales (usar map estático para mantener strings vivos)
  5. Agregar carga automática del idioma guardado al iniciar

Fase 3: Pruebas

  1. Crear Espanol.json de prueba
  2. Probar cambio de idioma
  3. Corregir rutas de búsqueda
  4. 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

# 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

  • LanguageManager.h/cpp - Carga de idiomas desde JSON
  • Selector de idioma dinámico en Settings
  • Aplicación automática de traducciones en AddWidget
  • Persistencia del idioma seleccionado (guardado/cargado al inicio)
  • Traducciones para menús principales (Settings, Enhancements, Randomizer, Network, DevTools)
  • Fix de strings temporales en dropdowns
  • Fix de compilación en Menu.cpp (SohGui::LanguageManager)
  • Compilación exitosa

Traducción del Randomizer

  • Plandomizer.cpp - Textos de UI traducidos (tablas, popups, tooltips, tabs)
  • randomizer_check_tracker.cpp - Textos de UI traducidos (checkboxes, botones, búsqueda, settings)
  • randomizer_item_tracker.cpp - Textos de UI traducidos (settings table, checks counter)
  • randomizer_entrance_tracker.cpp - Textos de UI traducidos (sort, group, legend, tooltips)
  • Claves de traducción añadidas a Espanol.json (~100 nuevas claves)

Pendientes / Mejoras Futuras

  • Agregar más traducciones al JSON (actualmente ~350+ claves)
  • Crear archivo Portugues.json
  • Posibilidad de recargar traducciones en tiempo real sin cerrar el juego

13. Mejoras al Plandomizer (Randomizer)

13.1 Filtro de Items en Locations

Agregadas nuevas funcionalidades en la pestaña Locations:

Feature Descripción
Campo de búsqueda Escribe el nombre de un item y presiona Enter para seleccionarlo
Botón A-Z Ordena la lista de items alfabéticamente
Limpiar El botón "Clear Item Filter" ahora también limpia el campo de búsqueda

13.2 Archivos Modificados

  • soh/soh/Enhancements/randomizer/Plandomizer.cpp

15. 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

15. 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