feat: Full UI internationalization, pool hashrate stats, and layout caching
- Replace all hardcoded English strings with TR() translation keys across every tab, dialog, and component (~20 UI files) - Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with complete translations (~37k lines added) - Improve i18n loader with exe-relative path fallback and English base fallback for missing keys - Add pool-side hashrate polling via pool stats API in xmrig_manager - Introduce Layout::beginFrame() per-frame caching and refresh balance layout config only on schema generation change - Offload daemon output parsing to worker thread - Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
This commit is contained in:
@@ -99,12 +99,12 @@ void BlockInfoDialog::render(App* app)
|
||||
auto hashBackLbl = S.label("dialogs.block-info", "hash-back-label");
|
||||
auto closeBtn = S.button("dialogs.block-info", "close-button");
|
||||
|
||||
if (material::BeginOverlayDialog("Block Information", &s_open, win.width, 0.94f)) {
|
||||
if (material::BeginOverlayDialog(TR("block_info_title"), &s_open, win.width, 0.94f)) {
|
||||
auto* rpc = app->rpc();
|
||||
const auto& state = app->getWalletState();
|
||||
|
||||
// Height input
|
||||
ImGui::Text("Block Height:");
|
||||
ImGui::Text("%s", TR("block_height"));
|
||||
ImGui::SetNextItemWidth(heightInput.width);
|
||||
ImGui::InputInt("##Height", &s_height);
|
||||
if (s_height < 1) s_height = 1;
|
||||
@@ -123,7 +123,7 @@ void BlockInfoDialog::render(App* app)
|
||||
ImGui::BeginDisabled();
|
||||
}
|
||||
|
||||
if (material::StyledButton("Get Block Info", ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::StyledButton(TR("block_get_info"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
if (rpc && rpc->isConnected()) {
|
||||
s_loading = true;
|
||||
s_error.clear();
|
||||
@@ -138,7 +138,7 @@ void BlockInfoDialog::render(App* app)
|
||||
if (s_loading) {
|
||||
ImGui::EndDisabled();
|
||||
ImGui::SameLine();
|
||||
ImGui::TextDisabled("Loading...");
|
||||
ImGui::TextDisabled("%s", TR("loading"));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
@@ -155,23 +155,23 @@ void BlockInfoDialog::render(App* app)
|
||||
// Block info display
|
||||
if (s_has_data) {
|
||||
// Block hash
|
||||
ImGui::Text("Block Hash:");
|
||||
ImGui::Text("%s", TR("block_hash"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
|
||||
ImGui::TextWrapped("%s", s_block_hash.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Click to copy");
|
||||
ImGui::SetTooltip("%s", TR("click_to_copy"));
|
||||
}
|
||||
if (ImGui::IsItemClicked()) {
|
||||
ImGui::SetClipboardText(s_block_hash.c_str());
|
||||
Notifications::instance().success("Block hash copied");
|
||||
Notifications::instance().success(TR("block_hash_copied"));
|
||||
}
|
||||
|
||||
ImGui::Spacing();
|
||||
|
||||
// Timestamp
|
||||
ImGui::Text("Timestamp:");
|
||||
ImGui::Text("%s", TR("block_timestamp"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
if (s_block_time > 0) {
|
||||
std::time_t t = static_cast<std::time_t>(s_block_time);
|
||||
@@ -179,21 +179,21 @@ void BlockInfoDialog::render(App* app)
|
||||
std::strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S", std::localtime(&t));
|
||||
ImGui::Text("%s", time_buf);
|
||||
} else {
|
||||
ImGui::TextDisabled("Unknown");
|
||||
ImGui::TextDisabled("%s", TR("unknown"));
|
||||
}
|
||||
|
||||
// Confirmations
|
||||
ImGui::Text("Confirmations:");
|
||||
ImGui::Text("%s", TR("confirmations"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
ImGui::Text("%d", s_confirmations);
|
||||
|
||||
// Transaction count
|
||||
ImGui::Text("Transactions:");
|
||||
ImGui::Text("%s", TR("block_transactions"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
ImGui::Text("%d", s_tx_count);
|
||||
|
||||
// Size
|
||||
ImGui::Text("Size:");
|
||||
ImGui::Text("%s", TR("block_size"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
if (s_block_size > 1024 * 1024) {
|
||||
ImGui::Text("%.2f MB", s_block_size / (1024.0 * 1024.0));
|
||||
@@ -204,12 +204,12 @@ void BlockInfoDialog::render(App* app)
|
||||
}
|
||||
|
||||
// Difficulty
|
||||
ImGui::Text("Difficulty:");
|
||||
ImGui::Text("%s", TR("difficulty"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
ImGui::Text("%.4f", s_difficulty);
|
||||
|
||||
// Bits
|
||||
ImGui::Text("Bits:");
|
||||
ImGui::Text("%s", TR("block_bits"));
|
||||
ImGui::SameLine(lbl.position);
|
||||
ImGui::Text("%s", s_bits.c_str());
|
||||
|
||||
@@ -218,7 +218,7 @@ void BlockInfoDialog::render(App* app)
|
||||
ImGui::Spacing();
|
||||
|
||||
// Merkle root
|
||||
ImGui::Text("Merkle Root:");
|
||||
ImGui::Text("%s", TR("block_merkle_root"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.7f, 0.7f, 0.7f, 1.0f));
|
||||
ImGui::TextWrapped("%s", s_merkle_root.c_str());
|
||||
ImGui::PopStyleColor();
|
||||
@@ -227,7 +227,7 @@ void BlockInfoDialog::render(App* app)
|
||||
|
||||
// Previous block
|
||||
if (!s_prev_hash.empty()) {
|
||||
ImGui::Text("Previous Block:");
|
||||
ImGui::Text("%s", TR("block_previous"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
|
||||
|
||||
// Truncate for display
|
||||
@@ -239,7 +239,7 @@ void BlockInfoDialog::render(App* app)
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Click to view previous block");
|
||||
ImGui::SetTooltip("%s", TR("block_click_prev"));
|
||||
}
|
||||
if (ImGui::IsItemClicked() && s_height > 1) {
|
||||
s_height--;
|
||||
@@ -249,7 +249,7 @@ void BlockInfoDialog::render(App* app)
|
||||
|
||||
// Next block
|
||||
if (!s_next_hash.empty()) {
|
||||
ImGui::Text("Next Block:");
|
||||
ImGui::Text("%s", TR("block_next"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(0.6f, 0.8f, 1.0f, 1.0f));
|
||||
|
||||
// Truncate for display
|
||||
@@ -261,7 +261,7 @@ void BlockInfoDialog::render(App* app)
|
||||
ImGui::PopStyleColor();
|
||||
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ImGui::SetTooltip("Click to view next block");
|
||||
ImGui::SetTooltip("%s", TR("block_click_next"));
|
||||
}
|
||||
if (ImGui::IsItemClicked()) {
|
||||
s_height++;
|
||||
@@ -276,7 +276,7 @@ void BlockInfoDialog::render(App* app)
|
||||
// Navigation buttons
|
||||
if (s_has_data) {
|
||||
if (s_height > 1) {
|
||||
if (material::StyledButton("<< Previous", ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::StyledButton(TR("block_nav_prev"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
s_height--;
|
||||
s_has_data = false;
|
||||
s_error.clear();
|
||||
@@ -285,7 +285,7 @@ void BlockInfoDialog::render(App* app)
|
||||
}
|
||||
|
||||
if (!s_next_hash.empty()) {
|
||||
if (material::StyledButton("Next >>", ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::StyledButton(TR("block_nav_next"), ImVec2(0,0), S.resolveFont(closeBtn.font))) {
|
||||
s_height++;
|
||||
s_has_data = false;
|
||||
s_error.clear();
|
||||
@@ -295,7 +295,7 @@ void BlockInfoDialog::render(App* app)
|
||||
|
||||
// Close button at bottom
|
||||
ImGui::SetCursorPosY(ImGui::GetWindowHeight() - 40);
|
||||
if (material::StyledButton("Close", ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) {
|
||||
if (material::StyledButton(TR("close"), ImVec2(closeBtn.width, 0), S.resolveFont(closeBtn.font))) {
|
||||
s_open = false;
|
||||
}
|
||||
material::EndOverlayDialog();
|
||||
|
||||
Reference in New Issue
Block a user