1/**************************************************************************/
2/* editor_property_name_processor.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "editor_property_name_processor.h"
32
33#include "core/string/translation.h"
34#include "editor_settings.h"
35
36EditorPropertyNameProcessor *EditorPropertyNameProcessor::singleton = nullptr;
37
38EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_default_inspector_style() {
39 if (!EditorSettings::get_singleton()) {
40 return STYLE_CAPITALIZED;
41 }
42 const Style style = (Style)EDITOR_GET("interface/inspector/default_property_name_style").operator int();
43 if (style == STYLE_LOCALIZED && !is_localization_available()) {
44 return STYLE_CAPITALIZED;
45 }
46 return style;
47}
48
49EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_settings_style() {
50 if (!EditorSettings::get_singleton()) {
51 return STYLE_LOCALIZED;
52 }
53 const bool translate = EDITOR_GET("interface/editor/localize_settings");
54 return translate ? STYLE_LOCALIZED : STYLE_CAPITALIZED;
55}
56
57EditorPropertyNameProcessor::Style EditorPropertyNameProcessor::get_tooltip_style(Style p_style) {
58 return p_style == STYLE_LOCALIZED ? STYLE_CAPITALIZED : STYLE_LOCALIZED;
59}
60
61bool EditorPropertyNameProcessor::is_localization_available() {
62 if (!EditorSettings::get_singleton()) {
63 return false;
64 }
65 const Vector<String> forbidden = String("en").split(",");
66 return forbidden.find(EDITOR_GET("interface/editor/editor_language")) == -1;
67}
68
69String EditorPropertyNameProcessor::_capitalize_name(const String &p_name) const {
70 HashMap<String, String>::ConstIterator cached = capitalize_string_cache.find(p_name);
71 if (cached) {
72 return cached->value;
73 }
74
75 Vector<String> parts = p_name.split("_", false);
76 for (int i = 0; i < parts.size(); i++) {
77 // Articles/conjunctions/prepositions which should only be capitalized when not at beginning and end.
78 if (i > 0 && i + 1 < parts.size() && stop_words.find(parts[i]) != -1) {
79 continue;
80 }
81 HashMap<String, String>::ConstIterator remap = capitalize_string_remaps.find(parts[i]);
82 if (remap) {
83 parts.write[i] = remap->value;
84 } else {
85 parts.write[i] = parts[i].capitalize();
86 }
87 }
88 const String capitalized = String(" ").join(parts);
89
90 capitalize_string_cache[p_name] = capitalized;
91 return capitalized;
92}
93
94String EditorPropertyNameProcessor::process_name(const String &p_name, Style p_style) const {
95 switch (p_style) {
96 case STYLE_RAW: {
97 return p_name;
98 } break;
99
100 case STYLE_CAPITALIZED: {
101 return _capitalize_name(p_name);
102 } break;
103
104 case STYLE_LOCALIZED: {
105 const String capitalized = _capitalize_name(p_name);
106 if (TranslationServer::get_singleton()) {
107 return TranslationServer::get_singleton()->property_translate(capitalized);
108 }
109 return capitalized;
110 } break;
111 }
112 ERR_FAIL_V_MSG(p_name, "Unexpected property name style.");
113}
114
115String EditorPropertyNameProcessor::translate_group_name(const String &p_name) const {
116 if (TranslationServer::get_singleton()) {
117 return TranslationServer::get_singleton()->property_translate(p_name);
118 }
119 return p_name;
120}
121
122EditorPropertyNameProcessor::EditorPropertyNameProcessor() {
123 ERR_FAIL_COND(singleton != nullptr);
124 singleton = this;
125
126 // The following initialization is parsed by the l10n extraction script with a regex.
127 // The map name and value definition format should be kept synced with the regex.
128 // https://github.com/godotengine/godot-editor-l10n/blob/main/scripts/common.py
129 capitalize_string_remaps["2d"] = "2D";
130 capitalize_string_remaps["3d"] = "3D";
131 capitalize_string_remaps["aa"] = "AA";
132 capitalize_string_remaps["aabb"] = "AABB";
133 capitalize_string_remaps["adb"] = "ADB";
134 capitalize_string_remaps["ao"] = "AO";
135 capitalize_string_remaps["api"] = "API";
136 capitalize_string_remaps["apk"] = "APK";
137 capitalize_string_remaps["arm32"] = "arm32";
138 capitalize_string_remaps["arm64"] = "arm64";
139 capitalize_string_remaps["arm64-v8a"] = "arm64-v8a";
140 capitalize_string_remaps["armeabi-v7a"] = "armeabi-v7a";
141 capitalize_string_remaps["arvr"] = "ARVR";
142 capitalize_string_remaps["astc"] = "ASTC";
143 capitalize_string_remaps["bbcode"] = "BBCode";
144 capitalize_string_remaps["bg"] = "BG";
145 capitalize_string_remaps["bidi"] = "BiDi";
146 capitalize_string_remaps["bp"] = "BP";
147 capitalize_string_remaps["bpc"] = "BPC";
148 capitalize_string_remaps["bpm"] = "BPM";
149 capitalize_string_remaps["bptc"] = "BPTC";
150 capitalize_string_remaps["bvh"] = "BVH";
151 capitalize_string_remaps["ca"] = "CA";
152 capitalize_string_remaps["ccdik"] = "CCDIK";
153 capitalize_string_remaps["cd"] = "CD";
154 capitalize_string_remaps["cpu"] = "CPU";
155 capitalize_string_remaps["csg"] = "CSG";
156 capitalize_string_remaps["db"] = "dB";
157 capitalize_string_remaps["dof"] = "DoF";
158 capitalize_string_remaps["dpi"] = "DPI";
159 capitalize_string_remaps["dtls"] = "DTLS";
160 capitalize_string_remaps["eol"] = "EOL";
161 capitalize_string_remaps["erp"] = "ERP";
162 capitalize_string_remaps["etc"] = "ETC";
163 capitalize_string_remaps["etc2"] = "ETC2";
164 capitalize_string_remaps["fabrik"] = "FABRIK";
165 capitalize_string_remaps["fbx"] = "FBX";
166 capitalize_string_remaps["fbx2gltf"] = "FBX2glTF";
167 capitalize_string_remaps["fft"] = "FFT";
168 capitalize_string_remaps["fg"] = "FG";
169 capitalize_string_remaps["filesystem"] = "FileSystem";
170 capitalize_string_remaps["fov"] = "FOV";
171 capitalize_string_remaps["fps"] = "FPS";
172 capitalize_string_remaps["fs"] = "FS";
173 capitalize_string_remaps["fsr"] = "FSR";
174 capitalize_string_remaps["fxaa"] = "FXAA";
175 capitalize_string_remaps["gdscript"] = "GDScript";
176 capitalize_string_remaps["ggx"] = "GGX";
177 capitalize_string_remaps["gi"] = "GI";
178 capitalize_string_remaps["gl"] = "GL";
179 capitalize_string_remaps["glb"] = "GLB";
180 capitalize_string_remaps["gles2"] = "GLES2";
181 capitalize_string_remaps["gles3"] = "GLES3";
182 capitalize_string_remaps["gltf"] = "glTF";
183 capitalize_string_remaps["gpu"] = "GPU";
184 capitalize_string_remaps["gui"] = "GUI";
185 capitalize_string_remaps["guid"] = "GUID";
186 capitalize_string_remaps["hdr"] = "HDR";
187 capitalize_string_remaps["hidpi"] = "hiDPI";
188 capitalize_string_remaps["hipass"] = "High-pass";
189 capitalize_string_remaps["hl"] = "HL";
190 capitalize_string_remaps["hsv"] = "HSV";
191 capitalize_string_remaps["html"] = "HTML";
192 capitalize_string_remaps["http"] = "HTTP";
193 capitalize_string_remaps["id"] = "ID";
194 capitalize_string_remaps["ids"] = "IDs";
195 capitalize_string_remaps["igd"] = "IGD";
196 capitalize_string_remaps["ik"] = "IK";
197 capitalize_string_remaps["image@2x"] = "Image @2x";
198 capitalize_string_remaps["image@3x"] = "Image @3x";
199 capitalize_string_remaps["iod"] = "IOD";
200 capitalize_string_remaps["ios"] = "iOS";
201 capitalize_string_remaps["ip"] = "IP";
202 capitalize_string_remaps["ipad"] = "iPad";
203 capitalize_string_remaps["iphone"] = "iPhone";
204 capitalize_string_remaps["ipv6"] = "IPv6";
205 capitalize_string_remaps["ir"] = "IR";
206 capitalize_string_remaps["itunes"] = "iTunes";
207 capitalize_string_remaps["jit"] = "JIT";
208 capitalize_string_remaps["k1"] = "K1";
209 capitalize_string_remaps["k2"] = "K2";
210 capitalize_string_remaps["kb"] = "(KB)"; // Unit.
211 capitalize_string_remaps["lcd"] = "LCD";
212 capitalize_string_remaps["ldr"] = "LDR";
213 capitalize_string_remaps["lod"] = "LOD";
214 capitalize_string_remaps["lods"] = "LODs";
215 capitalize_string_remaps["lowpass"] = "Low-pass";
216 capitalize_string_remaps["macos"] = "macOS";
217 capitalize_string_remaps["mb"] = "(MB)"; // Unit.
218 capitalize_string_remaps["mjpeg"] = "MJPEG";
219 capitalize_string_remaps["mms"] = "MMS";
220 capitalize_string_remaps["ms"] = "(ms)"; // Unit
221 capitalize_string_remaps["msaa"] = "MSAA";
222 capitalize_string_remaps["msdf"] = "MSDF";
223 // Not used for now as AudioEffectReverb has a `msec` property.
224 //capitalize_string_remaps["msec"] = "(msec)"; // Unit.
225 capitalize_string_remaps["navmesh"] = "NavMesh";
226 capitalize_string_remaps["nfc"] = "NFC";
227 capitalize_string_remaps["ok"] = "OK";
228 capitalize_string_remaps["opengl"] = "OpenGL";
229 capitalize_string_remaps["opentype"] = "OpenType";
230 capitalize_string_remaps["openxr"] = "OpenXR";
231 capitalize_string_remaps["osslsigncode"] = "osslsigncode";
232 capitalize_string_remaps["pck"] = "PCK";
233 capitalize_string_remaps["png"] = "PNG";
234 capitalize_string_remaps["po2"] = "(Power of 2)"; // Unit.
235 capitalize_string_remaps["ppc32"] = "ppc32";
236 capitalize_string_remaps["ppc64"] = "ppc64";
237 capitalize_string_remaps["pvrtc"] = "PVRTC";
238 capitalize_string_remaps["pvs"] = "PVS";
239 capitalize_string_remaps["rcedit"] = "rcedit";
240 capitalize_string_remaps["rcodesign"] = "rcodesign";
241 capitalize_string_remaps["rgb"] = "RGB";
242 capitalize_string_remaps["rid"] = "RID";
243 capitalize_string_remaps["rmb"] = "RMB";
244 capitalize_string_remaps["rpc"] = "RPC";
245 capitalize_string_remaps["rv64"] = "rv64";
246 capitalize_string_remaps["s3tc"] = "S3TC";
247 capitalize_string_remaps["scp"] = "SCP";
248 capitalize_string_remaps["sdf"] = "SDF";
249 capitalize_string_remaps["sdfgi"] = "SDFGI";
250 capitalize_string_remaps["sdk"] = "SDK";
251 capitalize_string_remaps["sec"] = "(sec)"; // Unit.
252 capitalize_string_remaps["signtool"] = "signtool";
253 capitalize_string_remaps["sms"] = "SMS";
254 capitalize_string_remaps["srgb"] = "sRGB";
255 capitalize_string_remaps["ssao"] = "SSAO";
256 capitalize_string_remaps["ssh"] = "SSH";
257 capitalize_string_remaps["ssil"] = "SSIL";
258 capitalize_string_remaps["ssl"] = "SSL";
259 capitalize_string_remaps["sss"] = "SSS";
260 capitalize_string_remaps["stderr"] = "stderr";
261 capitalize_string_remaps["stdout"] = "stdout";
262 capitalize_string_remaps["sv"] = "SV";
263 capitalize_string_remaps["svg"] = "SVG";
264 capitalize_string_remaps["taa"] = "TAA";
265 capitalize_string_remaps["tcp"] = "TCP";
266 capitalize_string_remaps["textfile"] = "TextFile";
267 capitalize_string_remaps["tls"] = "TLS";
268 capitalize_string_remaps["ui"] = "UI";
269 capitalize_string_remaps["uri"] = "URI";
270 capitalize_string_remaps["url"] = "URL";
271 capitalize_string_remaps["urls"] = "URLs";
272 capitalize_string_remaps["us"] = U"(µs)"; // Unit.
273 capitalize_string_remaps["usb"] = "USB";
274 capitalize_string_remaps["usec"] = U"(µsec)"; // Unit.
275 capitalize_string_remaps["uuid"] = "UUID";
276 capitalize_string_remaps["uv"] = "UV";
277 capitalize_string_remaps["uv1"] = "UV1";
278 capitalize_string_remaps["uv2"] = "UV2";
279 capitalize_string_remaps["vector2"] = "Vector2";
280 capitalize_string_remaps["vpn"] = "VPN";
281 capitalize_string_remaps["vram"] = "VRAM";
282 capitalize_string_remaps["vrs"] = "VRS";
283 capitalize_string_remaps["vsync"] = "V-Sync";
284 capitalize_string_remaps["wap"] = "WAP";
285 capitalize_string_remaps["webp"] = "WebP";
286 capitalize_string_remaps["webrtc"] = "WebRTC";
287 capitalize_string_remaps["websocket"] = "WebSocket";
288 capitalize_string_remaps["wine"] = "wine";
289 capitalize_string_remaps["wifi"] = "Wi-Fi";
290 capitalize_string_remaps["x86"] = "x86";
291 capitalize_string_remaps["x86_32"] = "x86_32";
292 capitalize_string_remaps["x86_64"] = "x86_64";
293 capitalize_string_remaps["xr"] = "XR";
294 capitalize_string_remaps["xray"] = "X-Ray";
295 capitalize_string_remaps["xy"] = "XY";
296 capitalize_string_remaps["xz"] = "XZ";
297 capitalize_string_remaps["yz"] = "YZ";
298
299 // Articles, conjunctions, prepositions.
300 // The following initialization is parsed in `editor/translations/scripts/common.py` with a regex.
301 // The word definition format should be kept synced with the regex.
302 stop_words = LocalVector<String>({
303 "a",
304 "an",
305 "and",
306 "as",
307 "at",
308 "by",
309 "for",
310 "in",
311 "not",
312 "of",
313 "on",
314 "or",
315 "over",
316 "per",
317 "the",
318 "then",
319 "to",
320 });
321}
322
323EditorPropertyNameProcessor::~EditorPropertyNameProcessor() {
324 singleton = nullptr;
325}
326