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

372 lines
13 KiB
Markdown

# 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
```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/
```
### 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`:
```cmake
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
```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<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
```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<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 ✅
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
### Traducción del Randomizer ✅
- [x] Plandomizer.cpp - Textos de UI traducidos (tablas, popups, tooltips, tabs)
- [x] randomizer_check_tracker.cpp - Textos de UI traducidos (checkboxes, botones, búsqueda, settings)
- [x] randomizer_item_tracker.cpp - Textos de UI traducidos (settings table, checks counter)
- [x] randomizer_entrance_tracker.cpp - Textos de UI traducidos (sort, group, legend, tooltips)
- [x] 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 |