Path: blob/master/tests/core/string/test_translation.cpp
45997 views
/**************************************************************************/1/* test_translation.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_translation)3334#include "core/io/resource_loader.h"35#include "core/string/optimized_translation.h"36#include "core/string/plural_rules.h"37#include "core/string/translation.h"38#include "core/string/translation_server.h"3940#ifdef TOOLS_ENABLED41#include "editor/import/resource_importer_csv_translation.h"42#endif4344#include "tests/test_utils.h"4546namespace TestTranslation {4748TEST_CASE("[Translation] Messages") {49Ref<Translation> translation;50translation.instantiate();51translation->set_locale("fr");52translation->add_message("Hello", "Bonjour");53CHECK(translation->get_message("Hello") == "Bonjour");5455translation->erase_message("Hello");56// The message no longer exists, so it returns an empty string instead.57CHECK(translation->get_message("Hello") == "");5859List<StringName> messages;60translation->get_message_list(&messages);61CHECK(translation->get_message_count() == 0);62CHECK(messages.size() == 0);6364translation->add_message("Hello2", "Bonjour2");65translation->add_message("Hello3", "Bonjour3");66messages.clear();67translation->get_message_list(&messages);68CHECK(translation->get_message_count() == 2);69CHECK(messages.size() == 2);70// Messages are stored in a Map, don't assume ordering.71CHECK(messages.find("Hello2"));72CHECK(messages.find("Hello3"));73}7475TEST_CASE("[Translation] Messages with context") {76Ref<Translation> translation;77translation.instantiate();78translation->set_locale("fr");79translation->add_message("Hello", "Bonjour");80translation->add_message("Hello", "Salut", "friendly");81CHECK(translation->get_message("Hello") == "Bonjour");82CHECK(translation->get_message("Hello", "friendly") == "Salut");83CHECK(translation->get_message("Hello", "nonexistent_context") == "");8485// Only remove the message for the default context, not the "friendly" context.86translation->erase_message("Hello");87// The message no longer exists, so it returns an empty string instead.88CHECK(translation->get_message("Hello") == "");89CHECK(translation->get_message("Hello", "friendly") == "Salut");90CHECK(translation->get_message("Hello", "nonexistent_context") == "");9192List<StringName> messages;93translation->get_message_list(&messages);9495CHECK(translation->get_message_count() == 1);96CHECK(messages.size() == 1);9798translation->add_message("Hello2", "Bonjour2");99translation->add_message("Hello2", "Salut2", "friendly");100translation->add_message("Hello3", "Bonjour3");101messages.clear();102translation->get_message_list(&messages);103104CHECK(translation->get_message_count() == 4);105CHECK(messages.size() == 4);106// Messages are stored in a Map, don't assume ordering.107CHECK(messages.find("Hello2"));108CHECK(messages.find("Hello3"));109// Context and untranslated string are separated by EOT.110CHECK(messages.find("friendly\x04Hello2"));111}112113TEST_CASE("[Translation] Plural messages") {114{115Ref<Translation> translation;116translation.instantiate();117translation->set_locale("fr");118CHECK(translation->get_nplurals() == 2);119}120121{122Ref<Translation> translation;123translation.instantiate();124translation->set_locale("invalid");125CHECK(translation->get_nplurals() == 2);126}127128{129Ref<Translation> translation;130translation.instantiate();131translation->set_plural_rules_override("Plural-Forms: nplurals=2; plural=(n >= 2);");132CHECK(translation->get_nplurals() == 2);133134PackedStringArray plurals;135plurals.push_back("Il y a %d pomme");136plurals.push_back("Il y a %d pommes");137translation->add_plural_message("There are %d apples", plurals);138ERR_PRINT_OFF;139// This is invalid, as the number passed to `get_plural_message()` may not be negative.140CHECK(vformat(translation->get_plural_message("There are %d apples", "", -1), -1) == "");141ERR_PRINT_ON;142CHECK(vformat(translation->get_plural_message("There are %d apples", "", 0), 0) == "Il y a 0 pomme");143CHECK(vformat(translation->get_plural_message("There are %d apples", "", 1), 1) == "Il y a 1 pomme");144CHECK(vformat(translation->get_plural_message("There are %d apples", "", 2), 2) == "Il y a 2 pommes");145}146}147148TEST_CASE("[Translation] Plural rules parsing") {149ERR_PRINT_OFF;150{151CHECK(PluralRules::parse("") == nullptr);152153CHECK(PluralRules::parse("plurals=(n != 1);") == nullptr);154CHECK(PluralRules::parse("nplurals; plurals=(n != 1);") == nullptr);155CHECK(PluralRules::parse("nplurals=; plurals=(n != 1);") == nullptr);156CHECK(PluralRules::parse("nplurals=0; plurals=(n != 1);") == nullptr);157CHECK(PluralRules::parse("nplurals=-1; plurals=(n != 1);") == nullptr);158159CHECK(PluralRules::parse("nplurals=2;") == nullptr);160CHECK(PluralRules::parse("nplurals=2; plurals;") == nullptr);161CHECK(PluralRules::parse("nplurals=2; plurals=;") == nullptr);162}163ERR_PRINT_ON;164165{166PluralRules *pr = PluralRules::parse("nplurals=3; plural=(n==0 ? 0 : n==1 ? 1 : 2);");167REQUIRE(pr != nullptr);168169CHECK(pr->get_nplurals() == 3);170CHECK(pr->get_plural() == "(n==0 ? 0 : n==1 ? 1 : 2)");171172CHECK(pr->evaluate(0) == 0);173CHECK(pr->evaluate(1) == 1);174CHECK(pr->evaluate(2) == 2);175CHECK(pr->evaluate(3) == 2);176177memdelete(pr);178}179180{181PluralRules *pr = PluralRules::parse("nplurals=1; plural=0;");182REQUIRE(pr != nullptr);183184CHECK(pr->get_nplurals() == 1);185CHECK(pr->get_plural() == "0");186187CHECK(pr->evaluate(0) == 0);188CHECK(pr->evaluate(1) == 0);189CHECK(pr->evaluate(2) == 0);190CHECK(pr->evaluate(3) == 0);191192memdelete(pr);193}194}195196#ifdef TOOLS_ENABLED197TEST_CASE("[OptimizedTranslation] Generate from Translation and read messages") {198Ref<Translation> translation = memnew(Translation);199translation->set_locale("fr");200translation->add_message("Hello", "Bonjour");201translation->add_message("Hello2", "Bonjour2");202translation->add_message("Hello3", "Bonjour3");203204Ref<OptimizedTranslation> optimized_translation = memnew(OptimizedTranslation);205optimized_translation->generate(translation);206CHECK(optimized_translation->get_message("Hello") == "Bonjour");207CHECK(optimized_translation->get_message("Hello2") == "Bonjour2");208CHECK(optimized_translation->get_message("Hello3") == "Bonjour3");209CHECK(optimized_translation->get_message("DoesNotExist") == "");210211List<StringName> messages;212// `get_message_list()` can't return the list of messages stored in an OptimizedTranslation.213optimized_translation->get_message_list(&messages);214CHECK(optimized_translation->get_message_count() == 0);215CHECK(messages.size() == 0);216}217218TEST_CASE("[TranslationCSV] CSV import") {219Ref<ResourceImporterCSVTranslation> import_csv_translation = memnew(ResourceImporterCSVTranslation);220221HashMap<StringName, Variant> options;222options["compress"] = false;223options["delimiter"] = 0;224225List<String> gen_files;226227Error result = import_csv_translation->import(0, TestUtils::get_data_path("translations.csv"),228"", options, nullptr, &gen_files);229CHECK(result == OK);230CHECK(gen_files.size() == 4);231232Ref<TranslationDomain> td = TranslationServer::get_singleton()->get_or_add_domain("godot.test");233for (const String &file : gen_files) {234Ref<Translation> translation = ResourceLoader::load(file);235CHECK(translation.is_valid());236td->add_translation(translation);237}238239td->set_locale_override("en");240241CHECK(td->translate("GOOD_MORNING", StringName()) == "Good Morning");242CHECK(td->translate("GOOD_EVENING", StringName()) == "Good Evening");243244td->set_locale_override("de");245246CHECK(td->translate("GOOD_MORNING", StringName()) == "Guten Morgen");247CHECK(td->translate("GOOD_EVENING", StringName()) == "Good Evening"); // Left blank in CSV, should source from 'en'.248249td->set_locale_override("ja");250251CHECK(td->translate("GOOD_MORNING", StringName()) == String::utf8("おはよう"));252CHECK(td->translate("GOOD_EVENING", StringName()) == String::utf8("こんばんは"));253254/* FIXME: This passes, but triggers a chain reaction that makes test_viewport255* and test_text_edit explode in a billion glittery Unicode particles.256td->set_locale_override("fa");257258CHECK(td->translate("GOOD_MORNING", String()) == String::utf8("صبح بخیر"));259CHECK(td->translate("GOOD_EVENING", String()) == String::utf8("عصر بخیر"));260*/261262TranslationServer::get_singleton()->remove_domain("godot.test");263}264265#endif // TOOLS_ENABLED266267} // namespace TestTranslation268269270