diff --git a/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs index 82503d9bc..713afe969 100644 --- a/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs +++ b/Source/Editor/CustomEditors/Editors/LocalizedStringEditor.cs @@ -176,7 +176,6 @@ namespace FlaxEditor.CustomEditors.Editors allKeys.Add(e.Key); } } - _valueElement.TextBox.SetTextAsUser(null); string newKey = null; if (string.IsNullOrEmpty(_idElement.Text)) { @@ -220,13 +219,14 @@ namespace FlaxEditor.CustomEditors.Editors { var table = locale.First(); var entries = table.Entries; - if (table.Locale == "en") + if (table.Locale == settings.LocalizedStringTables[0].Locale) entries[newKey] = new[] { newValue }; else entries[newKey] = new[] { string.Empty }; table.Entries = entries; table.Save(); } + _valueElement.TextBox.SetTextAsUser(null); _idElement.TextBox.SetTextAsUser(newKey); Profiler.EndEvent(); } diff --git a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs index e4a772a5d..5351c2b6c 100644 --- a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs +++ b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs @@ -113,7 +113,10 @@ namespace FlaxEditor.Windows.Assets [CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.CultureInfoEditor))] public string Locale; - [EditorOrder(10), EditorDisplay("Entries", EditorDisplayAttribute.InlineStyle), Tooltip("The string table. Maps the message id into the localized text. For plural messages the list contains separate items for value numbers.")] + [EditorOrder(10), EditorDisplay("General"), Tooltip("The fallback table to use for lookup."),] + public LocalizedStringTable FallBackTable; + + [EditorOrder(20), EditorDisplay("Entries", EditorDisplayAttribute.InlineStyle), Tooltip("The string table. Maps the message id into the localized text. For plural messages the list contains separate items for value numbers.")] [Collection(Spacing = 10, OverrideEditorTypeName = "FlaxEditor.Windows.Assets.LocalizedStringTableWindow+EntryEditor")] public Dictionary Entries; } @@ -166,6 +169,7 @@ namespace FlaxEditor.Windows.Assets _asset.Locale = _proxy.Locale; _asset.Entries = _proxy.Entries; + _asset.FallbackTable = _proxy.FallBackTable; if (_asset.Save(_item.Path)) { Editor.LogError("Cannot save asset."); @@ -192,6 +196,7 @@ namespace FlaxEditor.Windows.Assets { Locale = _asset.Locale, Entries = _asset.Entries, + FallBackTable = _asset.FallbackTable }; _presenter.Select(_proxy); _undo.Clear(); diff --git a/Source/Engine/Localization/Localization.cpp b/Source/Engine/Localization/Localization.cpp index defab1c4f..c7b956ee0 100644 --- a/Source/Engine/Localization/Localization.cpp +++ b/Source/Engine/Localization/Localization.cpp @@ -2,8 +2,6 @@ #include "Localization.h" #include "CultureInfo.h" -#include "LocalizedString.h" -#include "LocalizationSettings.h" #include "Engine/Core/Log.h" #include "Engine/Core/Config/GameSettings.h" #include "Engine/Engine/EngineService.h" @@ -19,6 +17,8 @@ public: CultureInfo CurrentLanguage; Array> LocalizedStringTables; + Dictionary, InlinedAllocation<8>>> tables; + LocalizationService() : EngineService(TEXT("Localization"), -500) , CurrentCulture(0) @@ -122,7 +122,9 @@ void LocalizationService::OnLocalizationChanged() // Collect all localization tables into mapping locale -> tables auto& settings = *LocalizationSettings::Get(); - Dictionary, InlinedAllocation<8>>> tables; + + + tables.Clear(); for (auto& e : settings.LocalizedStringTables) { auto table = e.Get(); @@ -142,9 +144,14 @@ void LocalizationService::OnLocalizationChanged() table = tables.TryGet(parentLanguage.GetName()); if (!table) { - // Fallback to English - const CultureInfo english("en"); - table = tables.TryGet(english.GetName()); + // Fallback to Default + table = tables.TryGet(settings.DefaultFallbackLanguage); + if (!table) + { + // Fallback to English + const CultureInfo english("en"); + table = tables.TryGet(english.GetName()); + } } } @@ -159,7 +166,7 @@ void LocalizationService::OnLocalizationChanged() locale = e.Key; break; } - } + } LOG(Info, "Using localization for {0}", locale); Instance.LocalizedStringTables.Add(table->Get(), table->Count()); } @@ -258,15 +265,22 @@ void Localization::SetCurrentLanguageCulture(const CultureInfo& value) String Localization::GetString(const String& id, const String& fallback) { + if (id.IsEmpty()) + return fallback; String result; for (auto& e : Instance.LocalizedStringTables) { const auto table = e.Get(); - const auto messages = table ? table->Entries.TryGet(id) : nullptr; - if (messages && messages->Count() != 0) + if (table) + result = table->GetString(id); + } + if (result.IsEmpty() && Instance.tables.ContainsKey(LocalizationSettings::Get()->DefaultFallbackLanguage)) { + + for (auto& e : Instance.tables[LocalizationSettings::Get()->DefaultFallbackLanguage]) { - result = messages->At(0); - break; + const auto table = e.Get(); + if (table) + result = table->GetString(id); } } if (result.IsEmpty()) @@ -276,17 +290,24 @@ String Localization::GetString(const String& id, const String& fallback) String Localization::GetPluralString(const String& id, int32 n, const String& fallback) { + if (id.IsEmpty()) + return fallback; CHECK_RETURN(n >= 1, fallback); n--; StringView result; for (auto& e : Instance.LocalizedStringTables) { const auto table = e.Get(); - const auto messages = table ? table->Entries.TryGet(id) : nullptr; - if (messages && messages->Count() > n) + if(table) + result = table->GetPluralString(id, n); + } + if (result.IsEmpty() && Instance.tables.ContainsKey(LocalizationSettings::Get()->DefaultFallbackLanguage)) + { + for (auto& e : Instance.tables[LocalizationSettings::Get()->DefaultFallbackLanguage]) { - result = messages->At(n); - break; + const auto table = e.Get(); + if (table) + result = table->GetPluralString(id, n); } } if (result.IsEmpty()) diff --git a/Source/Engine/Localization/Localization.h b/Source/Engine/Localization/Localization.h index 6502c5523..89ec95ed3 100644 --- a/Source/Engine/Localization/Localization.h +++ b/Source/Engine/Localization/Localization.h @@ -4,6 +4,8 @@ #include "CultureInfo.h" #include "Engine/Core/Types/BaseTypes.h" +#include "LocalizedString.h" +#include "LocalizationSettings.h" /// /// The language and culture localization manager. @@ -59,4 +61,5 @@ public: /// The optional fallback string value to use if localized string is missing. /// The localized text. API_FUNCTION() static String GetPluralString(const String& id, int32 n, const String& fallback = String::Empty); + }; diff --git a/Source/Engine/Localization/LocalizationSettings.h b/Source/Engine/Localization/LocalizationSettings.h index 26ba06c96..af90a2362 100644 --- a/Source/Engine/Localization/LocalizationSettings.h +++ b/Source/Engine/Localization/LocalizationSettings.h @@ -18,8 +18,14 @@ public: /// API_FIELD() Array> LocalizedStringTables; + /// + /// The last used lanaguage, only other thing that could be used is hardcoded "en" if this is not set too + /// + /// + API_FIELD() String DefaultFallbackLanguage; public: + /// /// Gets the instance of the settings asset (default value if missing). Object returned by this method is always loaded with valid data to use. /// diff --git a/Source/Engine/Localization/LocalizedString.cs b/Source/Engine/Localization/LocalizedString.cs index 2d6535336..464be8bba 100644 --- a/Source/Engine/Localization/LocalizedString.cs +++ b/Source/Engine/Localization/LocalizedString.cs @@ -41,7 +41,7 @@ namespace FlaxEngine /// The localized text. public string ToStringPlural(int n) { - return string.IsNullOrEmpty(Value) ? Localization.GetPluralString(Id, n) : Value; + return Localization.GetPluralString(Id, n, Value); } /// @@ -53,7 +53,7 @@ namespace FlaxEngine { if ((object)str == null) return null; - return string.IsNullOrEmpty(str.Value) ? Localization.GetString(str.Id) : str.Value; + return Localization.GetString(str.Id, str.Value); } /// @@ -111,7 +111,7 @@ namespace FlaxEngine /// public bool Equals(string other) { - return Value == other || Localization.GetString(Id) == other; + return Value == other || Localization.GetString(Id, Value) == other; } /// @@ -144,7 +144,7 @@ namespace FlaxEngine /// public override string ToString() { - return string.IsNullOrEmpty(Value) ? Localization.GetString(Id) : Value; + return Localization.GetString(Id, Value); } } } diff --git a/Source/Engine/Localization/LocalizedStringTable.cpp b/Source/Engine/Localization/LocalizedStringTable.cpp index c705856f4..45d5b0e5e 100644 --- a/Source/Engine/Localization/LocalizedStringTable.cpp +++ b/Source/Engine/Localization/LocalizedStringTable.cpp @@ -33,6 +33,35 @@ void LocalizedStringTable::AddPluralString(const StringView& id, const StringVie values[n] = value; } + + +String LocalizedStringTable::GetString(const String& id) +{ + StringView result; + const auto messages = Entries.TryGet(id); + if (messages && messages->Count() != 0) + { + result = messages->At(0); + } + if (result.IsEmpty() && FallbackTable) + result = FallbackTable->GetString(id); + return result; +} + +String LocalizedStringTable::GetPluralString(const String& id, int32 n) +{ + StringView result; + const auto messages = Entries.TryGet(id); + if (messages && messages->Count() != 0) + { + result = messages->At(0); + } + if (result.IsEmpty() && FallbackTable) + result = FallbackTable->GetPluralString(id, n); + return result; +} + + #if USE_EDITOR bool LocalizedStringTable::Save(const StringView& path) diff --git a/Source/Engine/Localization/LocalizedStringTable.h b/Source/Engine/Localization/LocalizedStringTable.h index 1c1b8d795..81ec1f086 100644 --- a/Source/Engine/Localization/LocalizedStringTable.h +++ b/Source/Engine/Localization/LocalizedStringTable.h @@ -5,6 +5,7 @@ #include "Engine/Content/JsonAsset.h" #include "Engine/Core/Collections/Array.h" #include "Engine/Core/Collections/Dictionary.h" +#include "Engine/Content/AssetReference.h" /// /// Contains localized strings table for a given culture. @@ -19,6 +20,11 @@ public: /// API_FIELD() String Locale; + /// + /// The fallback table used if we couldnt find the key here + /// + API_FIELD() AssetReference FallbackTable; + /// /// The string table. Maps the message id into the localized text. For plural messages the list contains separate items for value numbers. /// @@ -40,6 +46,26 @@ public: /// The plural value (0, 1, 2..). API_FUNCTION() void AddPluralString(const StringView& id, const StringView& value, int32 n); + + + /// + /// Gets the localized string for the current language by using string id lookup. + /// + /// The message identifier. + /// The optional fallback string value to use if localized string is missing. + /// The localized text. + API_FUNCTION() String GetString(const String& id); + + /// + /// Gets the localized plural string for the current language by using string id lookup. + /// + /// The message identifier. + /// The value count for plural message selection. + /// The optional fallback string value to use if localized string is missing. + /// The localized text. + API_FUNCTION() String GetPluralString(const String& id, int32 n); + + #if USE_EDITOR ///