i18n(wizard): translate the first-run wizard (was all hardcoded English)
The first-run wizard predated the i18n system — every label, button, and help line was hardcoded English. Route them all through TR(): - 62 literals in app_wizard.cpp replaced with TR(). Where a wizard string is a parallel of existing content, it reuses the existing key (the whole bootstrap_* family, plus theme / language / balance_layout / low_spec_mode / ui_opacity / console_scanline / download / retry / cancel) so it inherits their translations and stays consistent with Settings. Button labels drop their ##id suffix and use TR(key) directly (the app's established TactileButton(TR(...)) pattern; the wizard's phase buttons never share a frame, so no id collision). - 55 new wiz_* English keys in i18n.cpp for the wizard-specific strings, and their translations back-filled across all 8 languages (es/de/fr/pt/ru/zh/ja/ko); format specifiers (%s, %zu) and newlines preserved. CJK subset rebuilt. Verified on the sweep: English renders with correct labels in place (all keys resolve), and a German run shows the reused + new keys translated end-to-end (Darstellung / Acryl-Glaseffekte / Verschlüsseln & Weiter …). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -189,14 +189,14 @@ void App::renderFirstRunWizard() {
|
||||
headerCy += logoSize + 8.0f * dp;
|
||||
|
||||
{
|
||||
const char* welcomeTitle = "Welcome to ObsidianDragon!";
|
||||
const char* welcomeTitle = TR("wiz_welcome_title");
|
||||
ImVec2 wts = titleFont->CalcTextSizeA(titleFont->LegacySize, FLT_MAX, 0, welcomeTitle);
|
||||
dl->AddText(titleFont, titleFont->LegacySize,
|
||||
ImVec2(winPos.x + (winSize.x - wts.x) * 0.5f, headerCy), textCol, welcomeTitle);
|
||||
headerCy += wts.y + 6.0f * dp;
|
||||
|
||||
// Warmer, less-sparse header: a one-line subtitle under the welcome (dimmed body).
|
||||
const char* welcomeSub = "A few quick choices and your full node is ready.";
|
||||
const char* welcomeSub = TR("wiz_welcome_sub");
|
||||
ImVec2 sts = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, 0, welcomeSub);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(winPos.x + (winSize.x - sts.x) * 0.5f, headerCy), dimCol, welcomeSub);
|
||||
@@ -274,13 +274,13 @@ void App::renderFirstRunWizard() {
|
||||
{
|
||||
float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state));
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 1");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step1"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
|
||||
// Title
|
||||
{
|
||||
const char* t = "Appearance";
|
||||
const char* t = TR("wiz_appearance");
|
||||
dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t);
|
||||
cy += titleFont->LegacySize + 10.0f * dp;
|
||||
}
|
||||
@@ -336,14 +336,14 @@ void App::renderFirstRunWizard() {
|
||||
for (const auto& skin : skins) {
|
||||
if (skin.id == skinMgr.activeSkinId()) { activePreview = skin.name; break; }
|
||||
}
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Theme");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("theme"));
|
||||
float comboX = cx + 110.0f * dp;
|
||||
float comboW = contentW - 110.0f * dp;
|
||||
ImGui::SetCursorScreenPos(ImVec2(comboX, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp);
|
||||
ImGui::SetNextItemWidth(comboW);
|
||||
if (ImGui::BeginCombo("##wiz_theme", activePreview.c_str())) {
|
||||
ImGui::TextDisabled("Built-in");
|
||||
ImGui::TextDisabled(TR("wiz_theme_builtin"));
|
||||
ImGui::Separator();
|
||||
for (const auto& skin : skins) {
|
||||
if (!skin.bundled) continue;
|
||||
@@ -359,7 +359,7 @@ void App::renderFirstRunWizard() {
|
||||
for (const auto& skin : skins) { if (!skin.bundled) { hasCustom = true; break; } }
|
||||
if (hasCustom) {
|
||||
ImGui::Spacing();
|
||||
ImGui::TextDisabled("Custom");
|
||||
ImGui::TextDisabled(TR("wiz_theme_custom"));
|
||||
ImGui::Separator();
|
||||
for (const auto& skin : skins) {
|
||||
if (skin.bundled) continue;
|
||||
@@ -367,7 +367,7 @@ void App::renderFirstRunWizard() {
|
||||
if (!skin.valid) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImVec4(1,0.3f,0.3f,1));
|
||||
ImGui::BeginDisabled(true);
|
||||
ImGui::Selectable((skin.name + " (invalid)").c_str(), false);
|
||||
ImGui::Selectable((skin.name + TR("wiz_theme_invalid")).c_str(), false);
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleColor();
|
||||
} else {
|
||||
@@ -395,7 +395,7 @@ void App::renderFirstRunWizard() {
|
||||
for (const auto& l : layouts) {
|
||||
if (l.id == wiz_balance_layout) { balPreview = l.name; break; }
|
||||
}
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Balance Layout");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("balance_layout"));
|
||||
float comboX = cx + 110.0f * dp;
|
||||
float comboW = contentW - 110.0f * dp;
|
||||
ImGui::SetCursorScreenPos(ImVec2(comboX, cy));
|
||||
@@ -426,7 +426,7 @@ void App::renderFirstRunWizard() {
|
||||
langNames.reserve(languages.size());
|
||||
for (const auto& lang : languages) langNames.push_back(lang.second.c_str());
|
||||
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, "Language");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy + 4.0f * dp), textCol, TR("language"));
|
||||
float comboX = cx + 110.0f * dp;
|
||||
float comboW = contentW - 110.0f * dp;
|
||||
ImGui::SetCursorScreenPos(ImVec2(comboX, cy));
|
||||
@@ -497,10 +497,10 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::SameLine();
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol,
|
||||
"Low-spec mode");
|
||||
TR("low_spec_mode"));
|
||||
cy += bodyFont->LegacySize + 6.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, "Disable all heavy visual effects");
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_lowspec_desc"));
|
||||
cy += captionFont->LegacySize + 16.0f * dp;
|
||||
|
||||
ImGui::BeginDisabled(wiz_low_spec);
|
||||
@@ -508,15 +508,15 @@ void App::renderFirstRunWizard() {
|
||||
// Acrylic blur slider
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(cx, cy + 2.0f * dp), textCol,
|
||||
"Acrylic glass effects");
|
||||
TR("wiz_acrylic"));
|
||||
cy += bodyFont->LegacySize + 4.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx, cy), dimCol, "Translucent blur on panels (Off disables)");
|
||||
ImVec2(cx, cy), dimCol, TR("wiz_acrylic_desc"));
|
||||
cy += captionFont->LegacySize + 10.0f * dp;
|
||||
|
||||
{
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + 4.0f * dp, cy), textCol, "Level:");
|
||||
ImVec2(cx + 4.0f * dp, cy), textCol, TR("wiz_level"));
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx + 72.0f * dp, cy - 2.0f * dp));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 4.0f * dp);
|
||||
float sliderW = contentW - 72.0f * dp;
|
||||
@@ -524,7 +524,7 @@ void App::renderFirstRunWizard() {
|
||||
{
|
||||
char blur_fmt[16];
|
||||
if (wiz_blur_amount < 0.01f)
|
||||
snprintf(blur_fmt, sizeof(blur_fmt), "Off");
|
||||
snprintf(blur_fmt, sizeof(blur_fmt), TR("wiz_off"));
|
||||
else
|
||||
snprintf(blur_fmt, sizeof(blur_fmt), "%.0f%%%%", wiz_blur_amount * 25.0f);
|
||||
if (ImGui::SliderFloat("##wiz_blur", &wiz_blur_amount, 0.0f, 4.0f, blur_fmt,
|
||||
@@ -558,19 +558,19 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::SameLine();
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol,
|
||||
"Theme visual effects");
|
||||
TR("wiz_theme_effects"));
|
||||
cy += bodyFont->LegacySize + 6.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, "Animated borders, color wash");
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_theme_effects_desc"));
|
||||
cy += captionFont->LegacySize + 16.0f * dp;
|
||||
|
||||
// UI Opacity slider
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(cx, cy + 2.0f * dp), textCol,
|
||||
"UI Opacity");
|
||||
TR("ui_opacity"));
|
||||
cy += bodyFont->LegacySize + 4.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx, cy), dimCol, "Card & sidebar transparency (1.0 = solid)");
|
||||
ImVec2(cx, cy), dimCol, TR("wiz_ui_opacity_desc"));
|
||||
cy += captionFont->LegacySize + 10.0f * dp;
|
||||
{
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy - 2.0f * dp));
|
||||
@@ -599,10 +599,10 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::SameLine();
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize,
|
||||
ImVec2(ImGui::GetCursorScreenPos().x, cy + 2.0f * dp), textCol,
|
||||
"Console scanline");
|
||||
TR("console_scanline"));
|
||||
cy += bodyFont->LegacySize + 6.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, "CRT scanline effect in console");
|
||||
ImVec2(cx + 28.0f * dp, cy), dimCol, TR("wiz_scanline_desc"));
|
||||
cy += captionFont->LegacySize + 24.0f * dp;
|
||||
|
||||
ImGui::EndDisabled(); // low-spec
|
||||
@@ -620,7 +620,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(ui::material::PrimaryVariant()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Continue##app", ImVec2(btnW, btnH))) {
|
||||
if (ui::material::TactileButton(TR("wiz_continue"), ImVec2(btnW, btnH))) {
|
||||
// Save appearance choices, advance to Bootstrap
|
||||
settings_->setAcrylicEnabled(wiz_blur_amount > 0.001f);
|
||||
settings_->setAcrylicQuality(wiz_blur_amount > 0.001f
|
||||
@@ -667,23 +667,23 @@ void App::renderFirstRunWizard() {
|
||||
float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state));
|
||||
float labelX = cx + iconW + 4.0f * dp;
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(labelX, cy), dimCol, "Step 2");
|
||||
float step2W = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, "Step 2").x;
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(labelX, cy), dimCol, TR("wiz_step2"));
|
||||
float step2W = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, 0, TR("wiz_step2")).x;
|
||||
float titleX = labelX + step2W + 12.0f * dp;
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(titleX, cy), dimCol, "Bootstrap");
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(titleX, cy), dimCol, TR("wiz_bootstrap"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
} else {
|
||||
// Step indicator
|
||||
{
|
||||
float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state));
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 2");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step2"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
}
|
||||
|
||||
// Title
|
||||
{
|
||||
const char* t = "Bootstrap";
|
||||
const char* t = TR("wiz_bootstrap");
|
||||
dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t);
|
||||
cy += titleFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
@@ -706,11 +706,11 @@ void App::renderFirstRunWizard() {
|
||||
|
||||
const char* statusTitle;
|
||||
if (prog.state == util::Bootstrap::State::Downloading)
|
||||
statusTitle = "Downloading bootstrap...";
|
||||
statusTitle = TR("bootstrap_downloading");
|
||||
else if (prog.state == util::Bootstrap::State::Verifying)
|
||||
statusTitle = "Verifying checksums...";
|
||||
statusTitle = TR("bootstrap_verifying");
|
||||
else
|
||||
statusTitle = "Extracting blockchain data...";
|
||||
statusTitle = TR("bootstrap_extracting");
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, statusTitle);
|
||||
cy += bodyFont->LegacySize + 12.0f * dp;
|
||||
|
||||
@@ -739,7 +739,7 @@ void App::renderFirstRunWizard() {
|
||||
|
||||
if (prog.state == util::Bootstrap::State::Extracting) {
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
dimCol, "(wallet.dat is protected)");
|
||||
dimCol, TR("bootstrap_wallet_protected"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
|
||||
@@ -755,9 +755,9 @@ void App::renderFirstRunWizard() {
|
||||
dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f),
|
||||
dotR, dotCol);
|
||||
const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos
|
||||
? "Daemon stopping..."
|
||||
: "Daemon running")
|
||||
: "Daemon stopped";
|
||||
? TR("bootstrap_daemon_stopping")
|
||||
: TR("bootstrap_daemon_running"))
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy),
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label);
|
||||
@@ -772,7 +772,7 @@ void App::renderFirstRunWizard() {
|
||||
float cancelBX = rightX + (colW - cancelW) * 0.5f;
|
||||
ImGui::SetCursorScreenPos(ImVec2(cancelBX, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Cancel##bs", ImVec2(cancelW, cancelH))) {
|
||||
if (ui::material::TactileButton(TR("cancel"), ImVec2(cancelW, cancelH))) {
|
||||
bootstrap_->cancel();
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
@@ -799,10 +799,10 @@ void App::renderFirstRunWizard() {
|
||||
errMsg = bootstrap_->getProgress().error;
|
||||
bootstrap_.reset();
|
||||
}
|
||||
if (errMsg.empty()) errMsg = "Bootstrap failed";
|
||||
if (errMsg.empty()) errMsg = TR("wiz_bootstrap_failed");
|
||||
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), "Download Failed");
|
||||
ui::material::Error(), TR("wiz_download_failed"));
|
||||
cy += bodyFont->LegacySize + 8.0f * dp;
|
||||
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol,
|
||||
@@ -821,7 +821,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!supportsFullNodeLifecycleActions());
|
||||
if (ui::material::TactileButton("Retry##bs", ImVec2(btnW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("retry"), ImVec2(btnW2, btnH2))) {
|
||||
// Stop embedded daemon before bootstrap to avoid chain data corruption
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
@@ -835,7 +835,7 @@ void App::renderFirstRunWizard() {
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx + btnW2 + 12.0f * dp, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Skip##bsfail", ImVec2(btnW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(btnW2, btnH2))) {
|
||||
wizard_phase_ = WizardPhase::EncryptOffer;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
@@ -878,11 +878,11 @@ void App::renderFirstRunWizard() {
|
||||
{
|
||||
float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol, ICON_MD_WARNING);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, "External daemon running");
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, TR("wiz_ext_daemon_running"));
|
||||
}
|
||||
cy += bodyFont->LegacySize + 4.0f * dp;
|
||||
{
|
||||
const char* warnBody = "It must be stopped before downloading a bootstrap, otherwise chain data could be corrupted.";
|
||||
const char* warnBody = TR("wiz_ext_daemon_warning");
|
||||
ImVec2 ws = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, contentW, warnBody);
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol, warnBody, nullptr, contentW);
|
||||
cy += ws.y + 12.0f * dp;
|
||||
@@ -905,9 +905,9 @@ void App::renderFirstRunWizard() {
|
||||
IM_COL32(220, 60, 60, 255)));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 255, 255, 255));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Stop Daemon##wiz", ImVec2(stopW, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_stop_daemon"), ImVec2(stopW, btnH2))) {
|
||||
wizard_stopping_external_ = true;
|
||||
wizard_stop_status_ = "Sending stop command...";
|
||||
wizard_stop_status_ = TR("wiz_daemon_sending_stop");
|
||||
async_tasks_.submit("wizard-stop-external-daemon", [this](const util::AsyncTaskManager::Token& token) {
|
||||
auto config = rpc::Connection::autoDetectConfig();
|
||||
if (!config.rpcuser.empty() && !config.rpcpassword.empty()) {
|
||||
@@ -919,17 +919,17 @@ void App::renderFirstRunWizard() {
|
||||
tmp_rpc->disconnect();
|
||||
}
|
||||
}
|
||||
wizard_stop_status_ = "Waiting for daemon to shut down...";
|
||||
wizard_stop_status_ = TR("wiz_daemon_waiting_stop");
|
||||
for (int i = 0; i < 60 && !token.cancelled(); i++) {
|
||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||
if (!daemon::EmbeddedDaemon::isRpcPortInUse()) {
|
||||
wizard_stop_status_ = "Daemon stopped.";
|
||||
wizard_stop_status_ = TR("wiz_daemon_stopped_ok");
|
||||
wizard_stopping_external_ = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (token.cancelled()) return;
|
||||
wizard_stop_status_ = "Daemon did not stop — try manually.";
|
||||
wizard_stop_status_ = TR("wiz_daemon_stop_failed");
|
||||
wizard_stopping_external_ = false;
|
||||
});
|
||||
}
|
||||
@@ -938,7 +938,7 @@ void App::renderFirstRunWizard() {
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx + stopW + 12.0f * dp, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Skip##extd", ImVec2(skipW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) {
|
||||
wizard_phase_ = WizardPhase::EncryptOffer;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
@@ -947,7 +947,7 @@ void App::renderFirstRunWizard() {
|
||||
} else {
|
||||
// --- Normal bootstrap offer ---
|
||||
{
|
||||
const char* bsText = "Download a blockchain bootstrap to dramatically speed up initial sync.\n\nYour existing wallet.dat will NOT be modified or replaced.";
|
||||
const char* bsText = TR("wiz_bootstrap_desc");
|
||||
ImVec2 bsSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, bsText);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, bsText, nullptr, contentW);
|
||||
cy += bsSize.y + 8.0f * dp;
|
||||
@@ -960,7 +960,7 @@ void App::renderFirstRunWizard() {
|
||||
ImU32 warnCol = (textCol & 0x00FFFFFF) | ((ImU32)(255 * warnOpacity) << 24);
|
||||
float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol, ICON_MD_WARNING);
|
||||
const char* twText = "Only use bootstrap.dragonx.is or bootstrap2.dragonx.is. Using files from untrusted sources could compromise your node.";
|
||||
const char* twText = TR("bootstrap_trust_warning");
|
||||
float twWrap = contentW - iw - 4.0f * dp;
|
||||
ImVec2 twSize = captionFont->CalcTextSizeA(captionFont->LegacySize, FLT_MAX, twWrap, twText);
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol, twText, nullptr, twWrap);
|
||||
@@ -979,9 +979,9 @@ void App::renderFirstRunWizard() {
|
||||
dl->AddCircleFilled(ImVec2(cx + dotR, cy + captionFont->LegacySize * 0.5f),
|
||||
dotR, dotCol);
|
||||
const char* label = daemonUp ? (dStatus.find("Stopping") != std::string::npos
|
||||
? "Daemon stopping..."
|
||||
: "Daemon running")
|
||||
: "Daemon stopped";
|
||||
? TR("bootstrap_daemon_stopping")
|
||||
: TR("bootstrap_daemon_running"))
|
||||
: TR("bootstrap_daemon_stopped");
|
||||
dl->AddText(captionFont, captionFont->LegacySize,
|
||||
ImVec2(cx + dotR * 2.0f + 6.0f * dp, cy),
|
||||
(dimCol & 0x00FFFFFF) | IM_COL32(0,0,0,140), label);
|
||||
@@ -1004,7 +1004,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!supportsFullNodeLifecycleActions());
|
||||
if (ui::material::TactileButton("Download##bs", ImVec2(dlBtnW, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("download"), ImVec2(dlBtnW, btnH2))) {
|
||||
// Stop embedded daemon before bootstrap to avoid chain data corruption
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
@@ -1023,7 +1023,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnSurface()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!supportsFullNodeLifecycleActions());
|
||||
if (ui::material::TactileButton("Mirror##bs_mirror", ImVec2(mirrorW, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("bootstrap_mirror"), ImVec2(mirrorW, btnH2))) {
|
||||
stopDaemonForBootstrap();
|
||||
bootstrap_ = std::make_unique<util::Bootstrap>();
|
||||
std::string dataDir = util::Platform::getDragonXDataDir();
|
||||
@@ -1033,7 +1033,7 @@ void App::renderFirstRunWizard() {
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
if (ImGui::IsItemHovered()) {
|
||||
ui::material::Tooltip("Download from mirror (bootstrap2.dragonx.is).\nUse this if the main download is slow or failing.");
|
||||
ui::material::Tooltip(TR("bootstrap_mirror_tooltip"));
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
ImGui::PopStyleColor(3);
|
||||
@@ -1041,7 +1041,7 @@ void App::renderFirstRunWizard() {
|
||||
// --- Skip button ---
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx + dlBtnW + 8.0f * dp + mirrorW + 8.0f * dp, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Skip##bs", ImVec2(skipW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) {
|
||||
wizard_phase_ = WizardPhase::EncryptOffer;
|
||||
}
|
||||
ImGui::PopStyleVar();
|
||||
@@ -1093,14 +1093,14 @@ void App::renderFirstRunWizard() {
|
||||
{
|
||||
float iconW = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, stepIcon(state)).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), dimCol, stepIcon(state));
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, "Step 3");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx + iconW + 4.0f * dp, cy), dimCol, TR("wiz_step3"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
}
|
||||
|
||||
// Title (changes for PinSetup sub-state)
|
||||
{
|
||||
const char* t = (isFocused && wizard_phase_ == WizardPhase::PinSetup)
|
||||
? "Quick-Unlock PIN" : "Encryption";
|
||||
? TR("wiz_pin_title") : TR("wiz_encryption");
|
||||
dl->AddText(titleFont, titleFont->LegacySize, ImVec2(cx, cy), textCol, t);
|
||||
cy += titleFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
@@ -1117,11 +1117,11 @@ void App::renderFirstRunWizard() {
|
||||
ImU32 okCol = ui::material::Secondary();
|
||||
float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_VERIFIED_USER).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), okCol, ICON_MD_VERIFIED_USER);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 6.0f * dp, cy), okCol, "Wallet is already encrypted");
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 6.0f * dp, cy), okCol, TR("wiz_already_encrypted"));
|
||||
cy += bodyFont->LegacySize + 12.0f * dp;
|
||||
}
|
||||
{
|
||||
const char* desc = "Your wallet is protected with a passphrase. No further action is needed.";
|
||||
const char* desc = TR("wiz_already_encrypted_desc");
|
||||
ImVec2 ds = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, desc);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, desc, nullptr, contentW);
|
||||
cy += ds.y + 20.0f * dp;
|
||||
@@ -1136,7 +1136,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImGui::ColorConvertU32ToFloat4(ui::material::PrimaryVariant()));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Continue##encok", ImVec2(btnW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_continue"), ImVec2(btnW2, btnH2))) {
|
||||
wizard_phase_ = WizardPhase::Done;
|
||||
settings_->setWizardCompleted(true);
|
||||
settings_->save();
|
||||
@@ -1148,7 +1148,7 @@ void App::renderFirstRunWizard() {
|
||||
} else if (isFocused) {
|
||||
// ---- Encryption offer + optional PIN (combined) ----
|
||||
{
|
||||
const char* encDesc = "Encrypt your wallet to protect private keys with a passphrase.";
|
||||
const char* encDesc = TR("wiz_encrypt_desc");
|
||||
ImVec2 edSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, encDesc);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), textCol, encDesc, nullptr, contentW);
|
||||
cy += edSize.y + 6.0f * dp;
|
||||
@@ -1157,7 +1157,7 @@ void App::renderFirstRunWizard() {
|
||||
ImU32 warnCol2 = ui::material::Warning();
|
||||
float iw = iconFont->CalcTextSizeA(iconFont->LegacySize, FLT_MAX, 0, ICON_MD_WARNING).x;
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(cx, cy), warnCol2, ICON_MD_WARNING);
|
||||
const char* warnLoss = "If you lose your passphrase, you lose access to your funds.";
|
||||
const char* warnLoss = TR("wiz_encrypt_warning");
|
||||
float wlWrap = contentW - iw - 4.0f * dp;
|
||||
ImVec2 wlSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, wlWrap, warnLoss);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx + iw + 4.0f * dp, cy), warnCol2, warnLoss, nullptr, wlWrap);
|
||||
@@ -1165,7 +1165,7 @@ void App::renderFirstRunWizard() {
|
||||
}
|
||||
|
||||
// Passphrase input
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Passphrase:");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_passphrase"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||
@@ -1177,7 +1177,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PopItemWidth();
|
||||
cy += 36.0f * dp + 6.0f * dp;
|
||||
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Confirm:");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_confirm"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||
@@ -1192,16 +1192,16 @@ void App::renderFirstRunWizard() {
|
||||
// Strength meter
|
||||
{
|
||||
size_t len = strlen(encrypt_pass_buf_);
|
||||
const char* strengthLabel = "Weak";
|
||||
const char* strengthLabel = TR("wiz_strength_weak");
|
||||
ImU32 strengthCol = ui::material::Error();
|
||||
float strengthPct = 0.25f;
|
||||
|
||||
if (len >= 16) {
|
||||
strengthLabel = "Strong"; strengthCol = ui::material::Secondary(); strengthPct = 1.0f;
|
||||
strengthLabel = TR("wiz_strength_strong"); strengthCol = ui::material::Secondary(); strengthPct = 1.0f;
|
||||
} else if (len >= 12) {
|
||||
strengthLabel = "Good"; strengthCol = ui::material::Secondary(); strengthPct = 0.75f;
|
||||
strengthLabel = TR("wiz_strength_good"); strengthCol = ui::material::Secondary(); strengthPct = 0.75f;
|
||||
} else if (len >= 8) {
|
||||
strengthLabel = "Fair"; strengthCol = ui::material::Warning(); strengthPct = 0.5f;
|
||||
strengthLabel = TR("wiz_strength_fair"); strengthCol = ui::material::Warning(); strengthPct = 0.5f;
|
||||
}
|
||||
|
||||
float sBarH = 4.0f * dp, sBarR = 2.0f * dp;
|
||||
@@ -1214,7 +1214,7 @@ void App::renderFirstRunWizard() {
|
||||
cy += sBarH + 4.0f * dp;
|
||||
|
||||
char slabel[64];
|
||||
snprintf(slabel, sizeof(slabel), "Strength: %s", strengthLabel);
|
||||
snprintf(slabel, sizeof(slabel), TR("wiz_strength"), strengthLabel);
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, slabel);
|
||||
cy += captionFont->LegacySize + 10.0f * dp;
|
||||
}
|
||||
@@ -1224,14 +1224,14 @@ void App::renderFirstRunWizard() {
|
||||
size_t pLen = strlen(encrypt_pass_buf_);
|
||||
if (pLen > 0 && pLen < 8) {
|
||||
char fb[80];
|
||||
snprintf(fb, sizeof(fb), "Passphrase must be at least 8 characters (%zu/8)", pLen);
|
||||
snprintf(fb, sizeof(fb), TR("wiz_pass_too_short"), pLen);
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), fb);
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
} else if (pLen >= 8 && strlen(encrypt_confirm_buf_) > 0 &&
|
||||
strcmp(encrypt_pass_buf_, encrypt_confirm_buf_) != 0) {
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), "Passphrases do not match");
|
||||
ui::material::Error(), TR("wiz_pass_mismatch"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
}
|
||||
@@ -1243,12 +1243,12 @@ void App::renderFirstRunWizard() {
|
||||
cy += 8.0f * dp;
|
||||
|
||||
{
|
||||
const char* pinTitle = "Quick-Unlock PIN (optional)";
|
||||
const char* pinTitle = TR("wiz_pin_optional");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), textCol, pinTitle);
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
}
|
||||
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "PIN (4-8 digits):");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_pin_label"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||
@@ -1260,7 +1260,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PopItemWidth();
|
||||
cy += 36.0f * dp + 6.0f * dp;
|
||||
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, "Confirm PIN:");
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy), dimCol, TR("wiz_pin_confirm"));
|
||||
cy += captionFont->LegacySize + 4.0f * dp;
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, cy));
|
||||
@@ -1277,12 +1277,12 @@ void App::renderFirstRunWizard() {
|
||||
std::string pinStr(wizard_pin_buf_);
|
||||
if (!pinStr.empty() && !util::SecureVault::isValidPin(pinStr)) {
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), "PIN must be 4-8 digits");
|
||||
ui::material::Error(), TR("wiz_pin_invalid"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
} else if (!pinStr.empty() && strlen(wizard_pin_confirm_buf_) > 0 &&
|
||||
pinStr != std::string(wizard_pin_confirm_buf_)) {
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), "PINs do not match");
|
||||
ui::material::Error(), TR("wiz_pin_mismatch"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
}
|
||||
@@ -1303,7 +1303,7 @@ void App::renderFirstRunWizard() {
|
||||
}
|
||||
if (passEdgeSpace) {
|
||||
dl->AddText(captionFont, captionFont->LegacySize, ImVec2(cx, cy),
|
||||
ui::material::Error(), "Passphrase has leading/trailing spaces — remove them");
|
||||
ui::material::Error(), TR("wiz_pass_spaces"));
|
||||
cy += captionFont->LegacySize + 6.0f * dp;
|
||||
}
|
||||
|
||||
@@ -1333,7 +1333,7 @@ void App::renderFirstRunWizard() {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, ImGui::ColorConvertU32ToFloat4(ui::material::OnPrimary()));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
ImGui::BeginDisabled(!canEncrypt);
|
||||
if (ui::material::TactileButton("Encrypt & Continue##wiz", ImVec2(encBtnW, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_encrypt_continue"), ImVec2(encBtnW, btnH2))) {
|
||||
// Save passphrase + optional PIN for background processing
|
||||
wallet_security_.beginDeferredEncryption(
|
||||
std::string(encrypt_pass_buf_),
|
||||
@@ -1356,7 +1356,7 @@ void App::renderFirstRunWizard() {
|
||||
wizard_phase_ = WizardPhase::Done;
|
||||
settings_->setWizardCompleted(true);
|
||||
settings_->save();
|
||||
ui::Notifications::instance().info("Encryption will complete in the background");
|
||||
ui::Notifications::instance().info(TR("wiz_encrypt_bg"));
|
||||
}
|
||||
ImGui::EndDisabled();
|
||||
ImGui::PopStyleVar();
|
||||
@@ -1364,12 +1364,12 @@ void App::renderFirstRunWizard() {
|
||||
|
||||
ImGui::SetCursorScreenPos(ImVec2(bx + encBtnW + 12.0f * dp, cy));
|
||||
ImGui::PushStyleVar(ImGuiStyleVar_FrameRounding, 8.0f * dp);
|
||||
if (ui::material::TactileButton("Skip##enc", ImVec2(skipW2, btnH2))) {
|
||||
if (ui::material::TactileButton(TR("wiz_skip"), ImVec2(skipW2, btnH2))) {
|
||||
static bool s_skipEncConfirm = false;
|
||||
if (!s_skipEncConfirm) {
|
||||
// Skipping stores private keys UNENCRYPTED — require a confirming second click.
|
||||
s_skipEncConfirm = true;
|
||||
encrypt_status_ = "Continue WITHOUT encryption? Keys will be stored unencrypted — click Skip again to confirm.";
|
||||
encrypt_status_ = TR("wiz_skip_confirm");
|
||||
} else {
|
||||
s_skipEncConfirm = false;
|
||||
wizard_phase_ = WizardPhase::Done;
|
||||
@@ -1389,7 +1389,7 @@ void App::renderFirstRunWizard() {
|
||||
}
|
||||
} else {
|
||||
// ---- Not focused: show static description ----
|
||||
const char* encDesc = "Encrypt your wallet to protect private keys with a passphrase.";
|
||||
const char* encDesc = TR("wiz_encrypt_desc");
|
||||
ImVec2 edSize = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, contentW, encDesc);
|
||||
dl->AddText(bodyFont, bodyFont->LegacySize, ImVec2(cx, cy), dimCol, encDesc, nullptr, contentW);
|
||||
cy += edSize.y + 6.0f * dp;
|
||||
|
||||
@@ -464,6 +464,63 @@ void I18n::loadBuiltinEnglish()
|
||||
strings_["tt_reduce_motion"] = "Disable animated transitions and balance lerp for accessibility";
|
||||
strings_["ago"] = "ago";
|
||||
strings_["wizard_daemon_start_failed"] = "Failed to start daemon \xe2\x80\x94 it will be retried automatically";
|
||||
// First-run wizard (app_wizard.cpp). Bootstrap/appearance labels reuse existing keys; these are
|
||||
// the wizard-specific strings.
|
||||
strings_["wiz_welcome_title"] = "Welcome to ObsidianDragon!";
|
||||
strings_["wiz_welcome_sub"] = "A few quick choices and your full node is ready.";
|
||||
strings_["wiz_step1"] = "Step 1";
|
||||
strings_["wiz_step2"] = "Step 2";
|
||||
strings_["wiz_step3"] = "Step 3";
|
||||
strings_["wiz_appearance"] = "Appearance";
|
||||
strings_["wiz_theme_builtin"] = "Built-in";
|
||||
strings_["wiz_theme_custom"] = "Custom";
|
||||
strings_["wiz_theme_invalid"] = " (invalid)";
|
||||
strings_["wiz_lowspec_desc"] = "Disable all heavy visual effects";
|
||||
strings_["wiz_acrylic"] = "Acrylic glass effects";
|
||||
strings_["wiz_acrylic_desc"] = "Translucent blur on panels (Off disables)";
|
||||
strings_["wiz_level"] = "Level:";
|
||||
strings_["wiz_off"] = "Off";
|
||||
strings_["wiz_theme_effects"] = "Theme visual effects";
|
||||
strings_["wiz_theme_effects_desc"] = "Animated borders, color wash";
|
||||
strings_["wiz_ui_opacity_desc"] = "Card & sidebar transparency (1.0 = solid)";
|
||||
strings_["wiz_scanline_desc"] = "CRT scanline effect in console";
|
||||
strings_["wiz_bootstrap"] = "Bootstrap";
|
||||
strings_["wiz_bootstrap_desc"] = "Download a blockchain bootstrap to dramatically speed up initial sync.\n\nYour existing wallet.dat will NOT be modified or replaced.";
|
||||
strings_["wiz_skip"] = "Skip";
|
||||
strings_["wiz_bootstrap_failed"] = "Bootstrap failed";
|
||||
strings_["wiz_download_failed"] = "Download Failed";
|
||||
strings_["wiz_ext_daemon_running"] = "External daemon running";
|
||||
strings_["wiz_ext_daemon_warning"] = "It must be stopped before downloading a bootstrap, otherwise chain data could be corrupted.";
|
||||
strings_["wiz_stop_daemon"] = "Stop Daemon";
|
||||
strings_["wiz_daemon_sending_stop"] = "Sending stop command...";
|
||||
strings_["wiz_daemon_waiting_stop"] = "Waiting for daemon to shut down...";
|
||||
strings_["wiz_daemon_stopped_ok"] = "Daemon stopped.";
|
||||
strings_["wiz_daemon_stop_failed"] = "Daemon did not stop \xe2\x80\x94 try manually.";
|
||||
strings_["wiz_encryption"] = "Encryption";
|
||||
strings_["wiz_pin_title"] = "Quick-Unlock PIN";
|
||||
strings_["wiz_already_encrypted"] = "Wallet is already encrypted";
|
||||
strings_["wiz_already_encrypted_desc"] = "Your wallet is protected with a passphrase. No further action is needed.";
|
||||
strings_["wiz_continue"] = "Continue";
|
||||
strings_["wiz_encrypt_desc"] = "Encrypt your wallet to protect private keys with a passphrase.";
|
||||
strings_["wiz_encrypt_warning"] = "If you lose your passphrase, you lose access to your funds.";
|
||||
strings_["wiz_passphrase"] = "Passphrase:";
|
||||
strings_["wiz_confirm"] = "Confirm:";
|
||||
strings_["wiz_strength_weak"] = "Weak";
|
||||
strings_["wiz_strength_fair"] = "Fair";
|
||||
strings_["wiz_strength_good"] = "Good";
|
||||
strings_["wiz_strength_strong"] = "Strong";
|
||||
strings_["wiz_strength"] = "Strength: %s";
|
||||
strings_["wiz_pass_too_short"] = "Passphrase must be at least 8 characters (%zu/8)";
|
||||
strings_["wiz_pass_mismatch"] = "Passphrases do not match";
|
||||
strings_["wiz_pass_spaces"] = "Passphrase has leading/trailing spaces \xe2\x80\x94 remove them";
|
||||
strings_["wiz_encrypt_continue"] = "Encrypt & Continue";
|
||||
strings_["wiz_encrypt_bg"] = "Encryption will complete in the background";
|
||||
strings_["wiz_skip_confirm"] = "Continue WITHOUT encryption? Keys will be stored unencrypted \xe2\x80\x94 click Skip again to confirm.";
|
||||
strings_["wiz_pin_optional"] = "Quick-Unlock PIN (optional)";
|
||||
strings_["wiz_pin_label"] = "PIN (4-8 digits):";
|
||||
strings_["wiz_pin_confirm"] = "Confirm PIN:";
|
||||
strings_["wiz_pin_invalid"] = "PIN must be 4-8 digits";
|
||||
strings_["wiz_pin_mismatch"] = "PINs do not match";
|
||||
strings_["settings_data_dir"] = "Data Dir:";
|
||||
strings_["settings_wallet_size_label"] = "Wallet Size:";
|
||||
strings_["settings_debug_changed"] = "Debug categories changed \xe2\x80\x94 restart daemon to apply";
|
||||
|
||||
Reference in New Issue
Block a user