diff options
27 files changed, 475 insertions, 90 deletions
diff --git a/extension/src/LoadLocalisation.cpp b/extension/src/LoadLocalisation.cpp new file mode 100644 index 0000000..c95c08b --- /dev/null +++ b/extension/src/LoadLocalisation.cpp @@ -0,0 +1,111 @@ +#include "LoadLocalisation.hpp" + +#include <godot_cpp/variant/utility_functions.hpp> +#include <godot_cpp/classes/file_access.hpp> +#include <godot_cpp/classes/dir_access.hpp> +#include <godot_cpp/classes/translation_server.hpp> + +using namespace godot; +using namespace OpenVic2; + +LoadLocalisation *LoadLocalisation::singleton = nullptr; + +void LoadLocalisation::_bind_methods() { + ClassDB::bind_method(D_METHOD("load_file", "file_path", "locale"), &LoadLocalisation::load_file); + ClassDB::bind_method(D_METHOD("load_locale_dir", "dir_path", "locale"), &LoadLocalisation::load_locale_dir); + ClassDB::bind_method(D_METHOD("load_localisation_dir", "dir_path"), &LoadLocalisation::load_localisation_dir); +} + +LoadLocalisation *LoadLocalisation::get_singleton() { + return singleton; +} + +LoadLocalisation::LoadLocalisation() { + ERR_FAIL_COND(singleton != nullptr); + singleton = this; +} + +LoadLocalisation::~LoadLocalisation() { + ERR_FAIL_COND(singleton != this); + singleton = nullptr; +} + +Error LoadLocalisation::_load_file_into_translation(String const& file_path, Ref<Translation> translation) { + Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ); + Error err = FileAccess::get_open_error(); + if (err != OK || file.is_null()) { + UtilityFunctions::push_error("Failed to load localisation file: ", file_path); + return err == OK ? FAILED : err; + } + int line_number = 0; + while (!file->eof_reached()) { + PackedStringArray line = file->get_csv_line(); + line_number++; + if (line.size() < 2 || line[0].is_empty() || line[1].is_empty()) { + if (!line[0].is_empty()) + UtilityFunctions::push_warning("Key \"", line[0], "\" missing value on line ", line_number, " in file: ", file_path); + else if (line.size() >= 2 && !line[1].is_empty()) + UtilityFunctions::push_warning("Value \"", line[1], "\" missing key on line ", line_number, " in file: ", file_path); + continue; + } + translation->add_message(line[0], line[1].c_unescape()); + } + return OK; +} + +Ref<Translation> LoadLocalisation::_get_translation(String const& locale) { + TranslationServer *server = TranslationServer::get_singleton(); + Ref<Translation> translation = server->get_translation_object(locale); + if (translation.is_null() || translation->get_locale() != locale) { + translation.instantiate(); + translation->set_locale(locale); + server->add_translation(translation); + } + return translation; +} + +Error LoadLocalisation::load_file(String const& file_path, String const& locale) { + return _load_file_into_translation(file_path, _get_translation(locale)); +} + +/* REQUIREMENTS + * FS-18, FS-24, FS-25 + */ +Error LoadLocalisation::load_locale_dir(String const& dir_path, String const& locale) { + Ref<Translation> translation = _get_translation(locale); + if (DirAccess::dir_exists_absolute(dir_path)) { + Error err = OK; + for (String const& file_name : DirAccess::get_files_at(dir_path)) { + if (file_name.get_extension().to_lower() == "csv") { + String file_path = dir_path.path_join(file_name); + if (_load_file_into_translation(file_path, translation) != OK) + err = FAILED; + } + } + return err; + } + UtilityFunctions::push_error("Locale directory does not exist: ", dir_path); + return FAILED; +} + +/* REQUIREMENTS + * FS-23 + */ +Error LoadLocalisation::load_localisation_dir(String const& dir_path) { + if (DirAccess::dir_exists_absolute(dir_path)) { + TranslationServer *server = TranslationServer::get_singleton(); + Error err = OK; + for (String const& locale_name : DirAccess::get_directories_at(dir_path)) { + if (locale_name == server->standardize_locale(locale_name)) { + if (load_locale_dir(dir_path.path_join(locale_name), locale_name) != OK) + err = FAILED; + } else { + err = FAILED; + UtilityFunctions::push_error("Invalid locale directory name: ", locale_name); + } + } + return err; + } + UtilityFunctions::push_error("Localisation directory does not exist: ", dir_path); + return FAILED; +} diff --git a/extension/src/LoadLocalisation.hpp b/extension/src/LoadLocalisation.hpp new file mode 100644 index 0000000..90f3158 --- /dev/null +++ b/extension/src/LoadLocalisation.hpp @@ -0,0 +1,29 @@ +#pragma once + +#include <godot_cpp/core/class_db.hpp> +#include <godot_cpp/classes/translation.hpp> + +namespace OpenVic2 { + class LoadLocalisation : public godot::Object + { + GDCLASS(LoadLocalisation, godot::Object) + + static LoadLocalisation *singleton; + + godot::Error _load_file_into_translation(godot::String const& file_path, godot::Ref<godot::Translation> translation); + godot::Ref<godot::Translation> _get_translation(godot::String const& locale); + + protected: + static void _bind_methods(); + + public: + static LoadLocalisation *get_singleton(); + + LoadLocalisation(); + ~LoadLocalisation(); + + godot::Error load_file(godot::String const& file_path, godot::String const& locale); + godot::Error load_locale_dir(godot::String const& dir_path, godot::String const& locale); + godot::Error load_localisation_dir(godot::String const& dir_path); + }; +} diff --git a/extension/src/register_types.cpp b/extension/src/register_types.cpp index aac38b9..d1613a5 100644 --- a/extension/src/register_types.cpp +++ b/extension/src/register_types.cpp @@ -8,6 +8,7 @@ #include "TestSingleton.hpp" #include "Simulation.hpp" #include "Checksum.hpp" +#include "LoadLocalisation.hpp" using namespace godot; using namespace OpenVic2; @@ -15,6 +16,7 @@ using namespace OpenVic2; static TestSingleton* _test_singleton; static Simulation* _simulation; static Checksum* _checksum; +static LoadLocalisation* _load_localisation; void initialize_openvic2_types(ModuleInitializationLevel p_level) { @@ -34,6 +36,10 @@ void initialize_openvic2_types(ModuleInitializationLevel p_level) _checksum = memnew(Checksum); Engine::get_singleton()->register_singleton("Checksum", Checksum::get_singleton()); + ClassDB::register_class<LoadLocalisation>(); + _load_localisation = memnew(LoadLocalisation); + Engine::get_singleton()->register_singleton("LoadLocalisation", LoadLocalisation::get_singleton()); + } void uninitialize_openvic2_types(ModuleInitializationLevel p_level) { @@ -49,6 +55,9 @@ void uninitialize_openvic2_types(ModuleInitializationLevel p_level) { Engine::get_singleton()->unregister_singleton("Checksum"); memdelete(_checksum); + + Engine::get_singleton()->unregister_singleton("LoadLocalisation"); + memdelete(_load_localisation); } extern "C" diff --git a/game/default_bus_layout.tres b/game/default_bus_layout.tres index 87413af..d6f0c67 100644 --- a/game/default_bus_layout.tres +++ b/game/default_bus_layout.tres @@ -1,13 +1,13 @@ [gd_resource type="AudioBusLayout" format=3 uid="uid://cquqx51trot64"] [resource] -bus/1/name = &"Music" +bus/1/name = &"MUSIC_BUS" bus/1/solo = false bus/1/mute = false bus/1/bypass_fx = false bus/1/volume_db = 0.0 bus/1/send = &"Master" -bus/2/name = &"SFX" +bus/2/name = &"SFX_BUS" bus/2/solo = false bus/2/mute = false bus/2/bypass_fx = false diff --git a/game/localisation/README.md b/game/localisation/README.md new file mode 100644 index 0000000..401517f --- /dev/null +++ b/game/localisation/README.md @@ -0,0 +1,16 @@ +# Localisation + +This folder contains localisations for in-game text. Each sub-folder must be named with a standard locale code, e.g. `en_GB`, to which the localisations contained within it shall apply (Godot's supported locale codes are listed [here](https://docs.godotengine.org/en/latest/tutorials/i18n/locales.html)). These folders contain `.csv` files where each line is interpreted as a key-value pair. Empty lines allow for spacing out/separating sections, and any further entries beyond the first two on a line are ignored, which can be used to add comments at the end of lines. Lines with their key and value empty are skipped, allowing free-standing comments with no preceeding localisation, but lines with only one of their key or value empty, while not loaded as a localisation, will still result in warnings about incomplete entries. + +``` +,, Example Localisation Comment +EXAMPLE_KEY,Example Value +ANOTHER_EXAMPLE,Another Example, This is a comment + +,, Entries with empty keys/values are skipped but can still produce warnings +THIS,, produces a warning! +,As, does this! +,, This doesn't! +BUT_THIS_DOES +AND_THIS, +``` diff --git a/game/localisation/en_GB/menus.csv b/game/localisation/en_GB/menus.csv new file mode 100644 index 0000000..27a0858 --- /dev/null +++ b/game/localisation/en_GB/menus.csv @@ -0,0 +1,52 @@ +,, Main Menu +MAINMENU_TITLE,OpenVic2 +MAINMENU_NEW_GAME,New Game +MAINMENU_CONTINUE,Continue +MAINMENU_MULTIPLAYER,Multiplayer +MAINMENU_OPTIONS,Options +MAINMENU_CREDITS,Credits +MAINMENU_EXIT,Exit +MAINMENU_CHECKSUM,Checksum %s + +,, Options +OPTIONS_RESET,R +OPTIONS_BACK,X + +,, General Tab +OPTIONS_GENERAL_SAVEFORMAT,Savegame Format +OPTIONS_GENERAL_BINARY,Binary +OPTIONS_GENERAL_TEXT,Text +OPTIONS_GENERAL_AUTOSAVE,Autosave Interval +OPTIONS_GENERAL_AUTOSAVE_MONTHLY,Monthly +OPTIONS_GENERAL_AUTOSAVE_BIMONTHLY,Bi-Monthly +OPTIONS_GENERAL_AUTOSAVE_YEARLY,Yearly +OPTIONS_GENERAL_AUTOSAVE_BIYEARLY,Bi-Yearly +OPTIONS_GENERAL_AUTOSAVE_NEVER,Never +OPTIONS_GENERAL_LANGUAGE,Language + +,, Video Tab +OPTIONS_VIDEO_RESOLUTION,Resolution +OPTIONS_VIDEO_SCREEN_MODE,Screen Mode +OPTIONS_VIDEO_FULLSCREEN,Fullscreen +OPTIONS_VIDEO_BORDERLESS,Borderless +OPTIONS_VIDEO_WINDOWED,Windowed +OPTIONS_VIDEO_MONITOR_SELECTION,Monitor Selection +OPTIONS_VIDEO_REFRESH_RATE,Refresh Rate +OPTIONS_VIDEO_REFRESH_RATE_TOOLTIP,Only change from VSYNC if you are having issues with screen tearing. +OPTIONS_VIDEO_QUALITY,Quality Preset + +,, Sound Tab +MASTER_BUS,Master Volume +MUSIC_BUS,Music Volume +SFX_BUS,SFX Volume + +,, Credits Menu +CREDITS_BACK,Back to Main Menu + +,, Game Lobby +GAMELOBBY_START,Start Game +GAMELOBBY_BACK,Back + +,, Game Session Menu +GAMESESSIONMENU_RESIGN,Resign +GAMESESSIONMENU_CLOSE,Close diff --git a/game/localisation/en_GB/menus.csv.import b/game/localisation/en_GB/menus.csv.import new file mode 100644 index 0000000..8dd0c09 --- /dev/null +++ b/game/localisation/en_GB/menus.csv.import @@ -0,0 +1,3 @@ +[remap] + +importer="keep" diff --git a/game/localisation/en_US/menus.csv b/game/localisation/en_US/menus.csv new file mode 100644 index 0000000..27a0858 --- /dev/null +++ b/game/localisation/en_US/menus.csv @@ -0,0 +1,52 @@ +,, Main Menu +MAINMENU_TITLE,OpenVic2 +MAINMENU_NEW_GAME,New Game +MAINMENU_CONTINUE,Continue +MAINMENU_MULTIPLAYER,Multiplayer +MAINMENU_OPTIONS,Options +MAINMENU_CREDITS,Credits +MAINMENU_EXIT,Exit +MAINMENU_CHECKSUM,Checksum %s + +,, Options +OPTIONS_RESET,R +OPTIONS_BACK,X + +,, General Tab +OPTIONS_GENERAL_SAVEFORMAT,Savegame Format +OPTIONS_GENERAL_BINARY,Binary +OPTIONS_GENERAL_TEXT,Text +OPTIONS_GENERAL_AUTOSAVE,Autosave Interval +OPTIONS_GENERAL_AUTOSAVE_MONTHLY,Monthly +OPTIONS_GENERAL_AUTOSAVE_BIMONTHLY,Bi-Monthly +OPTIONS_GENERAL_AUTOSAVE_YEARLY,Yearly +OPTIONS_GENERAL_AUTOSAVE_BIYEARLY,Bi-Yearly +OPTIONS_GENERAL_AUTOSAVE_NEVER,Never +OPTIONS_GENERAL_LANGUAGE,Language + +,, Video Tab +OPTIONS_VIDEO_RESOLUTION,Resolution +OPTIONS_VIDEO_SCREEN_MODE,Screen Mode +OPTIONS_VIDEO_FULLSCREEN,Fullscreen +OPTIONS_VIDEO_BORDERLESS,Borderless +OPTIONS_VIDEO_WINDOWED,Windowed +OPTIONS_VIDEO_MONITOR_SELECTION,Monitor Selection +OPTIONS_VIDEO_REFRESH_RATE,Refresh Rate +OPTIONS_VIDEO_REFRESH_RATE_TOOLTIP,Only change from VSYNC if you are having issues with screen tearing. +OPTIONS_VIDEO_QUALITY,Quality Preset + +,, Sound Tab +MASTER_BUS,Master Volume +MUSIC_BUS,Music Volume +SFX_BUS,SFX Volume + +,, Credits Menu +CREDITS_BACK,Back to Main Menu + +,, Game Lobby +GAMELOBBY_START,Start Game +GAMELOBBY_BACK,Back + +,, Game Session Menu +GAMESESSIONMENU_RESIGN,Resign +GAMESESSIONMENU_CLOSE,Close diff --git a/game/localisation/en_US/menus.csv.import b/game/localisation/en_US/menus.csv.import new file mode 100644 index 0000000..8dd0c09 --- /dev/null +++ b/game/localisation/en_US/menus.csv.import @@ -0,0 +1,3 @@ +[remap] + +importer="keep" diff --git a/game/localisation/fr_FR/menus.csv b/game/localisation/fr_FR/menus.csv new file mode 100644 index 0000000..8e8a7ad --- /dev/null +++ b/game/localisation/fr_FR/menus.csv @@ -0,0 +1,52 @@ +,, Main Menu +MAINMENU_TITLE,OpenVic2 +MAINMENU_NEW_GAME,Nouveau Jeu +MAINMENU_CONTINUE,Continuer +MAINMENU_MULTIPLAYER,Multijouer +MAINMENU_OPTIONS,Options +MAINMENU_CREDITS,Credits +MAINMENU_EXIT,Quitter +MAINMENU_CHECKSUM,Somme de contrôle %s + +,, Options +OPTIONS_RESET,R +OPTIONS_BACK,X + +,, General Tab +OPTIONS_GENERAL_SAVEFORMAT,Format de Sauvegarde +OPTIONS_GENERAL_BINARY,Binaire +OPTIONS_GENERAL_TEXT,Texte +OPTIONS_GENERAL_AUTOSAVE,Intervalle d'Enregistrement Automatique +OPTIONS_GENERAL_AUTOSAVE_MONTHLY,Mensuel +OPTIONS_GENERAL_AUTOSAVE_BIMONTHLY,Bimensuel +OPTIONS_GENERAL_AUTOSAVE_YEARLY,Annuel +OPTIONS_GENERAL_AUTOSAVE_BIYEARLY,Bisannuel +OPTIONS_GENERAL_AUTOSAVE_NEVER,Jamais +OPTIONS_GENERAL_LANGUAGE,Langue + +,, Video Tab +OPTIONS_VIDEO_RESOLUTION,Résolution +OPTIONS_VIDEO_SCREEN_MODE,Mode Écran +OPTIONS_VIDEO_FULLSCREEN,Plein Écran +OPTIONS_VIDEO_BORDERLESS,Sans Bordure +OPTIONS_VIDEO_WINDOWED,Fenêtré +OPTIONS_VIDEO_MONITOR_SELECTION,Sélection du Moniteur +OPTIONS_VIDEO_REFRESH_RATE,Taux de Rafraîchissement +OPTIONS_VIDEO_REFRESH_RATE_TOOLTIP,Ne changez de VSYNC que si vous rencontrez des problèmes de déchirement d'écran. +OPTIONS_VIDEO_QUALITY,Préréglage de la Qualité + +,, Sound Tab +MASTER_BUS,Volume Principal +MUSIC_BUS,Volume de la Musique +SFX_BUS,Volume d'Effets Spéciaux + +,, Credits Menu +CREDITS_BACK,Retour au Menu Principal + +,, Game Lobby +GAMELOBBY_START,Démarrer Jeu +GAMELOBBY_BACK,Retourner + +,, Game Session Menu +GAMESESSIONMENU_RESIGN,Démissionner +GAMESESSIONMENU_CLOSE,Fermer diff --git a/game/localisation/fr_FR/menus.csv.import b/game/localisation/fr_FR/menus.csv.import new file mode 100644 index 0000000..8dd0c09 --- /dev/null +++ b/game/localisation/fr_FR/menus.csv.import @@ -0,0 +1,3 @@ +[remap] + +importer="keep" diff --git a/game/project.godot b/game/project.godot index b2a3098..363bbc1 100644 --- a/game/project.godot +++ b/game/project.godot @@ -50,8 +50,10 @@ locale/translation_remaps={} locale/fallback="en_GB" locale/locale_filter_mode=0 locale/country_short_name={ +"United Kingdom": "UK", "United States of America": "USA" } +locale/localisation_path="res://localisation" [memory] diff --git a/game/src/Autoload/Events.gd b/game/src/Autoload/Events.gd index f0f60b7..25a185f 100644 --- a/game/src/Autoload/Events.gd +++ b/game/src/Autoload/Events.gd @@ -1,3 +1,4 @@ extends Node var Options = preload("Events/Options.gd").new() +var Localisation = preload("Events/Localisation.gd").new() diff --git a/game/src/Autoload/Events/Localisation.gd b/game/src/Autoload/Events/Localisation.gd new file mode 100644 index 0000000..eda7e51 --- /dev/null +++ b/game/src/Autoload/Events/Localisation.gd @@ -0,0 +1,30 @@ +extends RefCounted + +# REQUIREMENTS +# * SS-59, SS-60, SS-61 +func get_default_locale() -> String: + var locales := TranslationServer.get_loaded_locales() + var default_locale := OS.get_locale() + if default_locale in locales: + return default_locale + var default_language := OS.get_locale_language() + for locale in locales: + if locale.begins_with(default_language): + return default_language + return ProjectSettings.get_setting("internationalization/locale/fallback", "en_GB") + +func load_localisation(dir_path : String) -> void: + if LoadLocalisation.load_localisation_dir(dir_path) == OK: + print("loaded locales: ", TranslationServer.get_loaded_locales()) + else: + push_error("Failed to load localisation directory: ", dir_path) + +# REQUIREMENTS +# * SS-57 +# * FS-17 +func _init(): + var localisation_dir_path : String = ProjectSettings.get_setting("internationalization/locale/localisation_path", "") + if localisation_dir_path.is_empty(): + push_error("Missing localisation_path setting!") + else: + load_localisation(localisation_dir_path) diff --git a/game/src/CreditsMenu/CreditsMenu.tscn b/game/src/CreditsMenu/CreditsMenu.tscn index d2819d7..6c5f36b 100644 --- a/game/src/CreditsMenu/CreditsMenu.tscn +++ b/game/src/CreditsMenu/CreditsMenu.tscn @@ -29,7 +29,7 @@ theme_type_variation = &"BackButtonsMargin" [node name="BackButton" type="Button" parent="ControlMargin"] editor_description = "UI-38" layout_mode = 2 -text = "Back to Main Menu" +text = "CREDITS_BACK" [node name="Scroll" type="ScrollContainer" parent="."] editor_description = "UI-35" diff --git a/game/src/GameSession/GameSessionMenu.tscn b/game/src/GameSession/GameSessionMenu.tscn index d6a7ca9..45fff4b 100644 --- a/game/src/GameSession/GameSessionMenu.tscn +++ b/game/src/GameSession/GameSessionMenu.tscn @@ -14,7 +14,7 @@ layout_mode = 2 [node name="MainMenuButton" type="Button" parent="VBoxContainer"] editor_description = "UI-71" layout_mode = 2 -text = "Resign" +text = "GAMESESSIONMENU_RESIGN" [node name="HSeparator" type="HSeparator" parent="VBoxContainer"] layout_mode = 2 @@ -22,7 +22,7 @@ layout_mode = 2 [node name="CloseButton" type="Button" parent="VBoxContainer"] editor_description = "UI-80" layout_mode = 2 -text = "Close" +text = "GAMESESSIONMENU_CLOSE" [connection signal="pressed" from="VBoxContainer/MainMenuButton" to="." method="_on_to_main_menu_pressed"] [connection signal="pressed" from="VBoxContainer/CloseButton" to="." method="_on_close_button_pressed"] diff --git a/game/src/LobbyMenu/LobbyMenu.tscn b/game/src/LobbyMenu/LobbyMenu.tscn index fcf2263..528e7ae 100644 --- a/game/src/LobbyMenu/LobbyMenu.tscn +++ b/game/src/LobbyMenu/LobbyMenu.tscn @@ -34,7 +34,7 @@ size_flags_vertical = 3 [node name="BackButton" type="Button" parent="GameSelectPanel/VBoxContainer"] editor_description = "UI-37" layout_mode = 2 -text = "Back" +text = "GAMELOBBY_BACK" [node name="Spacer2" type="Control" parent="GameSelectPanel/VBoxContainer"] custom_minimum_size = Vector2(0, 33) @@ -71,7 +71,7 @@ size_flags_vertical = 3 editor_description = "UI-43" layout_mode = 2 disabled = true -text = "Start Game" +text = "GAMELOBBY_START" [node name="Spacer3" type="Control" parent="GameStartPanel/VBoxContainer"] custom_minimum_size = Vector2(0, 33) diff --git a/game/src/LocaleButton.gd b/game/src/LocaleButton.gd index c3c6925..2b717a4 100644 --- a/game/src/LocaleButton.gd +++ b/game/src/LocaleButton.gd @@ -1,40 +1,49 @@ extends OptionButton -const section_name : String = "Localization" -const setting_name : String = "Locale" +const section_name : String = "localisation" +const setting_name : String = "locale" -var _locales_country_rename : Dictionary -var _locales_list : Array[String] +var _default_locale_index : int func _ready(): - _locales_country_rename = ProjectSettings.get_setting("internationalization/locale/country_short_name", {}) + var locales_country_rename : Dictionary = ProjectSettings.get_setting("internationalization/locale/country_short_name", {}) - _locales_list = [TranslationServer.get_locale()] - _locales_list.append_array(TranslationServer.get_loaded_locales()) + var locales_list = TranslationServer.get_loaded_locales() + var default_locale := Events.Localisation.get_default_locale() + if default_locale not in locales_list: + locales_list.push_back(default_locale) - for locale in _locales_list: + for locale in locales_list: + # locale_name consists of a compulsory language name and optional script + # and country names, in the format: "<language>[ (script)][, country]" var locale_name := TranslationServer.get_locale_name(locale) - var locale_first_part := locale_name.get_slice(", ", 0) - var locale_second_part := locale_name.substr(locale_first_part.length() + 2) - if locale_second_part in _locales_country_rename: - locale_second_part = _locales_country_rename[locale_second_part] + var comma_idx := locale_name.find(", ") + if comma_idx != -1: + var locale_country_name := locale_name.substr(comma_idx + 2) + locale_country_name = locales_country_rename.get(locale_country_name, "") + if not locale_country_name.is_empty(): + locale_name = locale_name.left(comma_idx + 2) + locale_country_name - add_item("%s, %s" % [locale_first_part, locale_second_part]) + add_item(locale_name) + set_item_metadata(item_count - 1, locale) + + if locale == default_locale: + _default_locale_index = item_count - 1 Events.Options.load_settings.connect(load_setting) Events.Options.save_settings.connect(save_setting) - -func _notification(what): + +func _notification(what : int): match what: NOTIFICATION_TRANSLATION_CHANGED: _select_locale_by_string(TranslationServer.get_locale()) func _valid_index(index : int) -> bool: - return 0 <= index and index < _locales_list.size() + return 0 <= index and index < item_count func load_setting(file : ConfigFile) -> void: if file == null: return - var load_value = file.get_value(section_name, setting_name, TranslationServer.get_locale()) + var load_value = file.get_value(section_name, setting_name, Events.Localisation.get_default_locale()) match typeof(load_value): TYPE_STRING, TYPE_STRING_NAME: if _select_locale_by_string(load_value as String): @@ -42,28 +51,29 @@ func load_setting(file : ConfigFile) -> void: return push_error("Setting value '%s' invalid for setting [%s] %s" % [load_value, section_name, setting_name]) reset_setting() - + func _select_locale_by_string(locale : String) -> bool: - var locale_index := _locales_list.find(locale) - if locale_index != -1: - selected = locale_index - return true + for idx in item_count: + if get_item_metadata(idx) == locale: + selected = idx + return true + selected = _default_locale_index return false # REQUIREMENTS: # * UIFUN-74 func save_setting(file : ConfigFile) -> void: if file == null: return - file.set_value(section_name, setting_name, _locales_list[selected]) + file.set_value(section_name, setting_name, get_item_metadata(selected)) func reset_setting() -> void: - selected = _locales_list.find(TranslationServer.get_locale()) + _select_locale_by_string(TranslationServer.get_locale()) # REQUIREMENTS: # * SS-58 func _on_item_selected(index : int) -> void: if _valid_index(index): - TranslationServer.set_locale(_locales_list[index]) + TranslationServer.set_locale(get_item_metadata(index)) Events.Options.save_settings_to_file.call_deferred() else: push_error("Invalid LocaleButton index: %d" % index) diff --git a/game/src/MainMenu/MainMenu.tscn b/game/src/MainMenu/MainMenu.tscn index 10bf526..5fb6ca9 100644 --- a/game/src/MainMenu/MainMenu.tscn +++ b/game/src/MainMenu/MainMenu.tscn @@ -34,7 +34,7 @@ layout_mode = 2 size_flags_vertical = 6 size_flags_stretch_ratio = 1.5 theme_type_variation = &"TitleLabel" -text = "OpenVic2" +text = "MAINMENU_TITLE" horizontal_alignment = 1 vertical_alignment = 1 @@ -60,7 +60,7 @@ focus_neighbor_right = NodePath("../ContinueButton") focus_next = NodePath("../ContinueButton") focus_previous = NodePath("../ExitButton") theme_type_variation = &"TitleButton" -text = "New Game" +text = "MAINMENU_NEW_GAME" clip_text = true [node name="ContinueButton" type="Button" parent="Panel/VBox/Margin/ButtonList"] @@ -72,7 +72,7 @@ focus_next = NodePath("../MultiplayerButton") focus_previous = NodePath("../NewGameButton") theme_type_variation = &"TitleButton" disabled = true -text = "Continue" +text = "MAINMENU_CONTINUE" clip_text = true [node name="MultiplayerButton" type="Button" parent="Panel/VBox/Margin/ButtonList"] @@ -84,7 +84,7 @@ focus_neighbor_right = NodePath("../OptionsButton") focus_next = NodePath("../OptionsButton") focus_previous = NodePath("../ContinueButton") theme_type_variation = &"TitleButton" -text = "Multiplayer" +text = "MAINMENU_MULTIPLAYER" clip_text = true [node name="OptionsButton" type="Button" parent="Panel/VBox/Margin/ButtonList"] @@ -96,7 +96,7 @@ focus_neighbor_right = NodePath("../CreditsButton") focus_next = NodePath("../CreditsButton") focus_previous = NodePath("../MultiplayerButton") theme_type_variation = &"TitleButton" -text = "Options" +text = "MAINMENU_OPTIONS" clip_text = true [node name="CreditsButton" type="Button" parent="Panel/VBox/Margin/ButtonList"] @@ -108,7 +108,7 @@ focus_neighbor_right = NodePath("../ExitButton") focus_next = NodePath("../ExitButton") focus_previous = NodePath("../OptionsButton") theme_type_variation = &"TitleButton" -text = "Credits" +text = "MAINMENU_CREDITS" clip_text = true [node name="ExitButton" type="Button" parent="Panel/VBox/Margin/ButtonList"] @@ -120,7 +120,7 @@ focus_neighbor_right = NodePath("../NewGameButton") focus_next = NodePath("../NewGameButton") focus_previous = NodePath("../OptionsButton") theme_type_variation = &"TitleButton" -text = "Exit" +text = "MAINMENU_EXIT" clip_text = true [node name="BottomSpace" type="Control" parent="Panel/VBox"] diff --git a/game/src/MainMenu/ReleaseInfoBox.gd b/game/src/MainMenu/ReleaseInfoBox.gd index 48686f3..ca03af3 100644 --- a/game/src/MainMenu/ReleaseInfoBox.gd +++ b/game/src/MainMenu/ReleaseInfoBox.gd @@ -9,6 +9,8 @@ var _commit_label : Button @export var _checksum_label : Button +var _checksum : String = "????" + # REQUIREMENTS: # * UIFUN-97 func _ready(): @@ -17,9 +19,17 @@ func _ready(): _commit_label.text = _GIT_INFO_.short_hash _commit_label.tooltip_text = _GIT_INFO_.commit_hash # UI-111 - _checksum_label.tooltip_text = "Checksum " + Checksum.get_checksum_text() - _checksum_label.text = "(" + Checksum.get_checksum_text().substr(0, 4) + ")" + _checksum = Checksum.get_checksum_text() + _update_checksum_label_text() + +func _notification(what : int): + match what: + NOTIFICATION_TRANSLATION_CHANGED: + _update_checksum_label_text() +func _update_checksum_label_text() -> void: + _checksum_label.tooltip_text = tr("MAINMENU_CHECKSUM") % _checksum + _checksum_label.text = "(%s)" % _checksum.substr(0, 4) func _on_version_label_pressed(): DisplayServer.clipboard_set(_GIT_INFO_.tag) @@ -28,4 +38,4 @@ func _on_commit_label_pressed(): DisplayServer.clipboard_set(_GIT_INFO_.commit_hash) func _on_checksum_label_pressed(): - DisplayServer.clipboard_set(Checksum.get_checksum_text()) + DisplayServer.clipboard_set(_checksum) diff --git a/game/src/MainMenu/ReleaseInfoBox.tscn b/game/src/MainMenu/ReleaseInfoBox.tscn index d15ae31..821982b 100644 --- a/game/src/MainMenu/ReleaseInfoBox.tscn +++ b/game/src/MainMenu/ReleaseInfoBox.tscn @@ -11,25 +11,25 @@ _checksum_label = NodePath("ChecksumLabel") [node name="VersionLabel" type="Button" parent="."] layout_mode = 2 -tooltip_text = "OpenVic2 v0.01 \"Primum Mobile\"" +tooltip_text = "VERSION_MISSING" theme_type_variation = &"VersionLabel" -text = "v0.01" +text = "VERSION_MISSING" flat = true alignment = 0 [node name="CommitLabel" type="Button" parent="."] layout_mode = 2 theme_type_variation = &"CommitLabel" -text = "ffffffff" +text = "????????" flat = true alignment = 0 [node name="ChecksumLabel" type="Button" parent="."] editor_description = "UI-111" layout_mode = 2 -tooltip_text = "Checksum 00000000" +tooltip_text = "CHECKSUM_MISSING" theme_type_variation = &"ChecksumLabel" -text = "(0000)" +text = "(????)" flat = true alignment = 0 diff --git a/game/src/OptionMenu/GeneralTab.tscn b/game/src/OptionMenu/GeneralTab.tscn index b38f548..a9223af 100644 --- a/game/src/OptionMenu/GeneralTab.tscn +++ b/game/src/OptionMenu/GeneralTab.tscn @@ -26,7 +26,7 @@ columns = 2 [node name="SavegameFormatLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Savegame Format" +text = "OPTIONS_GENERAL_SAVEFORMAT" [node name="SavegameFormatSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] editor_description = "UI-50" @@ -34,18 +34,18 @@ layout_mode = 2 focus_neighbor_bottom = NodePath("../AutosaveIntervalSelector") item_count = 2 selected = 0 -popup/item_0/text = "Binary" +popup/item_0/text = "OPTIONS_GENERAL_BINARY" popup/item_0/id = 0 -popup/item_1/text = "Text" +popup/item_1/text = "OPTIONS_GENERAL_TEXT" popup/item_1/id = 1 script = ExtResource("2_msx2u") -section_name = "General" -setting_name = "Savegame Format" +section_name = "general" +setting_name = "savegame_format" default_selected = 0 [node name="AutosaveIntervalLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Autosave Interval" +text = "OPTIONS_GENERAL_AUTOSAVE" horizontal_alignment = 1 [node name="AutosaveIntervalSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] @@ -55,24 +55,24 @@ focus_neighbor_top = NodePath("../SavegameFormatSelector") focus_neighbor_bottom = NodePath("../LocaleButton") item_count = 5 selected = 0 -popup/item_0/text = "Monthly" +popup/item_0/text = "OPTIONS_GENERAL_AUTOSAVE_MONTHLY" popup/item_0/id = 0 -popup/item_1/text = "Bi-Monthly" +popup/item_1/text = "OPTIONS_GENERAL_AUTOSAVE_BIMONTHLY" popup/item_1/id = 1 -popup/item_2/text = "Bi-Yearly" +popup/item_2/text = "OPTIONS_GENERAL_AUTOSAVE_YEARLY" popup/item_2/id = 2 -popup/item_3/text = "Yearly" +popup/item_3/text = "OPTIONS_GENERAL_AUTOSAVE_BIYEARLY" popup/item_3/id = 3 -popup/item_4/text = "Never" +popup/item_4/text = "OPTIONS_GENERAL_AUTOSAVE_NEVER" popup/item_4/id = 4 script = ExtResource("2_t06tb") -section_name = "General" -setting_name = "Autosave Interval" +section_name = "general" +setting_name = "autosave_interval" default_selected = 0 [node name="LocaleLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Language" +text = "OPTIONS_GENERAL_LANGUAGE" [node name="LocaleButton" parent="VBoxContainer/GridContainer" instance=ExtResource("2_5cfd7")] editor_description = "UI-79" diff --git a/game/src/OptionMenu/OptionsMenu.gd b/game/src/OptionMenu/OptionsMenu.gd index d5f128c..5f6a088 100644 --- a/game/src/OptionMenu/OptionsMenu.gd +++ b/game/src/OptionMenu/OptionsMenu.gd @@ -20,7 +20,7 @@ func _ready(): # * UI-12 # * UIFUN-14 var reset_button := Button.new() - reset_button.text = "R" + reset_button.text = "OPTIONS_RESET" reset_button.pressed.connect(Events.Options.try_reset_settings) button_list.add_child(reset_button) @@ -28,7 +28,7 @@ func _ready(): # * UI-11 # * UIFUN-17 var back_button := Button.new() - back_button.text = "X" + back_button.text = "OPTIONS_BACK" back_button.pressed.connect(_on_back_button_pressed) button_list.add_child(back_button) get_viewport().get_window().close_requested.connect(_on_window_close_requested) diff --git a/game/src/OptionMenu/SettingNodes/SettingHSlider.gd b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd index 6a0e5ed..4e7c9c9 100644 --- a/game/src/OptionMenu/SettingNodes/SettingHSlider.gd +++ b/game/src/OptionMenu/SettingNodes/SettingHSlider.gd @@ -2,10 +2,10 @@ extends HSlider class_name SettingHSlider @export -var section_name : String = "Setting" +var section_name : String = "setting" @export -var setting_name : String = "SettingHSlider" +var setting_name : String = "setting_hslider" @export var default_value : float = 0 diff --git a/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd index c7b760d..e0b8e4c 100644 --- a/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd +++ b/game/src/OptionMenu/SettingNodes/SettingOptionButton.gd @@ -2,10 +2,10 @@ extends OptionButton class_name SettingOptionButton @export -var section_name : String = "Setting" +var section_name : String = "setting" @export -var setting_name : String = "SettingOptionMenu" +var setting_name : String = "setting_optionbutton" @export var default_selected : int = -1: diff --git a/game/src/OptionMenu/VideoTab.tscn b/game/src/OptionMenu/VideoTab.tscn index c060b19..c92f7f7 100644 --- a/game/src/OptionMenu/VideoTab.tscn +++ b/game/src/OptionMenu/VideoTab.tscn @@ -9,7 +9,6 @@ [node name="Video" type="HBoxContainer" node_paths=PackedStringArray("initial_focus")] editor_description = "UI-46" -tooltip_text = "This is my cool and very nice tooltip" alignment = 1 script = ExtResource("1_jvv62") initial_focus = NodePath("VBoxContainer/GridContainer/ResolutionSelector") @@ -29,7 +28,7 @@ columns = 2 [node name="ResolutionLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Resolution" +text = "OPTIONS_VIDEO_RESOLUTION" [node name="ResolutionSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] editor_description = "UI-19" @@ -40,13 +39,13 @@ selected = 0 popup/item_0/text = "MISSING" popup/item_0/id = 0 script = ExtResource("1_i8nro") -section_name = "Video" -setting_name = "Resolution" +section_name = "video" +setting_name = "resolution" [node name="ScreenModeLabel" type="Label" parent="VBoxContainer/GridContainer"] editor_description = "UI-44" layout_mode = 2 -text = "Screen Mode" +text = "OPTIONS_VIDEO_SCREEN_MODE" [node name="ScreenModeSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] layout_mode = 2 @@ -54,19 +53,19 @@ focus_neighbor_top = NodePath("../ResolutionSelector") focus_neighbor_bottom = NodePath("../MonitorDisplaySelector") item_count = 3 selected = 0 -popup/item_0/text = "Fullscreen" +popup/item_0/text = "OPTIONS_VIDEO_FULLSCREEN" popup/item_0/id = 0 -popup/item_1/text = "Borderless" +popup/item_1/text = "OPTIONS_VIDEO_BORDERLESS" popup/item_1/id = 1 -popup/item_2/text = "Windowed" +popup/item_2/text = "OPTIONS_VIDEO_WINDOWED" popup/item_2/id = 2 script = ExtResource("2_wa7vw") -section_name = "Video" -setting_name = "Mode Selected" +section_name = "video" +setting_name = "mode_selected" [node name="MonitorSelectionLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Monitor Selection" +text = "OPTIONS_VIDEO_MONITOR_SELECTION" [node name="MonitorDisplaySelector" type="OptionButton" parent="VBoxContainer/GridContainer"] layout_mode = 2 @@ -77,17 +76,17 @@ selected = 0 popup/item_0/text = "MISSING" popup/item_0/id = 0 script = ExtResource("3_y6lyb") -section_name = "Video" -setting_name = "Current Screen" +section_name = "video" +setting_name = "current_screen" [node name="RefreshRateLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Refresh Rate" +text = "OPTIONS_VIDEO_REFRESH_RATE" [node name="RefreshRateSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] editor_description = "UI-18" layout_mode = 2 -tooltip_text = "Only change from VSYNC if you are having issues with screen tearing." +tooltip_text = "OPTIONS_VIDEO_REFRESH_RATE_TOOLTIP" focus_neighbor_top = NodePath("../MonitorDisplaySelector") focus_neighbor_bottom = NodePath("../QualityPresetSelector") item_count = 8 @@ -109,13 +108,13 @@ popup/item_6/id = 6 popup/item_7/text = "Unlimited" popup/item_7/id = 7 script = ExtResource("4_381mg") -section_name = "Video" -setting_name = "Refresh Rate" +section_name = "video" +setting_name = "refresh_rate" default_selected = 0 [node name="QualityPresetLabel" type="Label" parent="VBoxContainer/GridContainer"] layout_mode = 2 -text = "Quality Preset" +text = "OPTIONS_VIDEO_QUALITY" [node name="QualityPresetSelector" type="OptionButton" parent="VBoxContainer/GridContainer"] editor_description = "UI-21" @@ -134,8 +133,8 @@ popup/item_3/id = 3 popup/item_4/text = "Custom" popup/item_4/id = 4 script = ExtResource("5_srg4v") -section_name = "Video" -setting_name = "Quality Preset" +section_name = "video" +setting_name = "quality_preset" default_selected = 1 [connection signal="item_selected" from="VBoxContainer/GridContainer/ResolutionSelector" to="VBoxContainer/GridContainer/ResolutionSelector" method="_on_item_selected"] diff --git a/game/src/OptionMenu/VolumeGrid.gd b/game/src/OptionMenu/VolumeGrid.gd index 5b1d13f..46613b4 100644 --- a/game/src/OptionMenu/VolumeGrid.gd +++ b/game/src/OptionMenu/VolumeGrid.gd @@ -14,13 +14,16 @@ func get_volume_value_as_db(value : float) -> float: # linear_to_db consumes a float between 0 and 1 to produce the db value return linear_to_db(value / RATIO_FOR_LINEAR) -func add_volume_row(bus_name : StringName, bus_index : int) -> HSlider: +func add_volume_row(bus_name : String, bus_index : int) -> HSlider: var volume_label := Label.new() - volume_label.text = bus_name + " Volume" + if bus_name == "Master": + volume_label.text = "MASTER_BUS" + else: + volume_label.text = bus_name add_child(volume_label) var volume_slider := SettingHSlider.new() - volume_slider.section_name = "Audio" + volume_slider.section_name = "audio" volume_slider.setting_name = volume_label.text volume_slider.custom_minimum_size = Vector2(290, 0) volume_slider.size_flags_vertical = Control.SIZE_FILL |