translation_domain.cpp 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. /**************************************************************************/
  2. /* translation_domain.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. #include "translation_domain.h"
  31. #include "core/string/translation.h"
  32. #include "core/string/translation_server.h"
  33. #include "core/variant/typed_array.h"
  34. struct _character_accent_pair {
  35. const char32_t character;
  36. const char32_t *accented_character;
  37. };
  38. static _character_accent_pair _character_to_accented[] = {
  39. { 'A', U"Å" },
  40. { 'B', U"ß" },
  41. { 'C', U"Ç" },
  42. { 'D', U"Ð" },
  43. { 'E', U"É" },
  44. { 'F', U"F́" },
  45. { 'G', U"Ĝ" },
  46. { 'H', U"Ĥ" },
  47. { 'I', U"Ĩ" },
  48. { 'J', U"Ĵ" },
  49. { 'K', U"ĸ" },
  50. { 'L', U"Ł" },
  51. { 'M', U"Ḿ" },
  52. { 'N', U"й" },
  53. { 'O', U"Ö" },
  54. { 'P', U"Ṕ" },
  55. { 'Q', U"Q́" },
  56. { 'R', U"Ř" },
  57. { 'S', U"Ŝ" },
  58. { 'T', U"Ŧ" },
  59. { 'U', U"Ũ" },
  60. { 'V', U"Ṽ" },
  61. { 'W', U"Ŵ" },
  62. { 'X', U"X́" },
  63. { 'Y', U"Ÿ" },
  64. { 'Z', U"Ž" },
  65. { 'a', U"á" },
  66. { 'b', U"ḅ" },
  67. { 'c', U"ć" },
  68. { 'd', U"d́" },
  69. { 'e', U"é" },
  70. { 'f', U"f́" },
  71. { 'g', U"ǵ" },
  72. { 'h', U"h̀" },
  73. { 'i', U"í" },
  74. { 'j', U"ǰ" },
  75. { 'k', U"ḱ" },
  76. { 'l', U"ł" },
  77. { 'm', U"m̀" },
  78. { 'n', U"ή" },
  79. { 'o', U"ô" },
  80. { 'p', U"ṕ" },
  81. { 'q', U"q́" },
  82. { 'r', U"ŕ" },
  83. { 's', U"š" },
  84. { 't', U"ŧ" },
  85. { 'u', U"ü" },
  86. { 'v', U"ṽ" },
  87. { 'w', U"ŵ" },
  88. { 'x', U"x́" },
  89. { 'y', U"ý" },
  90. { 'z', U"ź" },
  91. };
  92. String TranslationDomain::_get_override_string(const String &p_message) const {
  93. String res;
  94. for (int i = 0; i < p_message.length(); i++) {
  95. if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
  96. res += p_message[i];
  97. res += p_message[i + 1];
  98. i++;
  99. continue;
  100. }
  101. res += '*';
  102. }
  103. return res;
  104. }
  105. String TranslationDomain::_double_vowels(const String &p_message) const {
  106. String res;
  107. for (int i = 0; i < p_message.length(); i++) {
  108. if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
  109. res += p_message[i];
  110. res += p_message[i + 1];
  111. i++;
  112. continue;
  113. }
  114. res += p_message[i];
  115. if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
  116. p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
  117. res += p_message[i];
  118. }
  119. }
  120. return res;
  121. }
  122. String TranslationDomain::_replace_with_accented_string(const String &p_message) const {
  123. String res;
  124. for (int i = 0; i < p_message.length(); i++) {
  125. if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
  126. res += p_message[i];
  127. res += p_message[i + 1];
  128. i++;
  129. continue;
  130. }
  131. const char32_t *accented = _get_accented_version(p_message[i]);
  132. if (accented) {
  133. res += accented;
  134. } else {
  135. res += p_message[i];
  136. }
  137. }
  138. return res;
  139. }
  140. String TranslationDomain::_wrap_with_fakebidi_characters(const String &p_message) const {
  141. String res;
  142. char32_t fakebidiprefix = U'\u202e';
  143. char32_t fakebidisuffix = U'\u202c';
  144. res += fakebidiprefix;
  145. // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
  146. for (int i = 0; i < p_message.length(); i++) {
  147. if (p_message[i] == '\n') {
  148. res += fakebidisuffix;
  149. res += p_message[i];
  150. res += fakebidiprefix;
  151. } else if (pseudolocalization.skip_placeholders_enabled && _is_placeholder(p_message, i)) {
  152. res += fakebidisuffix;
  153. res += p_message[i];
  154. res += p_message[i + 1];
  155. res += fakebidiprefix;
  156. i++;
  157. } else {
  158. res += p_message[i];
  159. }
  160. }
  161. res += fakebidisuffix;
  162. return res;
  163. }
  164. String TranslationDomain::_add_padding(const String &p_message, int p_length) const {
  165. String underscores = String("_").repeat(p_length * pseudolocalization.expansion_ratio / 2);
  166. String prefix = pseudolocalization.prefix + underscores;
  167. String suffix = underscores + pseudolocalization.suffix;
  168. return prefix + p_message + suffix;
  169. }
  170. const char32_t *TranslationDomain::_get_accented_version(char32_t p_character) const {
  171. if (!is_ascii_alphabet_char(p_character)) {
  172. return nullptr;
  173. }
  174. for (unsigned int i = 0; i < std_size(_character_to_accented); i++) {
  175. if (_character_to_accented[i].character == p_character) {
  176. return _character_to_accented[i].accented_character;
  177. }
  178. }
  179. return nullptr;
  180. }
  181. bool TranslationDomain::_is_placeholder(const String &p_message, int p_index) const {
  182. return p_index < p_message.length() - 1 && p_message[p_index] == '%' &&
  183. (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
  184. p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
  185. }
  186. StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_context) const {
  187. StringName res;
  188. int best_score = 0;
  189. for (const Ref<Translation> &E : translations) {
  190. int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
  191. if (score > 0 && score >= best_score) {
  192. const StringName r = E->get_message(p_message, p_context);
  193. if (!r) {
  194. continue;
  195. }
  196. res = r;
  197. best_score = score;
  198. if (score == 10) {
  199. break; // Exact match, skip the rest.
  200. }
  201. }
  202. }
  203. return res;
  204. }
  205. StringName TranslationDomain::get_message_from_translations(const String &p_locale, const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
  206. StringName res;
  207. int best_score = 0;
  208. for (const Ref<Translation> &E : translations) {
  209. int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
  210. if (score > 0 && score >= best_score) {
  211. const StringName r = E->get_plural_message(p_message, p_message_plural, p_n, p_context);
  212. if (!r) {
  213. continue;
  214. }
  215. res = r;
  216. best_score = score;
  217. if (score == 10) {
  218. break; // Exact match, skip the rest.
  219. }
  220. }
  221. }
  222. return res;
  223. }
  224. PackedStringArray TranslationDomain::get_loaded_locales() const {
  225. PackedStringArray locales;
  226. for (const Ref<Translation> &E : translations) {
  227. const String &locale = E->get_locale();
  228. if (!locales.has(locale)) {
  229. locales.push_back(locale);
  230. }
  231. }
  232. return locales;
  233. }
  234. #ifndef DISABLE_DEPRECATED
  235. Ref<Translation> TranslationDomain::get_translation_object(const String &p_locale) const {
  236. Ref<Translation> res;
  237. int best_score = 0;
  238. for (const Ref<Translation> &E : translations) {
  239. int score = TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale());
  240. if (score > 0 && score >= best_score) {
  241. res = E;
  242. best_score = score;
  243. if (score == 10) {
  244. break; // Exact match, skip the rest.
  245. }
  246. }
  247. }
  248. return res;
  249. }
  250. #endif
  251. void TranslationDomain::add_translation(const Ref<Translation> &p_translation) {
  252. ERR_FAIL_COND_MSG(p_translation.is_null(), "Invalid translation provided.");
  253. translations.insert(p_translation);
  254. }
  255. void TranslationDomain::remove_translation(const Ref<Translation> &p_translation) {
  256. translations.erase(p_translation);
  257. }
  258. void TranslationDomain::clear() {
  259. translations.clear();
  260. }
  261. const HashSet<Ref<Translation>> TranslationDomain::get_translations() const {
  262. return translations;
  263. }
  264. HashSet<Ref<Translation>> TranslationDomain::find_translations(const String &p_locale, bool p_exact) const {
  265. HashSet<Ref<Translation>> res;
  266. if (p_exact) {
  267. for (const Ref<Translation> &E : translations) {
  268. if (E->get_locale() == p_locale) {
  269. res.insert(E);
  270. }
  271. }
  272. } else {
  273. for (const Ref<Translation> &E : translations) {
  274. if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) {
  275. res.insert(E);
  276. }
  277. }
  278. }
  279. return res;
  280. }
  281. bool TranslationDomain::has_translation(const Ref<Translation> &p_translation) const {
  282. return translations.has(p_translation);
  283. }
  284. bool TranslationDomain::has_translation_for_locale(const String &p_locale, bool p_exact) const {
  285. if (p_exact) {
  286. for (const Ref<Translation> &E : translations) {
  287. if (E->get_locale() == p_locale) {
  288. return true;
  289. }
  290. }
  291. } else {
  292. for (const Ref<Translation> &E : translations) {
  293. if (TranslationServer::get_singleton()->compare_locales(p_locale, E->get_locale()) > 0) {
  294. return true;
  295. }
  296. }
  297. }
  298. return false;
  299. }
  300. TypedArray<Translation> TranslationDomain::get_translations_bind() const {
  301. TypedArray<Translation> res;
  302. res.reserve(translations.size());
  303. for (const Ref<Translation> &E : translations) {
  304. res.push_back(E);
  305. }
  306. return res;
  307. }
  308. TypedArray<Translation> TranslationDomain::find_translations_bind(const String &p_locale, bool p_exact) const {
  309. const HashSet<Ref<Translation>> &found = find_translations(p_locale, p_exact);
  310. TypedArray<Translation> res;
  311. res.reserve(found.size());
  312. for (const Ref<Translation> &E : found) {
  313. res.push_back(E);
  314. }
  315. return res;
  316. }
  317. StringName TranslationDomain::translate(const StringName &p_message, const StringName &p_context) const {
  318. if (!enabled) {
  319. return p_message;
  320. }
  321. const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override;
  322. StringName res = get_message_from_translations(locale, p_message, p_context);
  323. const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
  324. if (!res && fallback.length() >= 2) {
  325. res = get_message_from_translations(fallback, p_message, p_context);
  326. }
  327. if (!res) {
  328. return pseudolocalization.enabled ? pseudolocalize(p_message) : p_message;
  329. }
  330. return pseudolocalization.enabled ? pseudolocalize(res) : res;
  331. }
  332. StringName TranslationDomain::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
  333. if (!enabled) {
  334. return p_n == 1 ? p_message : p_message_plural;
  335. }
  336. const String &locale = locale_override.is_empty() ? TranslationServer::get_singleton()->get_locale() : locale_override;
  337. StringName res = get_message_from_translations(locale, p_message, p_message_plural, p_n, p_context);
  338. const String &fallback = TranslationServer::get_singleton()->get_fallback_locale();
  339. if (!res && fallback.length() >= 2) {
  340. res = get_message_from_translations(fallback, p_message, p_message_plural, p_n, p_context);
  341. }
  342. if (!res) {
  343. if (p_n == 1) {
  344. return p_message;
  345. }
  346. return p_message_plural;
  347. }
  348. return res;
  349. }
  350. String TranslationDomain::get_locale_override() const {
  351. return locale_override;
  352. }
  353. void TranslationDomain::set_locale_override(const String &p_locale) {
  354. locale_override = p_locale.is_empty() ? p_locale : TranslationServer::get_singleton()->standardize_locale(p_locale);
  355. }
  356. bool TranslationDomain::is_enabled() const {
  357. return enabled;
  358. }
  359. void TranslationDomain::set_enabled(bool p_enabled) {
  360. enabled = p_enabled;
  361. }
  362. bool TranslationDomain::is_pseudolocalization_enabled() const {
  363. return pseudolocalization.enabled;
  364. }
  365. void TranslationDomain::set_pseudolocalization_enabled(bool p_enabled) {
  366. pseudolocalization.enabled = p_enabled;
  367. }
  368. bool TranslationDomain::is_pseudolocalization_accents_enabled() const {
  369. return pseudolocalization.accents_enabled;
  370. }
  371. void TranslationDomain::set_pseudolocalization_accents_enabled(bool p_enabled) {
  372. pseudolocalization.accents_enabled = p_enabled;
  373. }
  374. bool TranslationDomain::is_pseudolocalization_double_vowels_enabled() const {
  375. return pseudolocalization.double_vowels_enabled;
  376. }
  377. void TranslationDomain::set_pseudolocalization_double_vowels_enabled(bool p_enabled) {
  378. pseudolocalization.double_vowels_enabled = p_enabled;
  379. }
  380. bool TranslationDomain::is_pseudolocalization_fake_bidi_enabled() const {
  381. return pseudolocalization.fake_bidi_enabled;
  382. }
  383. void TranslationDomain::set_pseudolocalization_fake_bidi_enabled(bool p_enabled) {
  384. pseudolocalization.fake_bidi_enabled = p_enabled;
  385. }
  386. bool TranslationDomain::is_pseudolocalization_override_enabled() const {
  387. return pseudolocalization.override_enabled;
  388. }
  389. void TranslationDomain::set_pseudolocalization_override_enabled(bool p_enabled) {
  390. pseudolocalization.override_enabled = p_enabled;
  391. }
  392. bool TranslationDomain::is_pseudolocalization_skip_placeholders_enabled() const {
  393. return pseudolocalization.skip_placeholders_enabled;
  394. }
  395. void TranslationDomain::set_pseudolocalization_skip_placeholders_enabled(bool p_enabled) {
  396. pseudolocalization.skip_placeholders_enabled = p_enabled;
  397. }
  398. float TranslationDomain::get_pseudolocalization_expansion_ratio() const {
  399. return pseudolocalization.expansion_ratio;
  400. }
  401. void TranslationDomain::set_pseudolocalization_expansion_ratio(float p_ratio) {
  402. pseudolocalization.expansion_ratio = p_ratio;
  403. }
  404. String TranslationDomain::get_pseudolocalization_prefix() const {
  405. return pseudolocalization.prefix;
  406. }
  407. void TranslationDomain::set_pseudolocalization_prefix(const String &p_prefix) {
  408. pseudolocalization.prefix = p_prefix;
  409. }
  410. String TranslationDomain::get_pseudolocalization_suffix() const {
  411. return pseudolocalization.suffix;
  412. }
  413. void TranslationDomain::set_pseudolocalization_suffix(const String &p_suffix) {
  414. pseudolocalization.suffix = p_suffix;
  415. }
  416. StringName TranslationDomain::pseudolocalize(const StringName &p_message) const {
  417. if (p_message.is_empty()) {
  418. return p_message;
  419. }
  420. String message = p_message;
  421. int length = message.length();
  422. if (pseudolocalization.override_enabled) {
  423. message = _get_override_string(message);
  424. }
  425. if (pseudolocalization.double_vowels_enabled) {
  426. message = _double_vowels(message);
  427. }
  428. if (pseudolocalization.accents_enabled) {
  429. message = _replace_with_accented_string(message);
  430. }
  431. if (pseudolocalization.fake_bidi_enabled) {
  432. message = _wrap_with_fakebidi_characters(message);
  433. }
  434. return _add_padding(message, length);
  435. }
  436. void TranslationDomain::_bind_methods() {
  437. #ifndef DISABLE_DEPRECATED
  438. ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationDomain::get_translation_object);
  439. #endif
  440. ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationDomain::add_translation);
  441. ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationDomain::remove_translation);
  442. ClassDB::bind_method(D_METHOD("clear"), &TranslationDomain::clear);
  443. ClassDB::bind_method(D_METHOD("get_translations"), &TranslationDomain::get_translations_bind);
  444. ClassDB::bind_method(D_METHOD("has_translation_for_locale", "locale", "exact"), &TranslationDomain::has_translation_for_locale);
  445. ClassDB::bind_method(D_METHOD("has_translation", "translation"), &TranslationDomain::has_translation);
  446. ClassDB::bind_method(D_METHOD("find_translations", "locale", "exact"), &TranslationDomain::find_translations_bind);
  447. ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationDomain::translate, DEFVAL(StringName()));
  448. ClassDB::bind_method(D_METHOD("translate_plural", "message", "message_plural", "n", "context"), &TranslationDomain::translate_plural, DEFVAL(StringName()));
  449. ClassDB::bind_method(D_METHOD("get_locale_override"), &TranslationDomain::get_locale_override);
  450. ClassDB::bind_method(D_METHOD("set_locale_override", "locale"), &TranslationDomain::set_locale_override);
  451. ClassDB::bind_method(D_METHOD("is_enabled"), &TranslationDomain::is_enabled);
  452. ClassDB::bind_method(D_METHOD("set_enabled", "enabled"), &TranslationDomain::set_enabled);
  453. ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationDomain::is_pseudolocalization_enabled);
  454. ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_enabled);
  455. ClassDB::bind_method(D_METHOD("is_pseudolocalization_accents_enabled"), &TranslationDomain::is_pseudolocalization_accents_enabled);
  456. ClassDB::bind_method(D_METHOD("set_pseudolocalization_accents_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_accents_enabled);
  457. ClassDB::bind_method(D_METHOD("is_pseudolocalization_double_vowels_enabled"), &TranslationDomain::is_pseudolocalization_double_vowels_enabled);
  458. ClassDB::bind_method(D_METHOD("set_pseudolocalization_double_vowels_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_double_vowels_enabled);
  459. ClassDB::bind_method(D_METHOD("is_pseudolocalization_fake_bidi_enabled"), &TranslationDomain::is_pseudolocalization_fake_bidi_enabled);
  460. ClassDB::bind_method(D_METHOD("set_pseudolocalization_fake_bidi_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_fake_bidi_enabled);
  461. ClassDB::bind_method(D_METHOD("is_pseudolocalization_override_enabled"), &TranslationDomain::is_pseudolocalization_override_enabled);
  462. ClassDB::bind_method(D_METHOD("set_pseudolocalization_override_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_override_enabled);
  463. ClassDB::bind_method(D_METHOD("is_pseudolocalization_skip_placeholders_enabled"), &TranslationDomain::is_pseudolocalization_skip_placeholders_enabled);
  464. ClassDB::bind_method(D_METHOD("set_pseudolocalization_skip_placeholders_enabled", "enabled"), &TranslationDomain::set_pseudolocalization_skip_placeholders_enabled);
  465. ClassDB::bind_method(D_METHOD("get_pseudolocalization_expansion_ratio"), &TranslationDomain::get_pseudolocalization_expansion_ratio);
  466. ClassDB::bind_method(D_METHOD("set_pseudolocalization_expansion_ratio", "ratio"), &TranslationDomain::set_pseudolocalization_expansion_ratio);
  467. ClassDB::bind_method(D_METHOD("get_pseudolocalization_prefix"), &TranslationDomain::get_pseudolocalization_prefix);
  468. ClassDB::bind_method(D_METHOD("set_pseudolocalization_prefix", "prefix"), &TranslationDomain::set_pseudolocalization_prefix);
  469. ClassDB::bind_method(D_METHOD("get_pseudolocalization_suffix"), &TranslationDomain::get_pseudolocalization_suffix);
  470. ClassDB::bind_method(D_METHOD("set_pseudolocalization_suffix", "suffix"), &TranslationDomain::set_pseudolocalization_suffix);
  471. ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationDomain::pseudolocalize);
  472. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "enabled"), "set_enabled", "is_enabled");
  473. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
  474. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_accents_enabled"), "set_pseudolocalization_accents_enabled", "is_pseudolocalization_accents_enabled");
  475. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_double_vowels_enabled"), "set_pseudolocalization_double_vowels_enabled", "is_pseudolocalization_double_vowels_enabled");
  476. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_fake_bidi_enabled"), "set_pseudolocalization_fake_bidi_enabled", "is_pseudolocalization_fake_bidi_enabled");
  477. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_override_enabled"), "set_pseudolocalization_override_enabled", "is_pseudolocalization_override_enabled");
  478. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_skip_placeholders_enabled"), "set_pseudolocalization_skip_placeholders_enabled", "is_pseudolocalization_skip_placeholders_enabled");
  479. ADD_PROPERTY(PropertyInfo(Variant::Type::FLOAT, "pseudolocalization_expansion_ratio"), "set_pseudolocalization_expansion_ratio", "get_pseudolocalization_expansion_ratio");
  480. ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_prefix"), "set_pseudolocalization_prefix", "get_pseudolocalization_prefix");
  481. ADD_PROPERTY(PropertyInfo(Variant::Type::STRING, "pseudolocalization_suffix"), "set_pseudolocalization_suffix", "get_pseudolocalization_suffix");
  482. }