forked from metin-server/m2dev-client
@@ -17,6 +17,9 @@ import serverCommandParser
|
||||
import ime
|
||||
import uiScriptLocale
|
||||
|
||||
# Multi-language hot-reload system
|
||||
from uilocaleselector import LocaleSelector
|
||||
|
||||
LOGIN_DELAY_SEC = 0.0
|
||||
SKIP_LOGIN_PHASE = False
|
||||
SKIP_LOGIN_PHASE_SUPPORT_CHANNEL = False
|
||||
@@ -55,6 +58,13 @@ def GetLoginDelay():
|
||||
|
||||
app.SetGuildMarkPath("test")
|
||||
|
||||
###############################################################################
|
||||
# Multi-Language Hot-Reload System
|
||||
# All locale selector and hot-reload logic moved to:
|
||||
# - uilocaleselector.py (UI component)
|
||||
# - uilocalechange.py (hot-reload manager)
|
||||
###############################################################################
|
||||
|
||||
class ConnectingDialog(ui.ScriptWindow):
|
||||
|
||||
def __init__(self):
|
||||
@@ -144,11 +154,8 @@ class LoginWindow(ui.ScriptWindow):
|
||||
self.virtualKeyboardIsUpper = False
|
||||
self.timeOutMsg = False #Fix
|
||||
|
||||
self.language_list = []
|
||||
self.flag_button_list = []
|
||||
self.language_board = None
|
||||
self.language_popup = None
|
||||
self.__LoadLocale()
|
||||
# Multi-language hot-reload system
|
||||
self.localeSelector = None
|
||||
|
||||
def __del__(self):
|
||||
net.ClearPhaseWindow(net.PHASE_WINDOW_LOGIN, self)
|
||||
@@ -276,10 +283,9 @@ class LoginWindow(ui.ScriptWindow):
|
||||
self.connectingDialog = None
|
||||
self.loadingImage = None
|
||||
|
||||
self.language_list = []
|
||||
self.flag_button_list = []
|
||||
self.language_board = None
|
||||
self.language_popup = None
|
||||
if self.localeSelector:
|
||||
self.localeSelector.Destroy()
|
||||
self.localeSelector = None
|
||||
|
||||
self.serverBoard = None
|
||||
self.serverList = None
|
||||
@@ -465,29 +471,11 @@ class LoginWindow(ui.ScriptWindow):
|
||||
self.GetChild("key_at").SetToggleDownEvent(lambda : self.__VirtualKeyboard_SetSymbolMode())
|
||||
self.GetChild("key_at").SetToggleUpEvent(lambda : self.__VirtualKeyboard_SetAlphabetMode())
|
||||
|
||||
self.language_board = ui.ThinBoard()
|
||||
self.language_board.SetParent(self)
|
||||
self.language_board.SetSize(wndMgr.GetScreenWidth(), 35)
|
||||
self.language_board.SetPosition(0, 20)
|
||||
self.language_board.Show()
|
||||
|
||||
step = wndMgr.GetScreenWidth() / len(self.language_list)
|
||||
x = 0
|
||||
|
||||
for i, lang in enumerate(self.language_list):
|
||||
img_path = "d:/ymir work/ui/intro/login/server_flag_%s.sub" % lang
|
||||
btn = ui.Button()
|
||||
btn.SetParent(self.language_board)
|
||||
btn.SetPosition(x + 15, 10)
|
||||
btn.SetUpVisual(img_path)
|
||||
btn.SetOverVisual(img_path)
|
||||
btn.SetDownVisual(img_path)
|
||||
btn.SetToolTipText(lang.upper())
|
||||
btn.SetEvent(ui.__mem_func__(self.__ClickLanguage), i)
|
||||
btn.Show()
|
||||
|
||||
self.flag_button_list.append(btn)
|
||||
x += step
|
||||
# Create locale selector (only if it doesn't exist - during hot-reload we keep the old one)
|
||||
if not self.localeSelector:
|
||||
self.localeSelector = LocaleSelector()
|
||||
self.localeSelector.Create(self)
|
||||
self.localeSelector.SetLocaleChangedEvent(ui.__mem_func__(self.__OnLocaleChanged))
|
||||
|
||||
except:
|
||||
import exception
|
||||
@@ -608,49 +596,95 @@ class LoginWindow(ui.ScriptWindow):
|
||||
def __OnClickExitButton(self):
|
||||
self.stream.SetPhaseWindow(0)
|
||||
|
||||
def __LoadLocale(self):
|
||||
self.language_list = [
|
||||
"ae", "en", "cz", "de", "dk",
|
||||
"es", "fr", "gr", "hu", "it",
|
||||
"nl", "pl", "pt", "ro", "ru", "tr",
|
||||
]
|
||||
def __OnLocaleChanged(self, newLocaleCode):
|
||||
"""Handle locale change - save config, reload, and refresh UI"""
|
||||
import dbg
|
||||
|
||||
def __SaveLocale(self, locale):
|
||||
# 1) Save locale code to config/locale.cfg
|
||||
try:
|
||||
with open("config/locale.cfg", "wt") as f:
|
||||
f.write(locale)
|
||||
except:
|
||||
import dbg
|
||||
dbg.LogBox("__SaveLocale error locale.cfg")
|
||||
app.Abort()
|
||||
|
||||
def __ClickLanguage(self, index):
|
||||
if index >= len(self.language_list):
|
||||
import os
|
||||
if not os.path.exists("config"):
|
||||
os.makedirs("config")
|
||||
with open("config/locale.cfg", "w") as f:
|
||||
f.write(newLocaleCode)
|
||||
dbg.TraceError("Saved new locale to config: %s" % newLocaleCode)
|
||||
except Exception as e:
|
||||
dbg.TraceError("Failed to save locale.cfg: %s" % str(e))
|
||||
return
|
||||
|
||||
self.locale = self.language_list[index]
|
||||
# 2) Call C++ to reload locale data (C++ data + Python modules)
|
||||
if not app.ReloadLocale():
|
||||
dbg.TraceError("app.ReloadLocale() failed")
|
||||
return
|
||||
|
||||
if not self.language_popup:
|
||||
self.language_popup = uiCommon.QuestionDialog()
|
||||
dbg.TraceError("Locale changed successfully, refreshing UI...")
|
||||
|
||||
self.language_popup.SetText("Change language and restart the client?")
|
||||
self.language_popup.SetAcceptEvent(ui.__mem_func__(self.__OnAcceptLanguage))
|
||||
self.language_popup.SetCancelEvent(ui.__mem_func__(self.__OnCancelLanguage))
|
||||
self.language_popup.Open()
|
||||
# 3) Refresh all UI text elements with new locale
|
||||
self.__RefreshLocaleUI()
|
||||
|
||||
def __OnAcceptLanguage(self):
|
||||
if self.language_popup:
|
||||
self.language_popup.Close()
|
||||
def __RefreshLocaleUI(self):
|
||||
"""
|
||||
Refresh all UI text elements after locale change
|
||||
|
||||
self.__SaveLocale(self.locale)
|
||||
Uses the generic uiLocaleRefresh module to update all visible text elements
|
||||
without needing to reload the entire UI.
|
||||
"""
|
||||
import uiScriptLocale
|
||||
import uiLocaleRefresh
|
||||
|
||||
import os
|
||||
app.Exit()
|
||||
os.popen('start "" "Metin2_Debug.exe"')
|
||||
try:
|
||||
# Refresh button and text elements using element mapping
|
||||
elementMapping = {
|
||||
self.serverInfo: "LOGIN_DEFAULT_SERVERADDR",
|
||||
self.selectConnectButton: "LOGIN_SELECT_BUTTON",
|
||||
self.loginButton: "LOGIN_CONNECT",
|
||||
self.loginExitButton: "LOGIN_EXIT",
|
||||
self.serverSelectButton: "OK",
|
||||
self.serverExitButton: "LOGIN_SELECT_EXIT",
|
||||
}
|
||||
uiLocaleRefresh.RefreshByMapping(elementMapping)
|
||||
|
||||
def __OnCancelLanguage(self):
|
||||
if self.language_popup:
|
||||
self.language_popup.Close()
|
||||
# Refresh ServerBoard Title (special case - accessed via GetChild)
|
||||
try:
|
||||
serverBoardTitle = self.GetChild("Title")
|
||||
serverBoardTitle.SetText(uiScriptLocale.LOGIN_SELECT_TITLE)
|
||||
except:
|
||||
pass
|
||||
|
||||
# Rebuild login error message dictionary with new locale strings
|
||||
loginFailureTemplate = {
|
||||
"ALREADY" : "LOGIN_FAILURE_ALREAY",
|
||||
"NOID" : "LOGIN_FAILURE_NOT_EXIST_ID",
|
||||
"WRONGPWD" : "LOGIN_FAILURE_WRONG_PASSWORD",
|
||||
"FULL" : "LOGIN_FAILURE_TOO_MANY_USER",
|
||||
"SHUTDOWN" : "LOGIN_FAILURE_SHUTDOWN",
|
||||
"REPAIR" : "LOGIN_FAILURE_REPAIR_ID",
|
||||
"BLOCK" : "LOGIN_FAILURE_BLOCK_ID",
|
||||
"BESAMEKEY" : "LOGIN_FAILURE_BE_SAME_KEY",
|
||||
"NOTAVAIL" : "LOGIN_FAILURE_NOT_AVAIL",
|
||||
"NOBILL" : "LOGIN_FAILURE_NOBILL",
|
||||
"BLKLOGIN" : "LOGIN_FAILURE_BLOCK_LOGIN",
|
||||
"WEBBLK" : "LOGIN_FAILURE_WEB_BLOCK",
|
||||
"BADSCLID" : "LOGIN_FAILURE_WRONG_SOCIALID",
|
||||
"AGELIMIT" : "LOGIN_FAILURE_SHUTDOWN_TIME",
|
||||
}
|
||||
self.loginFailureMsgDict = uiLocaleRefresh.RebuildDictionary(loginFailureTemplate, "localeInfo")
|
||||
|
||||
# Recreate locale selector to ensure it's on top with updated text
|
||||
if self.localeSelector:
|
||||
self.localeSelector.Destroy()
|
||||
self.localeSelector = None
|
||||
|
||||
self.localeSelector = LocaleSelector()
|
||||
self.localeSelector.Create(self)
|
||||
self.localeSelector.SetLocaleChangedEvent(ui.__mem_func__(self.__OnLocaleChanged))
|
||||
self.localeSelector.Show()
|
||||
self.localeSelector.SetTop()
|
||||
|
||||
except:
|
||||
# import dbg
|
||||
# dbg.TraceError("LoginWindow.__RefreshLocaleUI failed")
|
||||
pass
|
||||
|
||||
def __SetServerInfo(self, name):
|
||||
net.SetServerInfo(name.strip())
|
||||
|
||||
@@ -13,6 +13,8 @@ BLEND_POTION_NO_INFO = 'BLEND_POTION_NO_INFO'
|
||||
LOGIN_FAILURE_WRONG_SOCIALID = 'LOGIN_FAILURE_WRONG_SOCIALID'
|
||||
LOGIN_FAILURE_SHUTDOWN_TIME = 'LOGIN_FAILURE_SHUTDOWN_TIME'
|
||||
|
||||
LOCALE_CHANGE_CONFIRM = 'Change language to %s and reload the game?'
|
||||
|
||||
GUILD_MEMBER_COUNT_INFINITY = 'INFINITY'
|
||||
GUILD_MARK_MIN_LEVEL = '3'
|
||||
GUILD_BUILDING_LIST_TXT = '{:s}/GuildBuildingList.txt'.format(APP_GET_LOCALE_PATH)
|
||||
@@ -29,9 +31,29 @@ VIRTUAL_KEY_SYMBOLS = "!@#$%^&*()_+|{}:'<>?~"
|
||||
VIRTUAL_KEY_NUMBERS = "1234567890-=\[];',./`"
|
||||
VIRTUAL_KEY_SYMBOLS_BR = "!@#$%^&*()_+|{}:'<>?~aaaaeeeiioooouuc"
|
||||
|
||||
# Load locale data by specific path
|
||||
# Multi-language hot-reload support
|
||||
def LoadLocaleData():
|
||||
app.LoadLocaleData(app.GetLocalePath())
|
||||
"""
|
||||
Reload all game locale text strings from locale_game.txt
|
||||
|
||||
Called by app.ReloadLocale() when the user changes language.
|
||||
Reloads locale_game.txt and updates all module-level locale strings.
|
||||
|
||||
Returns:
|
||||
True on success, False on failure
|
||||
"""
|
||||
try:
|
||||
localePath = app.GetLocalePath()
|
||||
localeFilePath = "{:s}/locale_game.txt".format(localePath)
|
||||
|
||||
# Reload locale_game.txt - this updates all global variables in this module
|
||||
LoadLocaleFile(localeFilePath, globals())
|
||||
|
||||
return True
|
||||
except:
|
||||
# import dbg
|
||||
# dbg.TraceError("localeInfo.LoadLocaleData failed")
|
||||
return False
|
||||
|
||||
# Load locale_game.txt
|
||||
def LoadLocaleFile(srcFileName, localeDict):
|
||||
|
||||
242
assets/root/uilocalerefresh.py
Normal file
242
assets/root/uilocalerefresh.py
Normal file
@@ -0,0 +1,242 @@
|
||||
"""
|
||||
Generic UI Locale Refresh System
|
||||
This module provides automatic locale refresh for UI windows without hardcoding element names.
|
||||
"""
|
||||
|
||||
import dbg
|
||||
import ui
|
||||
|
||||
class LocaleRefreshHelper:
|
||||
"""
|
||||
Helper class to automatically refresh UI text elements when locale changes.
|
||||
Works by re-reading the original UI script and applying new locale strings.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.scriptCache = {} # Cache loaded UI scripts
|
||||
|
||||
def RefreshWindow(self, window, scriptPath):
|
||||
"""
|
||||
Automatically refresh all text elements in a window by re-reading the UI script.
|
||||
|
||||
Args:
|
||||
window: The ui.ScriptWindow instance to refresh
|
||||
scriptPath: Path to the UI script file (e.g., "UIScript/LoginWindow.py")
|
||||
|
||||
Returns:
|
||||
Number of elements successfully refreshed
|
||||
"""
|
||||
import uiScriptLocale
|
||||
import localeInfo
|
||||
|
||||
dbg.TraceError("LocaleRefreshHelper: Refreshing window from %s" % scriptPath)
|
||||
|
||||
# Load the UI script to get the original text definitions
|
||||
try:
|
||||
scriptData = self._LoadUIScript(scriptPath)
|
||||
except Exception as e:
|
||||
dbg.TraceError("LocaleRefreshHelper: Failed to load script %s: %s" % (scriptPath, str(e)))
|
||||
return 0
|
||||
|
||||
# Recursively refresh all elements
|
||||
refreshCount = self._RefreshElement(window, scriptData.get("window", {}), window)
|
||||
|
||||
dbg.TraceError("LocaleRefreshHelper: Refreshed %d elements" % refreshCount)
|
||||
return refreshCount
|
||||
|
||||
def RefreshElementsByMapping(self, elementMap):
|
||||
"""
|
||||
Refresh UI elements using a manual mapping dictionary.
|
||||
Useful for elements that can't be auto-detected.
|
||||
|
||||
Args:
|
||||
elementMap: Dict of {element_instance: locale_string_name}
|
||||
|
||||
Example:
|
||||
mapping = {
|
||||
self.loginButton: "LOGIN_CONNECT",
|
||||
self.exitButton: "LOGIN_EXIT"
|
||||
}
|
||||
helper.RefreshElementsByMapping(mapping)
|
||||
"""
|
||||
import uiScriptLocale
|
||||
import localeInfo
|
||||
|
||||
refreshCount = 0
|
||||
for element, localeKey in elementMap.items():
|
||||
try:
|
||||
# Try uiScriptLocale first, then localeInfo
|
||||
if hasattr(uiScriptLocale, localeKey):
|
||||
text = getattr(uiScriptLocale, localeKey)
|
||||
elif hasattr(localeInfo, localeKey):
|
||||
text = getattr(localeInfo, localeKey)
|
||||
else:
|
||||
dbg.TraceError("LocaleRefreshHelper: Locale key not found: %s" % localeKey)
|
||||
continue
|
||||
|
||||
# Set the text
|
||||
if hasattr(element, 'SetText'):
|
||||
element.SetText(text)
|
||||
refreshCount += 1
|
||||
except Exception as e:
|
||||
dbg.TraceError("LocaleRefreshHelper: Failed to refresh element with key %s: %s" % (localeKey, str(e)))
|
||||
|
||||
return refreshCount
|
||||
|
||||
def RefreshDictionaries(self, targetDict, localeModule="localeInfo"):
|
||||
"""
|
||||
Rebuild a dictionary with fresh locale strings.
|
||||
Useful for error message dictionaries, etc.
|
||||
|
||||
Args:
|
||||
targetDict: Dictionary to rebuild with format {key: "LOCALE_CONSTANT_NAME"}
|
||||
localeModule: Name of the locale module ("localeInfo" or "uiScriptLocale")
|
||||
|
||||
Returns:
|
||||
New dictionary with fresh locale values
|
||||
|
||||
Example:
|
||||
template = {
|
||||
"WRONGPWD": "LOGIN_FAILURE_WRONG_PASSWORD",
|
||||
"FULL": "LOGIN_FAILURE_TOO_MANY_USER"
|
||||
}
|
||||
newDict = helper.RefreshDictionaries(template)
|
||||
"""
|
||||
import localeInfo
|
||||
import uiScriptLocale
|
||||
|
||||
module = localeInfo if localeModule == "localeInfo" else uiScriptLocale
|
||||
newDict = {}
|
||||
|
||||
for key, localeKey in targetDict.items():
|
||||
if hasattr(module, localeKey):
|
||||
newDict[key] = getattr(module, localeKey)
|
||||
else:
|
||||
dbg.TraceError("LocaleRefreshHelper: Locale key not found: %s" % localeKey)
|
||||
|
||||
return newDict
|
||||
|
||||
def _LoadUIScript(self, scriptPath):
|
||||
"""Load and cache a UI script file."""
|
||||
if scriptPath in self.scriptCache:
|
||||
return self.scriptCache[scriptPath]
|
||||
|
||||
# Execute the UI script to get its data
|
||||
scriptData = {}
|
||||
try:
|
||||
execfile(scriptPath, scriptData)
|
||||
self.scriptCache[scriptPath] = scriptData
|
||||
except Exception as e:
|
||||
dbg.TraceError("LocaleRefreshHelper: Failed to execute script %s: %s" % (scriptPath, str(e)))
|
||||
raise
|
||||
|
||||
return scriptData
|
||||
|
||||
def _RefreshElement(self, windowInstance, elementDef, currentElement):
|
||||
"""
|
||||
Recursively refresh an element and its children.
|
||||
|
||||
Args:
|
||||
windowInstance: The root window instance
|
||||
elementDef: Element definition from UI script
|
||||
currentElement: Current UI element instance
|
||||
|
||||
Returns:
|
||||
Number of elements refreshed
|
||||
"""
|
||||
import uiScriptLocale
|
||||
import localeInfo
|
||||
|
||||
refreshCount = 0
|
||||
|
||||
# If this element has text defined in the script, refresh it
|
||||
if isinstance(elementDef, dict) and "text" in elementDef:
|
||||
textDef = elementDef["text"]
|
||||
|
||||
# Check if it's a locale reference (starts with uiScriptLocale or localeInfo)
|
||||
if isinstance(textDef, str):
|
||||
text = self._ResolveLocaleString(textDef)
|
||||
if text and hasattr(currentElement, 'SetText'):
|
||||
try:
|
||||
currentElement.SetText(text)
|
||||
refreshCount += 1
|
||||
except:
|
||||
pass
|
||||
|
||||
# Recursively process children
|
||||
if isinstance(elementDef, dict) and "children" in elementDef:
|
||||
children = elementDef.get("children", [])
|
||||
for childDef in children:
|
||||
if isinstance(childDef, dict) and "name" in childDef:
|
||||
childName = childDef["name"]
|
||||
try:
|
||||
childElement = windowInstance.GetChild(childName)
|
||||
refreshCount += self._RefreshElement(windowInstance, childDef, childElement)
|
||||
except:
|
||||
pass
|
||||
|
||||
return refreshCount
|
||||
|
||||
def _ResolveLocaleString(self, textDef):
|
||||
"""
|
||||
Resolve a locale string reference to its current value.
|
||||
|
||||
Args:
|
||||
textDef: String like "uiScriptLocale.LOGIN_CONNECT" or direct text
|
||||
|
||||
Returns:
|
||||
The resolved locale string or None
|
||||
"""
|
||||
import uiScriptLocale
|
||||
import localeInfo
|
||||
|
||||
# Check if it's a locale reference
|
||||
if "uiScriptLocale." in str(textDef):
|
||||
# Extract the attribute name
|
||||
parts = str(textDef).split(".")
|
||||
if len(parts) >= 2:
|
||||
attrName = parts[-1]
|
||||
if hasattr(uiScriptLocale, attrName):
|
||||
return getattr(uiScriptLocale, attrName)
|
||||
|
||||
elif "localeInfo." in str(textDef):
|
||||
parts = str(textDef).split(".")
|
||||
if len(parts) >= 2:
|
||||
attrName = parts[-1]
|
||||
if hasattr(localeInfo, attrName):
|
||||
return getattr(localeInfo, attrName)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
# Global helper instance for easy access
|
||||
_globalHelper = LocaleRefreshHelper()
|
||||
|
||||
def RefreshWindowByScript(window, scriptPath):
|
||||
"""
|
||||
Convenience function to refresh a window using its UI script.
|
||||
|
||||
Args:
|
||||
window: The ui.ScriptWindow instance
|
||||
scriptPath: Path to UI script (e.g., "UIScript/LoginWindow.py")
|
||||
"""
|
||||
return _globalHelper.RefreshWindow(window, scriptPath)
|
||||
|
||||
def RefreshByMapping(elementMap):
|
||||
"""
|
||||
Convenience function to refresh elements by mapping.
|
||||
|
||||
Args:
|
||||
elementMap: Dict of {element: "LOCALE_KEY"}
|
||||
"""
|
||||
return _globalHelper.RefreshElementsByMapping(elementMap)
|
||||
|
||||
def RebuildDictionary(template, localeModule="localeInfo"):
|
||||
"""
|
||||
Convenience function to rebuild a dictionary with fresh locale strings.
|
||||
|
||||
Args:
|
||||
template: Dict of {key: "LOCALE_KEY"}
|
||||
localeModule: "localeInfo" or "uiScriptLocale"
|
||||
"""
|
||||
return _globalHelper.RefreshDictionaries(template, localeModule)
|
||||
191
assets/root/uilocaleselector.py
Normal file
191
assets/root/uilocaleselector.py
Normal file
@@ -0,0 +1,191 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Locale Selector UI Component
|
||||
=============================
|
||||
|
||||
A reusable UI component for selecting and changing the client language.
|
||||
Can be added to any window (login, game settings, etc.).
|
||||
|
||||
Usage:
|
||||
from uilocaleselector import LocaleSelector
|
||||
|
||||
# In your window class:
|
||||
self.localeSelector = LocaleSelector()
|
||||
self.localeSelector.Create(self)
|
||||
self.localeSelector.SetLocaleChangedEvent(ui.__mem_func__(self.__OnLocaleChanged))
|
||||
|
||||
# Implement the callback:
|
||||
def __OnLocaleChanged(self, newLocaleCode):
|
||||
# Handle UI recreation here
|
||||
pass
|
||||
"""
|
||||
|
||||
import ui
|
||||
import uiCommon
|
||||
import localeInfo
|
||||
import wndMgr
|
||||
|
||||
|
||||
# Available locales configuration
|
||||
AVAILABLE_LOCALES = [
|
||||
{"code": "ae", "name": "Arabic", "flag": "ae"},
|
||||
{"code": "en", "name": "English", "flag": "en"},
|
||||
{"code": "cz", "name": "Čeština", "flag": "cz"},
|
||||
{"code": "de", "name": "Deutsch", "flag": "de"},
|
||||
{"code": "dk", "name": "Dansk", "flag": "dk"},
|
||||
{"code": "es", "name": "Español", "flag": "es"},
|
||||
{"code": "fr", "name": "Français", "flag": "fr"},
|
||||
{"code": "gr", "name": "Ελληνικά", "flag": "gr"},
|
||||
{"code": "hu", "name": "Magyar", "flag": "hu"},
|
||||
{"code": "it", "name": "Italiano", "flag": "it"},
|
||||
{"code": "nl", "name": "Nederlands", "flag": "nl"},
|
||||
{"code": "pl", "name": "Polski", "flag": "pl"},
|
||||
{"code": "pt", "name": "Português", "flag": "pt"},
|
||||
{"code": "ro", "name": "Română", "flag": "ro"},
|
||||
{"code": "ru", "name": "Русский", "flag": "ru"},
|
||||
{"code": "tr", "name": "Türkçe", "flag": "tr"},
|
||||
]
|
||||
|
||||
# Flag image path template
|
||||
FLAG_IMAGE_PATH = "d:/ymir work/ui/intro/login/server_flag_%s.sub"
|
||||
|
||||
|
||||
class LocaleSelector(ui.Window):
|
||||
"""
|
||||
UI component for selecting and changing client language.
|
||||
|
||||
Features:
|
||||
- Displays flag buttons for all available locales
|
||||
- Shows confirmation dialog before changing
|
||||
- Triggers callback when locale is confirmed
|
||||
- Self-contained and reusable
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
ui.Window.__init__(self)
|
||||
self.background = None
|
||||
self.flagButtons = []
|
||||
self.confirmDialog = None
|
||||
self.selectedLocaleCode = None
|
||||
self.eventLocaleChanged = None
|
||||
|
||||
def __del__(self):
|
||||
ui.Window.__del__(self)
|
||||
|
||||
def Destroy(self):
|
||||
"""Clean up resources when destroying the selector."""
|
||||
self.eventLocaleChanged = None
|
||||
self.selectedLocaleCode = None
|
||||
|
||||
if self.confirmDialog:
|
||||
self.confirmDialog.Close()
|
||||
self.confirmDialog = None
|
||||
|
||||
for btn in self.flagButtons:
|
||||
btn.Hide()
|
||||
btn = None
|
||||
self.flagButtons = []
|
||||
|
||||
if self.background:
|
||||
self.background.Hide()
|
||||
self.background = None
|
||||
|
||||
def Create(self, parent):
|
||||
"""
|
||||
Create and display the locale selector UI.
|
||||
|
||||
Args:
|
||||
parent: The parent window to attach to
|
||||
"""
|
||||
self.SetParent(parent)
|
||||
self.SetSize(wndMgr.GetScreenWidth(), 35)
|
||||
self.SetPosition(0, 20)
|
||||
|
||||
# Create background board
|
||||
self.background = ui.ThinBoard()
|
||||
self.background.SetParent(self)
|
||||
self.background.SetSize(wndMgr.GetScreenWidth(), 35)
|
||||
self.background.SetPosition(0, 0)
|
||||
self.background.Show()
|
||||
|
||||
# Create flag buttons
|
||||
self._CreateFlagButtons()
|
||||
|
||||
self.Show()
|
||||
|
||||
def _CreateFlagButtons(self):
|
||||
"""Create flag buttons for all available locales."""
|
||||
localeCount = len(AVAILABLE_LOCALES)
|
||||
if localeCount == 0:
|
||||
return
|
||||
|
||||
buttonSpacing = wndMgr.GetScreenWidth() / localeCount
|
||||
xPosition = 0
|
||||
|
||||
for locale in AVAILABLE_LOCALES:
|
||||
flagPath = FLAG_IMAGE_PATH % locale["flag"]
|
||||
|
||||
button = ui.Button()
|
||||
button.SetParent(self.background)
|
||||
button.SetPosition(xPosition + 15, 10)
|
||||
button.SetUpVisual(flagPath)
|
||||
button.SetOverVisual(flagPath)
|
||||
button.SetDownVisual(flagPath)
|
||||
button.SetToolTipText(locale["name"])
|
||||
button.SetEvent(ui.__mem_func__(self._OnClickFlag), locale["code"])
|
||||
button.Show()
|
||||
|
||||
self.flagButtons.append(button)
|
||||
xPosition += buttonSpacing
|
||||
|
||||
def _OnClickFlag(self, localeCode):
|
||||
"""
|
||||
Handle flag button click - show confirmation dialog.
|
||||
|
||||
Args:
|
||||
localeCode: The locale code that was clicked
|
||||
"""
|
||||
self.selectedLocaleCode = localeCode
|
||||
|
||||
# Get locale name for display
|
||||
localeName = "Unknown"
|
||||
for locale in AVAILABLE_LOCALES:
|
||||
if locale["code"] == localeCode:
|
||||
localeName = locale["name"]
|
||||
break
|
||||
|
||||
# Show confirmation dialog
|
||||
if not self.confirmDialog:
|
||||
self.confirmDialog = uiCommon.QuestionDialog()
|
||||
|
||||
self.confirmDialog.SetText(localeInfo.LOCALE_CHANGE_CONFIRM % localeName)
|
||||
self.confirmDialog.SetAcceptEvent(ui.__mem_func__(self._OnConfirmLocaleChange))
|
||||
self.confirmDialog.SetCancelEvent(ui.__mem_func__(self._OnCancelLocaleChange))
|
||||
self.confirmDialog.Open()
|
||||
|
||||
def _OnConfirmLocaleChange(self):
|
||||
"""User confirmed locale change - trigger the callback."""
|
||||
if self.confirmDialog:
|
||||
self.confirmDialog.Close()
|
||||
|
||||
if not self.selectedLocaleCode:
|
||||
return
|
||||
|
||||
# Notify parent window to handle the locale change
|
||||
if self.eventLocaleChanged:
|
||||
self.eventLocaleChanged(self.selectedLocaleCode)
|
||||
|
||||
def _OnCancelLocaleChange(self):
|
||||
"""User cancelled locale change."""
|
||||
if self.confirmDialog:
|
||||
self.confirmDialog.Close()
|
||||
self.selectedLocaleCode = None
|
||||
|
||||
def SetLocaleChangedEvent(self, event):
|
||||
"""
|
||||
Set callback function to be called when locale is confirmed.
|
||||
|
||||
Args:
|
||||
event: Callback function(localeCode) to handle locale change
|
||||
"""
|
||||
self.eventLocaleChanged = event
|
||||
@@ -47,4 +47,49 @@ EMPIREDESC_B = "%s/empiredesc_b.txt" % (name)
|
||||
EMPIREDESC_C = "%s/empiredesc_c.txt" % (name)
|
||||
|
||||
LOCALE_INTERFACE_FILE_NAME = "%s/locale_interface.txt" % (name)
|
||||
LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, locals())
|
||||
LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, locals())
|
||||
|
||||
def LoadLocaleData():
|
||||
"""
|
||||
Reload all UI locale strings from locale_interface.txt
|
||||
|
||||
Called by app.ReloadLocale() when the user changes language.
|
||||
Updates all locale-dependent paths and reloads locale_interface.txt.
|
||||
|
||||
Returns:
|
||||
True on success, False on failure
|
||||
"""
|
||||
try:
|
||||
# Update all locale-dependent paths
|
||||
global name, LOCALE_UISCRIPT_PATH, LOGIN_PATH, EMPIRE_PATH, GUILD_PATH, SELECT_PATH, WINDOWS_PATH, MAPNAME_PATH
|
||||
global JOBDESC_WARRIOR_PATH, JOBDESC_ASSASSIN_PATH, JOBDESC_SURA_PATH, JOBDESC_SHAMAN_PATH
|
||||
global EMPIREDESC_A, EMPIREDESC_B, EMPIREDESC_C, LOCALE_INTERFACE_FILE_NAME
|
||||
|
||||
name = app.GetLocalePath()
|
||||
LOCALE_UISCRIPT_PATH = "%s/ui/" % (name)
|
||||
LOGIN_PATH = "%s/ui/login/" % (name)
|
||||
EMPIRE_PATH = "%s/ui/empire/" % (name)
|
||||
GUILD_PATH = "%s/ui/guild/" % (name)
|
||||
SELECT_PATH = "%s/ui/select/" % (name)
|
||||
WINDOWS_PATH = "%s/ui/windows/" % (name)
|
||||
MAPNAME_PATH = "%s/ui/mapname/" % (name)
|
||||
|
||||
JOBDESC_WARRIOR_PATH = "%s/jobdesc_warrior.txt" % (name)
|
||||
JOBDESC_ASSASSIN_PATH = "%s/jobdesc_assassin.txt" % (name)
|
||||
JOBDESC_SURA_PATH = "%s/jobdesc_sura.txt" % (name)
|
||||
JOBDESC_SHAMAN_PATH = "%s/jobdesc_shaman.txt" % (name)
|
||||
|
||||
EMPIREDESC_A = "%s/empiredesc_a.txt" % (name)
|
||||
EMPIREDESC_B = "%s/empiredesc_b.txt" % (name)
|
||||
EMPIREDESC_C = "%s/empiredesc_c.txt" % (name)
|
||||
|
||||
LOCALE_INTERFACE_FILE_NAME = "%s/locale_interface.txt" % (name)
|
||||
|
||||
# Reload locale_interface.txt - this updates all UI strings
|
||||
LoadLocaleFile(LOCALE_INTERFACE_FILE_NAME, globals())
|
||||
|
||||
return True
|
||||
except:
|
||||
# import dbg
|
||||
# dbg.TraceError("uiScriptLocale.LoadLocaleData failed")
|
||||
return False
|
||||
Reference in New Issue
Block a user