11#include < StdInc.h>
2+
23#include < imgui.h>
34#include < imgui_internal.h>
4- #include < ConsoleHost.h>
55
6+ #include < ConsoleHost.h>
67#include < CoreConsole.h>
78
89#include < shellapi.h>
910
11+ #include < skyr/url.hpp>
12+ #include < fmt/format.h>
13+
1014#include < UrlConfirmationExport.h>
1115
1216#if defined(GTA_FIVE)
1519#define PRODUCT_NAME " RedM"
1620#endif
1721
22+ extern ImFont* consoleFontSmall;
23+
24+ namespace
25+ {
26+ struct LocalizedStrings
27+ {
28+ std::string heading;
29+ std::string warning;
30+ std::string visitSite;
31+ std::string cancel;
32+ };
33+
34+ const std::unordered_map<std::string, LocalizedStrings> g_translations = {
35+ { " en-US" , { " This link will take you to the website below" , " You are now leaving {0} and visiting an unaffiliated external site. This link may not be secure. Only click on links from trusted senders." , " Visit Site" , " Cancel" } },
36+ { " de-DE" , { " Dieser Link führt dich zur folgenden Website" , " Du verlässt jetzt {0} und begibst dich auf eine externe Seite eines Drittanbieters. Die Sicherheit des Links kann nicht gewährleistet werden. Klicke nur auf Links, wenn du dem Absender vertraust." , " Website besuchen" , " Abbrechen" } },
37+ { " es-ES" , { " Este enlace te llevará al sitio web a continuación" , " Estás abandonando {0} y serás redirigido a un sitio web externo no afiliado con Rockstar Games. Puede que este enlace no sea seguro. Haz clic solamente en enlaces procedentes de fuentes de fiar." , " Visitar sitio" , " Cancelar" } },
38+ { " es-MX" , { " Este enlace te llevará al sitio web a continuación" , " Estás por salir de {0} y se te dirigirá a una página externa no afiliada. Este enlace podría no ser seguro. Haz clic solo en enlaces de fuentes seguras." , " Visitar sitio" , " Cancelar" } },
39+ { " fr-FR" , { " Ce lien vous dirigera vers le site ci-dessous" , " Vous allez quitter {0} et accéder à un site externe non affilié. Ce lien n'est peut-être pas sûr. Cliquez uniquement sur les liens d'expéditeurs de confiance." , " Visiter le site" , " Annuler" } },
40+ { " it-IT" , { " Questo link ti porterà al sito web qui sotto" , " Stai uscendo da {0} per visitare un sito esterno non affiliato. Questo link potrebbe non essere sicuro. Clicca solo su link forniti da mittenti attendibili." , " Visita il sito" , " Annulla" } },
41+ { " pl-PL" , { " Ten link przeniesie Cię na poniższą stronę" , " Zamierzasz opuścić {0} i udać się na zewnętrzną stronę sieciową. Ten odnośnik może nie być bezpieczny. Otwieraj odnośniki jedynie z zaufanych źródeł." , " Odwiedź stronę" , " Anuluj" } },
42+ { " pt-BR" , { " Este link levará você ao site abaixo" , " Você está saindo do {0} e acessando um site externo não afiliado. O link por não ser seguro. Clique apenas em links de fontes confiáveis." , " Visitar site" , " Cancelar" } },
43+ { " ru-RU" , { " Эта ссылка приведёт вас на сайт ниже" , " Вы покидаете {0} и переходите на сторонний сайт. Ссылка может быть ненадежной. Переходите только по ссылкам от доверенных отправителей." , " Перейти на сайт" , " Отмена" } }
44+ };
45+
46+ const LocalizedStrings& GetLocalizedStrings ()
47+ {
48+ // Read the user's preferred languages from registry
49+ HKEY hKey;
50+ if (RegOpenKeyExW (HKEY_CURRENT_USER, L" Control Panel\\ International\\ User Profile" , 0 , KEY_READ, &hKey) == ERROR_SUCCESS)
51+ {
52+ DWORD bufferSize = 0 ;
53+ if (RegGetValueW (hKey, nullptr , L" Languages" , RRF_RT_REG_MULTI_SZ, nullptr , nullptr , &bufferSize) == ERROR_SUCCESS && bufferSize > 0 )
54+ {
55+ std::vector<wchar_t > buffer (bufferSize / sizeof (wchar_t ));
56+ if (RegGetValueW (hKey, nullptr , L" Languages" , RRF_RT_REG_MULTI_SZ, nullptr , buffer.data (), &bufferSize) == ERROR_SUCCESS)
57+ {
58+ RegCloseKey (hKey);
59+
60+ // Buffer contains null-separated list of languages in preference order
61+ const wchar_t * current = buffer.data ();
62+ while (*current)
63+ {
64+ std::string locale = ToNarrow (current);
65+
66+ auto it = g_translations.find (locale);
67+
68+ // Check for exact match first
69+ if (it != g_translations.end ())
70+ {
71+ return it->second ;
72+ }
73+
74+ // Try language-only fallback (e.g., "en -> "en-US")
75+ auto dashPos = locale.find (' -' );
76+ std::string langOnly = (dashPos != std::string::npos) ? locale.substr (0 , dashPos) : locale;
77+
78+ for (const auto & [key, value] : g_translations)
79+ {
80+ if (key.substr (0 , key.find (' -' )) == langOnly)
81+ {
82+ return value;
83+ }
84+ }
85+
86+ current += wcslen (current) + 1 ;
87+ }
88+ }
89+ }
90+ RegCloseKey (hKey);
91+ }
92+
93+ // Fallback to English if no match found
94+ return g_translations.at (" en-US" );
95+ }
96+ }
97+
1898void DrawUrlConfirmationModal ()
1999{
20100 if (!nui::g_showUrlConfirmModal.load ())
@@ -26,67 +106,108 @@ void DrawUrlConfirmationModal()
26106
27107 std::lock_guard<std::mutex> lock (nui::g_urlModalMutex);
28108
29- ImGui::PushStyleColor (ImGuiCol_PopupBg, ImVec4 (0 .11f , 0 .11f , 0 .13f , 1 .0f ));
30- ImGui::PushStyleColor (ImGuiCol_Border, ImVec4 (0 .20f , 0 .20f , 0 .22f , 1 .0f ));
31- ImGui::PushStyleVar (ImGuiStyleVar_WindowRounding, 4 .0f );
32- ImGui::PushStyleVar (ImGuiStyleVar_WindowPadding, ImVec2 (32 .0f , 24 .0f ));
33- ImGui::PushStyleVar (ImGuiStyleVar_WindowBorderSize, 1 .0f );
34- ImGui::PushStyleVar (ImGuiStyleVar_ItemSpacing, ImVec2 (8 .0f , 8 .0f ));
109+ const auto & strings = GetLocalizedStrings ();
110+
111+ ImGui::PushStyleColor (ImGuiCol_PopupBg, IM_COL32 (26 , 26 , 26 , 255 ));
112+ ImGui::PushStyleColor (ImGuiCol_Border, IM_COL32 (26 , 26 , 26 , 255 ));
113+ ImGui::PushStyleVar (ImGuiStyleVar_WindowRounding, 10 .0f );
114+ ImGui::PushStyleVar (ImGuiStyleVar_WindowPadding, ImVec2 (45 .0f , 50 .0f ));
115+ ImGui::PushStyleVar (ImGuiStyleVar_WindowBorderSize, 0 .0f );
35116
36117 ImVec2 center = ImGui::GetMainViewport ()->GetCenter ();
37118 ImGui::SetNextWindowPos (center, ImGuiCond_Always, ImVec2 (0 .5f , 0 .5f ));
38- ImGui::SetNextWindowSize (ImVec2 (850 , 0 ), ImGuiCond_Always);
119+ ImGui::SetNextWindowSize (ImVec2 (960 , 0 ), ImGuiCond_Always);
39120
40121 ImGuiWindowFlags flags = ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoCollapse;
41122
42123 if (ImGui::BeginPopupModal (" ##UrlConfirmModal" , nullptr , flags))
43124 {
44- ImVec2 windowSize = ImGui::GetWindowSize ();
45- ImVec2 windowPadding = ImGui::GetStyle ().WindowPadding ;
46-
47- // Content area
48- ImGui::SetCursorPos (ImVec2 (windowPadding.x , windowPadding.y + 16 .0f ));
49-
50125 // Main heading
51- ImGui::PushStyleColor (ImGuiCol_Text, ImVec4 ( 0 . 85f , 0 . 85f , 0 . 87f , 1 . 0f ));
52- ImGui::Text (" This link will take you to the website below " );
126+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 ( 235 , 235 , 235 , 255 ));
127+ ImGui::Text (" %s " , strings. heading . c_str () );
53128 ImGui::PopStyleColor ();
54129
55130 ImGui::Dummy (ImVec2 (0 , 8 .0f ));
56131
57- ImGui::PushStyleColor (ImGuiCol_Text, ImVec4 (0 .95f , 0 .75f , 0 .25f , 1 .0f ));
58- ImGui::Text (" %s" , nui::g_pendingUrl.c_str ());
59- ImGui::PopStyleColor ();
132+ std::string hostDisplay;
133+
134+ auto parsed = skyr::make_url (nui::g_pendingUrl);
135+ if (parsed && !parsed->host ().empty ())
136+ {
137+ hostDisplay = parsed->host ();
138+ }
139+ else
140+ {
141+ hostDisplay = nui::g_pendingUrl;
142+ }
143+
144+ size_t hostPos = nui::g_pendingUrl.find (hostDisplay);
145+ if (hostPos != std::string::npos && hostDisplay != nui::g_pendingUrl)
146+ {
147+ std::string beforeHost = nui::g_pendingUrl.substr (0 , hostPos);
148+ std::string afterHost = nui::g_pendingUrl.substr (hostPos + hostDisplay.length ());
149+
150+ // Display scheme
151+ if (!beforeHost.empty ())
152+ {
153+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (235 , 235 , 235 , 255 ));
154+ ImGui::Text (" %s" , beforeHost.c_str ());
155+ ImGui::PopStyleColor ();
156+ ImGui::SameLine (0 , 0 );
157+ }
158+
159+ // Display host in accent color
160+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (248 , 171 , 19 , 255 ));
161+ ImGui::Text (" %s" , hostDisplay.c_str ());
162+ ImGui::PopStyleColor ();
163+
164+ // Display path/query
165+ if (!afterHost.empty ())
166+ {
167+ ImGui::SameLine (0 , 0 );
168+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (235 , 235 , 235 , 255 ));
169+ ImGui::Text (" %s" , afterHost.c_str ());
170+ ImGui::PopStyleColor ();
171+ }
172+ }
173+ else
174+ {
175+ // Fallback: display entire URL in accent color if host not found
176+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (248 , 171 , 19 , 255 ));
177+ ImGui::Text (" %s" , nui::g_pendingUrl.c_str ());
178+ ImGui::PopStyleColor ();
179+ }
60180
61181 ImGui::Dummy (ImVec2 (0 , 12 .0f ));
62182
63183 // Separator
64- ImGui::PushStyleColor (ImGuiCol_Separator, ImVec4 ( 0 . 25f , 0 . 25f , 0 . 28f , 1 . 0f ));
184+ ImGui::PushStyleColor (ImGuiCol_Separator, IM_COL32 ( 44 , 44 , 44 , 255 ));
65185 ImGui::Separator ();
66186 ImGui::PopStyleColor ();
67187
68188 ImGui::Dummy (ImVec2 (0 , 12 .0f ));
69189
70190 // Warning text
71- ImGui::PushStyleColor (ImGuiCol_Text, ImVec4 (0 .55f , 0 .55f , 0 .57f , 1 .0f ));
72- ImGui::TextWrapped (" You are now leaving " PRODUCT_NAME " and visiting an unaffiliated external site." );
73- ImGui::TextWrapped (" This link may not be secure. Only click on links from trusted senders." );
191+ ImGui::PushFont (consoleFontSmall);
192+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 (152 , 152 , 152 , 255 ));
193+ auto warningText = fmt::format (fmt::runtime (strings.warning ), PRODUCT_NAME);
194+ ImGui::TextWrapped (" %s" , warningText.c_str ());
74195 ImGui::PopStyleColor ();
196+ ImGui::PopFont ();
75197
76198 ImGui::Dummy (ImVec2 (0 , 20 .0f ));
77199
78200 float buttonWidth = ImGui::GetContentRegionAvail ().x ;
79- float buttonHeight = 44 .0f ;
80201
81202 // Visit Site button
82- ImGui::PushStyleColor (ImGuiCol_Button, ImVec4 ( 0 . 10f , 0 . 30f , 0 . 55f , 1 . 0f ));
83- ImGui::PushStyleColor (ImGuiCol_ButtonHovered, ImVec4 ( 0 . 15f , 0 . 38f , 0 . 65f , 1 . 0f ));
84- ImGui::PushStyleColor (ImGuiCol_ButtonActive, ImVec4 ( 0 . 08f , 0 . 25f , 0 . 48f , 1 . 0f ));
85- ImGui::PushStyleColor (ImGuiCol_Text, ImVec4 ( 1 . 0f , 1 . 0f , 1 . 0f , 1 . 0f ));
86- ImGui::PushStyleVar (ImGuiStyleVar_FrameRounding, 4 .0f );
203+ ImGui::PushStyleColor (ImGuiCol_Text, IM_COL32 ( 235 , 235 , 235 , 255 ));
204+ ImGui::PushStyleColor (ImGuiCol_Button, IM_COL32 ( 23 , 73 , 138 , 255 ));
205+ ImGui::PushStyleColor (ImGuiCol_ButtonHovered, IM_COL32 ( 35 , 95 , 170 , 255 ));
206+ ImGui::PushStyleColor (ImGuiCol_ButtonActive, IM_COL32 ( 18 , 58 , 110 , 255 ));
207+ ImGui::PushStyleVar (ImGuiStyleVar_FrameRounding, 6 .0f );
87208 ImGui::PushStyleVar (ImGuiStyleVar_FrameBorderSize, 0 .0f );
88209
89- if (ImGui::Button (" Visit Site " , ImVec2 (buttonWidth, buttonHeight )))
210+ if (ImGui::Button (strings. visitSite . c_str () , ImVec2 (buttonWidth, 90 . 0f )))
90211 {
91212 ShellExecuteW (nullptr , L" open" , ToWide (nui::g_pendingUrl).c_str (), nullptr , nullptr , SW_SHOWNORMAL);
92213 nui::g_pendingUrl.clear ();
@@ -97,17 +218,17 @@ void DrawUrlConfirmationModal()
97218 ImGui::PopStyleVar (2 );
98219 ImGui::PopStyleColor (3 );
99220
100- ImGui::Dummy (ImVec2 (0 , 8 .0f ));
221+ ImGui::Dummy (ImVec2 (0 , 30 .0f ));
101222
102223 // Cancel button
103- ImGui::PushStyleColor (ImGuiCol_Button, ImVec4 ( 0 . 15f , 0 . 15f , 0 . 17f , 1 . 0f ));
104- ImGui::PushStyleColor (ImGuiCol_ButtonHovered, ImVec4 ( 0 . 20f , 0 . 20f , 0 . 23f , 1 . 0f ));
105- ImGui::PushStyleColor (ImGuiCol_ButtonActive, ImVec4 ( 0 . 12f , 0 . 12f , 0 . 14f , 1 . 0f ));
106- ImGui::PushStyleColor (ImGuiCol_Border, ImVec4 ( 0 . 35f , 0 . 35f , 0 . 38f , 1 . 0f ));
107- ImGui::PushStyleVar (ImGuiStyleVar_FrameRounding, 4 .0f );
108- ImGui::PushStyleVar (ImGuiStyleVar_FrameBorderSize, 1 .0f );
109-
110- if (ImGui::Button (" Cancel " , ImVec2 (buttonWidth, buttonHeight )))
224+ ImGui::PushStyleColor (ImGuiCol_Button, IM_COL32 ( 26 , 26 , 26 , 255 ));
225+ ImGui::PushStyleColor (ImGuiCol_ButtonHovered, IM_COL32 ( 50 , 50 , 53 , 255 ));
226+ ImGui::PushStyleColor (ImGuiCol_ButtonActive, IM_COL32 ( 35 , 35 , 38 , 255 ));
227+ ImGui::PushStyleColor (ImGuiCol_Border, IM_COL32 ( 63 , 63 , 63 , 255 ));
228+ ImGui::PushStyleVar (ImGuiStyleVar_FrameRounding, 6 .0f );
229+ ImGui::PushStyleVar (ImGuiStyleVar_FrameBorderSize, 2 .0f );
230+
231+ if (ImGui::Button (strings. cancel . c_str () , ImVec2 (buttonWidth, 80 . 0f )))
111232 {
112233 nui::g_pendingUrl.clear ();
113234 nui::g_showUrlConfirmModal.store (false );
@@ -117,9 +238,11 @@ void DrawUrlConfirmationModal()
117238 ImGui::PopStyleVar (2 );
118239 ImGui::PopStyleColor (5 );
119240
241+ ImGui::Dummy (ImVec2 (0 , 5 .0f ));
242+
120243 ImGui::EndPopup ();
121244 }
122245
123- ImGui::PopStyleVar (4 );
246+ ImGui::PopStyleVar (3 );
124247 ImGui::PopStyleColor (2 );
125248}
0 commit comments