From b09dffce8a7c5cc943616b27555fe4da9e9e3ebc Mon Sep 17 00:00:00 2001 From: Marc Gilleron Date: Wed, 14 Nov 2018 00:47:56 +0000 Subject: [PATCH] Add and remove languages, show * on modified languages, fix string dialog, fix CSV save --- .../tools/csv_loader.gd | 48 +-- .../tools/language_selection_dialog.gd | 68 ++++ .../tools/language_selection_dialog.tscn | 193 +++++++++ .../tools/locales.gd | 367 ++++++++++++++++++ .../tools/po_loader.gd | 4 + .../tools/translation_editor.gd | 191 ++++++++- .../tools/translation_editor.tscn | 6 +- 7 files changed, 837 insertions(+), 40 deletions(-) create mode 100644 addons/zylann.translation_editor/tools/language_selection_dialog.gd create mode 100644 addons/zylann.translation_editor/tools/language_selection_dialog.tscn create mode 100644 addons/zylann.translation_editor/tools/locales.gd diff --git a/addons/zylann.translation_editor/tools/csv_loader.gd b/addons/zylann.translation_editor/tools/csv_loader.gd index 815af2f..68c8c11 100644 --- a/addons/zylann.translation_editor/tools/csv_loader.gd +++ b/addons/zylann.translation_editor/tools/csv_loader.gd @@ -49,18 +49,21 @@ class _Sorter: static func save_csv_translation(filepath, data): + print("Saving: ", data) var languages = {} for id in data: var s = data[id] for language in s.translations: - languages[id] = true + languages[language] = true - assert(len(data.languages) > 0) + if len(languages) == 0: + printerr("No language found, nothing to save") + return [] languages = languages.keys() languages.sort() - var first_row = [] + var first_row = ["id"] first_row.resize(len(languages) + 1) for i in len(languages): first_row[i + 1] = languages[i] @@ -78,10 +81,10 @@ static func save_csv_translation(filepath, data): var text = "" if s.translations.has(languages[i]): text = s.translations[languages[i]] - row[i] = text + row[i + 1] = text rows[row_index] = row row_index += 1 - + print(rows) var sorter = _Sorter.new() rows.sort_custom(sorter, "sort") @@ -91,29 +94,26 @@ static func save_csv_translation(filepath, data): var err = f.open(filepath, File.WRITE) if err != OK: printerr("Could not open ", filepath, " for write, code ", err) - return false - - for h in first_row: - f.store_string(str(",", h)) - f.store_string("\n") + return [] + store_csv_line(f, first_row) for row in rows: - for i in len(row): - var text = row[i] - if i == 0: - f.store_string(text) - else: - f.store_string(",") - # Behavior taken from LibreOffice - if text.contains(delim): - if text.contains('"'): - text = text.replace('"', '""') - text = str('"', text, '"') - f.store_string(text) - f.store_string("\n") + store_csv_line(f, row) f.close() print("Saved ", filepath) - return true + var saved_languages = languages + return saved_languages +static func store_csv_line(f, a, delim = ","): + for i in len(a): + if i > 0: + f.store_string(",") + var text = str(a[i]) + # Behavior taken from LibreOffice + if text.find(delim) != -1 or text.find('"') != -1 or text.find("\n") != -1: + text = str('"', text.replace('"', '""'), '"') + f.store_string(text) + f.store_string("\n") + diff --git a/addons/zylann.translation_editor/tools/language_selection_dialog.gd b/addons/zylann.translation_editor/tools/language_selection_dialog.gd new file mode 100644 index 0000000..03e6b36 --- /dev/null +++ b/addons/zylann.translation_editor/tools/language_selection_dialog.gd @@ -0,0 +1,68 @@ +tool +extends WindowDialog + +const Locales = preload("locales.gd") + +signal language_selected(language) + +onready var _filter_edit = get_node("VBoxContainer/FilterEdit") +onready var _languages_list = get_node("VBoxContainer/LanguagesList") +onready var _ok_button = get_node("VBoxContainer/Buttons/OkButton") + +var _hidden_locales = [] + + +func configure(hidden_locales): + _hidden_locales = hidden_locales + refresh_list() + + +func refresh_list(): + _languages_list.clear() + + var filter = _filter_edit.text.strip_edges() + var locales = Locales.get_all_locales() + + # Hidden root + _languages_list.create_item() + + for locale in locales: + if _hidden_locales.find(locale[0]) != -1: + continue + if filter != "" and locale[0].findn(filter) == -1: + continue + var item = _languages_list.create_item() + item.set_text(0, locale[0]) + item.set_text(1, locale[1]) + + _ok_button.disabled = true + + +func submit(): + var item = _languages_list.get_selected() + emit_signal("language_selected", item.get_text(0)) + hide() + + +func _on_OkButton_pressed(): + submit() + + +func _on_CancelButton_pressed(): + hide() + + +func _on_LanguagesList_item_selected(): + _ok_button.disabled = false + + +func _on_LanguagesList_nothing_selected(): + _ok_button.disabled = true + + +func _on_LanguagesList_item_activated(): + submit() + + +func _on_FilterEdit_text_changed(new_text): + refresh_list() diff --git a/addons/zylann.translation_editor/tools/language_selection_dialog.tscn b/addons/zylann.translation_editor/tools/language_selection_dialog.tscn new file mode 100644 index 0000000..5fe791d --- /dev/null +++ b/addons/zylann.translation_editor/tools/language_selection_dialog.tscn @@ -0,0 +1,193 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://addons/zylann.translation_editor/tools/language_selection_dialog.gd" type="Script" id=1] + +[node name="LanguageSelectionDialog" type="WindowDialog" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 157.0 +margin_top = 145.0 +margin_right = 507.0 +margin_bottom = 481.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +popup_exclusive = false +window_title = "Select new language" +resizable = true +script = ExtResource( 1 ) + +[node name="VBoxContainer" type="VBoxContainer" parent="." index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 1.0 +anchor_bottom = 1.0 +margin_left = 8.0 +margin_top = 8.0 +margin_right = -8.0 +margin_bottom = -8.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +alignment = 0 +_sections_unfolded = [ "Margin" ] + +[node name="FilterEdit" type="LineEdit" parent="VBoxContainer" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_right = 334.0 +margin_bottom = 24.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +focus_mode = 2 +mouse_filter = 0 +mouse_default_cursor_shape = 1 +size_flags_horizontal = 1 +size_flags_vertical = 1 +focus_mode = 2 +context_menu_enabled = true +placeholder_alpha = 0.6 +caret_blink = false +caret_blink_speed = 0.65 +caret_position = 0 + +[node name="LanguagesList" type="Tree" parent="VBoxContainer" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 28.0 +margin_right = 334.0 +margin_bottom = 284.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = true +focus_mode = 2 +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 3 +columns = 2 +allow_reselect = false +allow_rmb_select = false +hide_folding = false +hide_root = true +drop_mode_flags = 0 +select_mode = 1 +_sections_unfolded = [ "Size Flags" ] + +[node name="Spacer" type="Control" parent="VBoxContainer" index="2"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 288.0 +margin_right = 334.0 +margin_bottom = 296.0 +rect_min_size = Vector2( 0, 8 ) +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +_sections_unfolded = [ "Rect" ] + +[node name="Buttons" type="HBoxContainer" parent="VBoxContainer" index="3"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_top = 300.0 +margin_right = 334.0 +margin_bottom = 320.0 +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +mouse_filter = 1 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +custom_constants/separation = 8 +alignment = 1 +_sections_unfolded = [ "custom_constants" ] + +[node name="OkButton" type="Button" parent="VBoxContainer/Buttons" index="0"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 63.0 +margin_right = 163.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 100, 0 ) +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +focus_mode = 2 +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +toggle_mode = false +enabled_focus_mode = 2 +shortcut = null +group = null +text = "OK" +flat = false +align = 1 +_sections_unfolded = [ "Rect" ] + +[node name="CancelButton" type="Button" parent="VBoxContainer/Buttons" index="1"] + +anchor_left = 0.0 +anchor_top = 0.0 +anchor_right = 0.0 +anchor_bottom = 0.0 +margin_left = 171.0 +margin_right = 271.0 +margin_bottom = 20.0 +rect_min_size = Vector2( 100, 0 ) +rect_pivot_offset = Vector2( 0, 0 ) +rect_clip_content = false +focus_mode = 2 +mouse_filter = 0 +mouse_default_cursor_shape = 0 +size_flags_horizontal = 1 +size_flags_vertical = 1 +toggle_mode = false +enabled_focus_mode = 2 +shortcut = null +group = null +text = "Cancel" +flat = false +align = 1 +_sections_unfolded = [ "Rect" ] + +[connection signal="text_changed" from="VBoxContainer/FilterEdit" to="." method="_on_FilterEdit_text_changed"] + +[connection signal="item_activated" from="VBoxContainer/LanguagesList" to="." method="_on_LanguagesList_item_activated"] + +[connection signal="item_selected" from="VBoxContainer/LanguagesList" to="." method="_on_LanguagesList_item_selected"] + +[connection signal="nothing_selected" from="VBoxContainer/LanguagesList" to="." method="_on_LanguagesList_nothing_selected"] + +[connection signal="pressed" from="VBoxContainer/Buttons/OkButton" to="." method="_on_OkButton_pressed"] + +[connection signal="pressed" from="VBoxContainer/Buttons/CancelButton" to="." method="_on_CancelButton_pressed"] + + diff --git a/addons/zylann.translation_editor/tools/locales.gd b/addons/zylann.translation_editor/tools/locales.gd new file mode 100644 index 0000000..ad195f0 --- /dev/null +++ b/addons/zylann.translation_editor/tools/locales.gd @@ -0,0 +1,367 @@ + +# Copied from http://docs.godotengine.org/en/3.0/tutorials/i18n/locales.html +const _locales = [ + ["aa", "Afar"], + ["aa_DJ", "Afar (Djibouti)"], + ["aa_ER", "Afar (Eritrea)"], + ["aa_ET", "Afar (Ethiopia)"], + ["af", "Afrikaans"], + ["af_ZA", "Afrikaans (South Africa)"], + ["agr_PE", "Aguaruna (Peru)"], + ["ak_GH", "Akan (Ghana)"], + ["am_ET", "Amharic (Ethiopia)"], + ["an_ES", "Aragonese (Spain)"], + ["anp_IN", "Angika (India)"], + ["ar", "Arabic"], + ["ar_AE", "Arabic (United Arab Emirates)"], + ["ar_BH", "Arabic (Bahrain)"], + ["ar_DZ", "Arabic (Algeria)"], + ["ar_EG", "Arabic (Egypt)"], + ["ar_IQ", "Arabic (Iraq)"], + ["ar_JO", "Arabic (Jordan)"], + ["ar_KW", "Arabic (Kuwait)"], + ["ar_LB", "Arabic (Lebanon)"], + ["ar_LY", "Arabic (Libya)"], + ["ar_MA", "Arabic (Morocco)"], + ["ar_OM", "Arabic (Oman)"], + ["ar_QA", "Arabic (Qatar)"], + ["ar_SA", "Arabic (Saudi Arabia)"], + ["ar_SD", "Arabic (Sudan)"], + ["ar_SY", "Arabic (Syria)"], + ["ar_TN", "Arabic (Tunisia)"], + ["ar_YE", "Arabic (Yemen)"], + ["as_IN", "Assamese (India)"], + ["ast_ES", "Asturian (Spain)"], + ["ayc_PE", "Southern Aymara (Peru)"], + ["ay_PE", "Aymara (Peru)"], + ["az_AZ", "Azerbaijani (Azerbaijan)"], + ["be", "Belarusian"], + ["be_BY", "Belarusian (Belarus)"], + ["bem_ZM", "Bemba (Zambia)"], + ["ber_DZ", "Berber languages (Algeria)"], + ["ber_MA", "Berber languages (Morocco)"], + ["bg", "Bulgarian"], + ["bg_BG", "Bulgarian (Bulgaria)"], + ["bhb_IN", "Bhili (India)"], + ["bho_IN", "Bhojpuri (India)"], + ["bi_TV", "Bislama (Tuvalu)"], + ["bn", "Bengali"], + ["bn_BD", "Bengali (Bangladesh)"], + ["bn_IN", "Bengali (India)"], + ["bo", "Tibetan"], + ["bo_CN", "Tibetan (China)"], + ["bo_IN", "Tibetan (India)"], + ["br_FR", "Breton (France)"], + ["brx_IN", "Bodo (India)"], + ["bs_BA", "Bosnian (Bosnia and Herzegovina)"], + ["byn_ER", "Bilin (Eritrea)"], + ["ca", "Catalan"], + ["ca_AD", "Catalan (Andorra)"], + ["ca_ES", "Catalan (Spain)"], + ["ca_FR", "Catalan (France)"], + ["ca_IT", "Catalan (Italy)"], + ["ce_RU", "Chechen (Russia)"], + ["chr_US", "Cherokee (United States)"], + ["cmn_TW", "Mandarin Chinese (Taiwan)"], + ["crh_UA", "Crimean Tatar (Ukraine)"], + ["csb_PL", "Kashubian (Poland)"], + ["cs", "Czech"], + ["cs_CZ", "Czech (Czech Republic)"], + ["cv_RU", "Chuvash (Russia)"], + ["cy_GB", "Welsh (United Kingdom)"], + ["da", "Danish"], + ["da_DK", "Danish (Denmark)"], + ["de", "German"], + ["de_AT", "German (Austria)"], + ["de_BE", "German (Belgium)"], + ["de_CH", "German (Switzerland)"], + ["de_DE", "German (Germany)"], + ["de_IT", "German (Italy)"], + ["de_LU", "German (Luxembourg)"], + ["doi_IN", "Dogri (India)"], + ["dv_MV", "Dhivehi (Maldives)"], + ["dz_BT", "Dzongkha (Bhutan)"], + ["el", "Greek"], + ["el_CY", "Greek (Cyprus)"], + ["el_GR", "Greek (Greece)"], + ["en", "English"], + ["en_AG", "English (Antigua and Barbuda)"], + ["en_AU", "English (Australia)"], + ["en_BW", "English (Botswana)"], + ["en_CA", "English (Canada)"], + ["en_DK", "English (Denmark)"], + ["en_GB", "English (United Kingdom)"], + ["en_HK", "English (Hong Kong)"], + ["en_IE", "English (Ireland)"], + ["en_IL", "English (Israel)"], + ["en_IN", "English (India)"], + ["en_NG", "English (Nigeria)"], + ["en_MT", "English (Malta)"], + ["en_NZ", "English (New Zealand)"], + ["en_PH", "English (Philippines)"], + ["en_SG", "English (Singapore)"], + ["en_US", "English (United States)"], + ["en_ZA", "English (South Africa)"], + ["en_ZM", "English (Zambia)"], + ["en_ZW", "English (Zimbabwe)"], + ["eo", "Esperanto"], + ["es", "Spanish"], + ["es_AR", "Spanish (Argentina)"], + ["es_BO", "Spanish (Bolivia)"], + ["es_CL", "Spanish (Chile)"], + ["es_CO", "Spanish (Colombia)"], + ["es_CR", "Spanish (Costa Rica)"], + ["en_CU", "Spanish (Cuba)"], + ["es_DO", "Spanish (Dominican Republic)"], + ["es_EC", "Spanish (Ecuador)"], + ["es_ES", "Spanish (Spain)"], + ["es_GT", "Spanish (Guatemala)"], + ["es_HN", "Spanish (Honduras)"], + ["es_MX", "Spanish (Mexico)"], + ["es_NI", "Spanish (Nicaragua)"], + ["es_PA", "Spanish (Panama)"], + ["es_PE", "Spanish (Peru)"], + ["es_PR", "Spanish (Puerto Rico)"], + ["es_PY", "Spanish (Paraguay)"], + ["es_SV", "Spanish (El Salvador)"], + ["es_US", "Spanish (United States)"], + ["es_UY", "Spanish (Uruguay)"], + ["es_VE", "Spanish (Venezuela)"], + ["et", "Estonian"], + ["et_EE", "Estonian (Estonia)"], + ["eu", "Basque"], + ["eu_ES", "Basque (Spain)"], + ["fa", "Persian"], + ["fa_IR", "Persian (Iran)"], + ["ff_SN", "Fulah (Senegal)"], + ["fi", "Finnish"], + ["fi_FI", "Finnish (Finland)"], + ["fil_PH", "Filipino (Philippines)"], + ["fo_FO", "Faroese (Faroe Islands)"], + ["fr", "French"], + ["fr_BE", "French (Belgium)"], + ["fr_CA", "French (Canada)"], + ["fr_CH", "French (Switzerland)"], + ["fr_FR", "French (France)"], + ["fr_LU", "French (Luxembourg)"], + ["fur_IT", "Friulian (Italy)"], + ["fy_DE", "Western Frisian (Germany)"], + ["fy_NL", "Western Frisian (Netherlands)"], + ["ga", "Irish"], + ["ga_IE", "Irish (Ireland)"], + ["gd_GB", "Scottish Gaelic (United Kingdom)"], + ["gez_ER", "Geez (Eritrea)"], + ["gez_ET", "Geez (Ethiopia)"], + ["gl_ES", "Galician (Spain)"], + ["gu_IN", "Gujarati (India)"], + ["gv_GB", "Manx (United Kingdom)"], + ["hak_TW", "Hakka Chinese (Taiwan)"], + ["ha_NG", "Hausa (Nigeria)"], + ["he", "Hebrew"], + ["he_IL", "Hebrew (Israel)"], + ["hi", "Hindi"], + ["hi_IN", "Hindi (India)"], + ["hne_IN", "Chhattisgarhi (India)"], + ["hr", "Croatian"], + ["hr_HR", "Croatian (Croatia)"], + ["hsb_DE", "Upper Sorbian (Germany)"], + ["ht_HT", "Haitian (Haiti)"], + ["hu", "Hungarian"], + ["hu_HU", "Hungarian (Hungary)"], + ["hus_MX", "Huastec (Mexico)"], + ["hy_AM", "Armenian (Armenia)"], + ["ia_FR", "Interlingua (France)"], + ["id", "Indonesian"], + ["id_ID", "Indonesian (Indonesia)"], + ["ig_NG", "Igbo (Nigeria)"], + ["ik_CA", "Inupiaq (Canada)"], + ["is", "Icelandic"], + ["is_IS", "Icelandic (Iceland)"], + ["it", "Italian"], + ["it_CH", "Italian (Switzerland)"], + ["it_IT", "Italian (Italy)"], + ["iu_CA", "Inuktitut (Canada)"], + ["ja", "Japanese"], + ["ja_JP", "Japanese (Japan)"], + ["kab_DZ", "Kabyle (Algeria)"], + ["ka_GE", "Georgian (Georgia)"], + ["kk_KZ", "Kazakh (Kazakhstan)"], + ["kl_GL", "Kalaallisut (Greenland)"], + ["km_KH", "Central Khmer (Cambodia)"], + ["kn_IN", "Kannada (India)"], + ["kok_IN", "Konkani (India)"], + ["ko", "Korean"], + ["ko_KR", "Korean (South Korea)"], + ["ks_IN", "Kashmiri (India)"], + ["ku", "Kurdish"], + ["ku_TR", "Kurdish (Turkey)"], + ["kw_GB", "Cornish (United Kingdom)"], + ["ky_KG", "Kirghiz (Kyrgyzstan)"], + ["lb_LU", "Luxembourgish (Luxembourg)"], + ["lg_UG", "Ganda (Uganda)"], + ["li_BE", "Limburgan (Belgium)"], + ["li_NL", "Limburgan (Netherlands)"], + ["lij_IT", "Ligurian (Italy)"], + ["ln_CD", "Lingala (Congo)"], + ["lo_LA", "Lao (Laos)"], + ["lt", "Lithuanian"], + ["lt_LT", "Lithuanian (Lithuania)"], + ["lv", "Latvian"], + ["lv_LV", "Latvian (Latvia)"], + ["lzh_TW", "Literary Chinese (Taiwan)"], + ["mag_IN", "Magahi (India)"], + ["mai_IN", "Maithili (India)"], + ["mg_MG", "Malagasy (Madagascar)"], + ["mh_MH", "Marshallese (Marshall Islands)"], + ["mhr_RU", "Eastern Mari (Russia)"], + ["mi_NZ", "Maori (New Zealand)"], + ["miq_NI", "Mískito (Nicaragua)"], + ["mk", "Macedonian"], + ["mk_MK", "Macedonian (Macedonia)"], + ["ml_IN", "Malayalam (India)"], + ["mni_IN", "Manipuri (India)"], + ["mn_MN", "Mongolian (Mongolia)"], + ["mr_IN", "Marathi (India)"], + ["ms", "Malay"], + ["ms_MY", "Malay (Malaysia)"], + ["mt", "Maltese"], + ["mt_MT", "Maltese (Malta)"], + ["my_MM", "Burmese (Myanmar)"], + ["myv_RU", "Erzya (Russia)"], + ["nah_MX", "Nahuatl languages (Mexico)"], + ["nan_TW", "Min Nan Chinese (Taiwan)"], + ["nb", "Norwegian Bokmål"], + ["nb_NO", "Norwegian Bokmål (Norway)"], + ["nds_DE", "Low German (Germany)"], + ["nds_NL", "Low German (Netherlands)"], + ["ne_NP", "Nepali (Nepal)"], + ["nhn_MX", "Central Nahuatl (Mexico)"], + ["niu_NU", "Niuean (Niue)"], + ["niu_NZ", "Niuean (New Zealand)"], + ["nl", "Dutch"], + ["nl_AW", "Dutch (Aruba)"], + ["nl_BE", "Dutch (Belgium)"], + ["nl_NL", "Dutch (Netherlands)"], + ["nn", "Norwegian Nynorsk"], + ["nn_NO", "Norwegian Nynorsk (Norway)"], + ["no", "Norwegian"], + ["no_NO", "Norwegian (Norway)"], + ["nr_ZA", "South Ndebele (South Africa)"], + ["nso_ZA", "Pedi (South Africa)"], + ["oc_FR", "Occitan (France)"], + ["om", "Oromo"], + ["om_ET", "Oromo (Ethiopia)"], + ["om_KE", "Oromo (Kenya)"], + ["or_IN", "Oriya (India)"], + ["os_RU", "Ossetian (Russia)"], + ["pa_IN", "Panjabi (India)"], + ["pap", "Papiamento"], + ["pap_AN", "Papiamento (Netherlands Antilles)"], + ["pap_AW", "Papiamento (Aruba)"], + ["pap_CW", "Papiamento (Curaçao)"], + ["pa_PK", "Panjabi (Pakistan)"], + ["pl", "Polish"], + ["pl_PL", "Polish (Poland)"], + ["pr", "Pirate"], + ["ps_AF", "Pushto (Afghanistan)"], + ["pt", "Portuguese"], + ["pt_BR", "Portuguese (Brazil)"], + ["pt_PT", "Portuguese (Portugal)"], + ["quy_PE", "Ayacucho Quechua (Peru)"], + ["quz_PE", "Cusco Quechua (Peru)"], + ["raj_IN", "Rajasthani (India)"], + ["ro", "Romanian"], + ["ro_RO", "Romanian (Romania)"], + ["ru", "Russian"], + ["ru_RU", "Russian (Russia)"], + ["ru_UA", "Russian (Ukraine)"], + ["rw_RW", "Kinyarwanda (Rwanda)"], + ["sa_IN", "Sanskrit (India)"], + ["sat_IN", "Santali (India)"], + ["sc_IT", "Sardinian (Italy)"], + ["sco", "Scots"], + ["sd_IN", "Sindhi (India)"], + ["se_NO", "Northern Sami (Norway)"], + ["sgs_LT", "Samogitian (Lithuania)"], + ["shs_CA", "Shuswap (Canada)"], + ["sid_ET", "Sidamo (Ethiopia)"], + ["si_LK", "Sinhala (Sri Lanka)"], + ["sk", "Slovak"], + ["sk_SK", "Slovak (Slovakia)"], + ["sl", "Slovenian"], + ["so", "Somali"], + ["so_DJ", "Somali (Djibouti)"], + ["so_ET", "Somali (Ethiopia)"], + ["so_KE", "Somali (Kenya)"], + ["so_SO", "Somali (Somalia)"], + ["son_ML", "Songhai languages (Mali)"], + ["sq", "Albanian"], + ["sq_AL", "Albanian (Albania)"], + ["sq_KV", "Albanian (Kosovo)"], + ["sq_MK", "Albanian (Macedonia)"], + ["sr", "Serbian"], + ["sr_BA", "Serbian (Bosnia and Herzegovina)"], + ["sr_CS", "Serbian (Serbia and Montenegro)"], + ["sr_ME", "Serbian (Montenegro)"], + ["sr_RS", "Serbian (Serbia)"], + ["ss_ZA", "Swati (South Africa)"], + ["st_ZA", "Southern Sotho (South Africa)"], + ["sv", "Swedish"], + ["sv_FI", "Swedish (Finland)"], + ["sv_SE", "Swedish (Sweden)"], + ["sw_KE", "Swahili (Kenya)"], + ["sw_TZ", "Swahili (Tanzania)"], + ["szl_PL", "Silesian (Poland)"], + ["ta", "Tamil"], + ["ta_IN", "Tamil (India)"], + ["ta_LK", "Tamil (Sri Lanka)"], + ["tcy_IN", "Tulu (India)"], + ["te_IN", "Telugu (India)"], + ["tg_TJ", "Tajik (Tajikistan)"], + ["the_NP", "Chitwania Tharu (Nepal)"], + ["th", "Thai"], + ["th_TH", "Thai (Thailand)"], + ["th_TH_TH", "Thai (Thailand,TH)"], + ["ti", "Tigrinya"], + ["ti_ER", "Tigrinya (Eritrea)"], + ["ti_ET", "Tigrinya (Ethiopia)"], + ["tig_ER", "Tigre (Eritrea)"], + ["tk_TM", "Turkmen (Turkmenistan)"], + ["tl_PH", "Tagalog (Philippines)"], + ["tn_ZA", "Tswana (South Africa)"], + ["tr", "Turkish"], + ["tr_CY", "Turkish (Cyprus)"], + ["tr_TR", "Turkish (Turkey)"], + ["ts_ZA", "Tsonga (South Africa)"], + ["tt_RU", "Tatar (Russia)"], + ["ug_CN", "Uighur (China)"], + ["uk", "Ukrainian"], + ["uk_UA", "Ukrainian (Ukraine)"], + ["unm_US", "Unami (United States)"], + ["ur", "Urdu"], + ["ur_IN", "Urdu (India)"], + ["ur_PK", "Urdu (Pakistan)"], + ["uz", "Uzbek"], + ["uz_UZ", "Uzbek (Uzbekistan)"], + ["ve_ZA", "Venda (South Africa)"], + ["vi", "Vietnamese"], + ["vi_VN", "Vietnamese (Vietnam)"], + ["wa_BE", "Walloon (Belgium)"], + ["wae_CH", "Walser (Switzerland)"], + ["wal_ET", "Wolaytta (Ethiopia)"], + ["wo_SN", "Wolof (Senegal)"], + ["xh_ZA", "Xhosa (South Africa)"], + ["yi_US", "Yiddish (United States)"], + ["yo_NG", "Yoruba (Nigeria)"], + ["yue_HK", "Yue Chinese (Hong Kong)"], + ["zh", "Chinese"], + ["zh_CN", "Chinese (China)"], + ["zh_HK", "Chinese (Hong Kong)"], + ["zh_SG", "Chinese (Singapore)"], + ["zh_TW", "Chinese (Taiwan)"], + ["zu_ZA", "Zulu (South Africa)"] +] + +static func get_all_locales(): + return _locales + diff --git a/addons/zylann.translation_editor/tools/po_loader.gd b/addons/zylann.translation_editor/tools/po_loader.gd index dc0b43f..8df7085 100644 --- a/addons/zylann.translation_editor/tools/po_loader.gd +++ b/addons/zylann.translation_editor/tools/po_loader.gd @@ -81,6 +81,7 @@ class _Sorter: static func save_po_translations(folder_path, translations, languages_to_save): var sorter = _Sorter.new() + var saved_languages = [] for language in languages_to_save: @@ -115,6 +116,9 @@ static func save_po_translations(folder_path, translations, languages_to_save): f.store_line("") f.close() + saved_languages.append(language) + + return saved_languages static func _write_msg(f, msgtype, msg): diff --git a/addons/zylann.translation_editor/tools/translation_editor.gd b/addons/zylann.translation_editor/tools/translation_editor.gd index 7deabd8..1ab7b84 100644 --- a/addons/zylann.translation_editor/tools/translation_editor.gd +++ b/addons/zylann.translation_editor/tools/translation_editor.gd @@ -4,10 +4,13 @@ extends Panel const CsvLoader = preload("csv_loader.gd") const PoLoader = preload("po_loader.gd") const StringEditionDialog = preload("string_edition_dialog.tscn") +const LanguageSelectionDialog = preload("language_selection_dialog.tscn") const MENU_FILE_OPEN = 0 const MENU_FILE_SAVE = 1 const MENU_FILE_SAVE_AS = 2 +const MENU_FILE_ADD_LANGUAGE = 3 +const MENU_FILE_REMOVE_LANGUAGE = 4 onready var _file_menu = get_node("VBoxContainer/MenuBar/FileMenu") onready var _edit_menu = get_node("VBoxContainer/MenuBar/EditMenu") @@ -17,19 +20,19 @@ onready var _translation_tab_container = \ onready var _notes_edit = get_node("VBoxContainer/Main/RightPane/VSplitContainer/VBoxContainer/NotesEdit") var _string_edit_dialog = null +var _language_selection_dialog = null +var _remove_language_confirmation_dialog = null var _open_dialog = null var _save_dialog = null - # This is set when integrated as a Godot plugin var _base_control = null - -var _data = null -# TODO Make this a config of some sort -var _languages = ["en", "fr"] -var _current_file = null - var _translation_edits = {} +var _data = {} +var _languages = [] +var _current_file = null +var _modified_languages = {} + func _ready(): # I don't want any of this to run in the edited scene (because `tool`)... @@ -39,6 +42,10 @@ func _ready(): _file_menu.get_popup().add_item("Open...", MENU_FILE_OPEN) _file_menu.get_popup().add_item("Save", MENU_FILE_SAVE) _file_menu.get_popup().add_item("Save as...", MENU_FILE_SAVE_AS) + _file_menu.get_popup().add_separator() + _file_menu.get_popup().add_item("Add language...", MENU_FILE_ADD_LANGUAGE) + _file_menu.get_popup().add_item("Remove language", MENU_FILE_REMOVE_LANGUAGE) + _file_menu.get_popup().set_item_disabled(_file_menu.get_popup().get_item_index(MENU_FILE_REMOVE_LANGUAGE), true) _file_menu.get_popup().connect("id_pressed", self, "_on_FileMenu_id_pressed") _edit_menu.get_popup().connect("id_pressed", self, "_on_EditMenu_id_pressed") @@ -66,7 +73,17 @@ func _ready(): _string_edit_dialog = StringEditionDialog.instance() _string_edit_dialog.set_validator(funcref(self, "_validate_new_string_id")) + _string_edit_dialog.connect("submitted", self, "_on_StringEditionDialog_submitted") dialogs_parent.add_child(_string_edit_dialog) + + _language_selection_dialog = LanguageSelectionDialog.instance() + _language_selection_dialog.connect("language_selected", self, "_on_LanguageSelectionDialog_language_selected") + dialogs_parent.add_child(_language_selection_dialog) + + _remove_language_confirmation_dialog = ConfirmationDialog.new() + _remove_language_confirmation_dialog.dialog_text = "Do you really want to remove this language? (There is no undo!)" + _remove_language_confirmation_dialog.connect("confirmed", self, "_on_RemoveLanguageConfirmationDialog_confirmed") + dialogs_parent.add_child(_remove_language_confirmation_dialog) func configure_for_godot_integration(base_control): @@ -79,10 +96,21 @@ func _on_FileMenu_id_pressed(id): match id: MENU_FILE_OPEN: _open_dialog.popup_centered_ratio() + MENU_FILE_SAVE: _save() + MENU_FILE_SAVE_AS: _save_dialog.popup_centered_ratio() + + MENU_FILE_ADD_LANGUAGE: + _language_selection_dialog.configure(_languages) + _language_selection_dialog.popup_centered_ratio() + + MENU_FILE_REMOVE_LANGUAGE: + var language = get_current_language() + _remove_language_confirmation_dialog.window_title = str("Remove language `", language, "`") + _remove_language_confirmation_dialog.popup_centered_minsize() func _on_EditMenu_id_pressed(id): @@ -101,6 +129,10 @@ func _on_SaveButton_pressed(): _save() +func _on_LanguageSelectionDialog_language_selected(language): + _add_language(language) + + func _save(): if _current_file == null: _save_dialog.popup_centered_ratio() @@ -118,6 +150,13 @@ func load_file(filepath): printerr("Unknown file format, cannot load ", filepath) return + _languages.clear() + for strid in _data: + var s = _data[strid] + for language in s.translations: + if _languages.find(language) == -1: + _languages.append(language) + _translation_edits.clear() for i in _translation_tab_container.get_child_count(): @@ -126,25 +165,108 @@ func load_file(filepath): child.queue_free() for language in _languages: - var edit = TextEdit.new() - var tab_index = _translation_tab_container.get_tab_count() - _translation_tab_container.add_child(edit) - _translation_tab_container.set_tab_title(tab_index, language) - _translation_edits[language] = edit + _create_translation_edit(language) refresh_list() _current_file = filepath + _modified_languages.clear() + + +func _create_translation_edit(language): + assert(not _translation_edits.has(language)) # boom + var edit = TextEdit.new() + edit.hide() + var tab_index = _translation_tab_container.get_tab_count() + _translation_tab_container.add_child(edit) + _translation_tab_container.set_tab_title(tab_index, language) + _translation_edits[language] = edit + edit.connect("text_changed", self, "_on_TranslationEdit_text_changed", [language]) + + +func _on_TranslationEdit_text_changed(language): + var edit = _translation_edits[language] + var selected_strids = _string_list.get_selected_items() + # TODO Don't show the editor if no strings are selected + if len(selected_strids) != 1: + return + #assert(len(selected_strids) == 1) + var strid = _string_list.get_item_text(selected_strids[0]) + var prev_text = null + var s = _data[strid] + if s.translations.has(language): + prev_text = s.translations[language] + if prev_text != edit.text: + s.translations[language] = edit.text + _set_language_modified(language) + + +func _on_NotesEdit_text_changed(): + var selected_strids = _string_list.get_selected_items() + # TODO Don't show the editor if no strings are selected + if len(selected_strids) != 1: + return + #assert(len(selected_strids) == 1) + var strid = _string_list.get_item_text(selected_strids[0]) + var s = _data[strid] + if s.comments != _notes_edit.text: + s.comments = _notes_edit.text + for language in _languages: + _set_language_modified(language) + + +func _set_language_modified(language): + if _modified_languages.has(language): + return + _modified_languages[language] = true + _set_language_tab_title(language, str(language, "*")) + + +func _set_language_unmodified(language): + if not _modified_languages.has(language): + return + _modified_languages.erase(language) + _set_language_tab_title(language, language) + + +func _set_language_tab_title(language, title): + var page = _translation_edits[language] + for i in _translation_tab_container.get_child_count(): + if _translation_tab_container.get_child(i) == page: + # TODO There seem to be a Godot bug, tab titles don't always update unless you click on them Oo + print("Set tab title ", title) + _translation_tab_container.set_tab_title(i, title) + return + # Something bad happened + assert(false) + + +func get_current_language(): + var page = _translation_tab_container.get_current_tab_control() + for language in _translation_edits: + if _translation_edits[language] == page: + return language + # Something bad happened + assert(false) + return null func save_file(filepath): var ext = filepath.get_extension() + var saved_languages = [] + if ext == "po": - PoLoader.save_po_translations(filepath.get_base_dir(), _data, _languages) + var languages_to_save = _modified_languages.keys() + saved_languages = PoLoader.save_po_translations(filepath.get_base_dir(), _data, languages_to_save) elif ext == "csv": - CsvLoader.save_csv_translation(_data) + saved_languages = CsvLoader.save_csv_translation(filepath, _data) else: printerr("Unknown file format, cannot save ", filepath) + for language in saved_languages: + _set_language_unmodified(language) + + _current_file = filepath + func refresh_list(): _string_list.clear() @@ -160,6 +282,7 @@ func _on_StringList_item_selected(index): var s = _data[str_id] for language in _languages: var e = _translation_edits[language] + e.show() if s.translations.has(language): e.text = s.translations[language] else: @@ -196,10 +319,16 @@ func _on_StringEditionDialog_submitted(str_id, prev_str_id): func _validate_new_string_id(str_id): if _data.has(str_id): return "Already existing" + if str_id.strip_edges() != str_id: + return "Must not start or end with spaces" + for k in _data: + if k.nocasecmp_to(str_id) == 0: + return "Already existing with different case" return true func add_new_string(strid): + print("Adding new string ", strid) assert(not _data.has(strid)) var s = { "translations": {}, @@ -219,3 +348,37 @@ func rename_string(old_strid, new_strid): _string_list.set_item_text(i, new_strid) break + +func _add_language(language): + assert(_languages.find(language) == -1) + + _create_translation_edit(language) + _languages.append(language) + _set_language_modified(language) + + var menu_index = _file_menu.get_popup().get_item_index(MENU_FILE_REMOVE_LANGUAGE) + _file_menu.get_popup().set_item_disabled(menu_index, false) + + print("Added language ", language) + + +func _remove_language(language): + assert(_languages.find(language) != -1) + + _set_language_unmodified(language) + var edit = _translation_edits[language] + edit.queue_free() + _translation_edits.erase(language) + _languages.erase(language) + + if len(_languages) == 0: + var menu_index = _file_menu.get_popup().get_item_index(MENU_FILE_REMOVE_LANGUAGE) + _file_menu.get_popup().set_item_disabled(menu_index, true) + + print("Removed language ", language) + + +func _on_RemoveLanguageConfirmationDialog_confirmed(): + var language = get_current_language() + _remove_language(language) + diff --git a/addons/zylann.translation_editor/tools/translation_editor.tscn b/addons/zylann.translation_editor/tools/translation_editor.tscn index 56e7443..81f2594 100644 --- a/addons/zylann.translation_editor/tools/translation_editor.tscn +++ b/addons/zylann.translation_editor/tools/translation_editor.tscn @@ -3,7 +3,7 @@ [ext_resource path="res://addons/zylann.translation_editor/tools/translation_editor.gd" type="Script" id=1] [ext_resource path="res://addons/zylann.translation_editor/tools/icons/icon_save.svg" type="Texture" id=2] -[node name="TranslationEditor" type="Panel" index="0"] +[node name="TranslationEditor" type="Panel"] anchor_left = 0.0 anchor_top = 0.0 @@ -432,7 +432,7 @@ caret_block_mode = false caret_blink = false caret_blink_speed = 0.65 caret_moving_by_right_click = true -_sections_unfolded = [ "Size Flags" ] +_sections_unfolded = [ "Rect", "Size Flags" ] [node name="StatusBar" type="HBoxContainer" parent="VBoxContainer" index="2"] @@ -480,4 +480,6 @@ max_lines_visible = -1 [connection signal="pressed" from="VBoxContainer/Main/LeftPane/StringListActions/RenameButton" to="." method="_on_RenameButton_pressed"] +[connection signal="text_changed" from="VBoxContainer/Main/RightPane/VSplitContainer/VBoxContainer/NotesEdit" to="." method="_on_NotesEdit_text_changed"] +