From 80ef2befd55ee58c3cbeb841185c9cd2215b9f67 Mon Sep 17 00:00:00 2001 From: Wojciech Figat Date: Mon, 15 Nov 2021 16:37:00 +0100 Subject: [PATCH] Add `FallbackTable` for localized strings table to redirect missing texts into other language --- .../Assets/LocalizedStringTableWindow.cs | 6 +++ Source/Engine/Localization/Localization.cpp | 40 ++++++++++++++++--- .../Localization/LocalizedStringTable.cpp | 10 +++++ .../Localization/LocalizedStringTable.h | 8 ++++ 4 files changed, 59 insertions(+), 5 deletions(-) diff --git a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs index 1d2df942c..b92b7ae9f 100644 --- a/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs +++ b/Source/Editor/Windows/Assets/LocalizedStringTableWindow.cs @@ -113,6 +113,10 @@ namespace FlaxEditor.Windows.Assets [CustomEditor(typeof(FlaxEditor.CustomEditors.Editors.CultureInfoEditor))] public string Locale; + [EditorOrder(5), EditorDisplay("General"), Tooltip("The fallback language table to use for missing keys. Eg. table for 'en-GB' can point to 'en' as a fallback to prevent problem of missing localized strings.")] + [AssetReference(true)] + public LocalizedStringTable FallbackTable; + [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.")] [Collection(Spacing = 10, OverrideEditorTypeName = "FlaxEditor.Windows.Assets.LocalizedStringTableWindow+EntryEditor")] public Dictionary Entries; @@ -174,6 +178,7 @@ namespace FlaxEditor.Windows.Assets return; _asset.Locale = _proxy.Locale; + _asset.FallbackTable = _proxy.FallbackTable; _asset.Entries = _proxy.Entries; if (_asset.Save(_item.Path)) { @@ -200,6 +205,7 @@ namespace FlaxEditor.Windows.Assets _proxy = new Proxy { Locale = _asset.Locale, + FallbackTable = _asset.FallbackTable, Entries = _asset.Entries, }; _presenter.Select(_proxy); diff --git a/Source/Engine/Localization/Localization.cpp b/Source/Engine/Localization/Localization.cpp index 7ac0a09ed..e5c035e3a 100644 --- a/Source/Engine/Localization/Localization.cpp +++ b/Source/Engine/Localization/Localization.cpp @@ -258,20 +258,35 @@ void Localization::SetCurrentLanguageCulture(const CultureInfo& value) String Localization::GetString(const String& id, const String& fallback) { - String result; + const String* result = nullptr; for (auto& e : Instance.LocalizedStringTables) { const auto table = e.Get(); const auto messages = table ? table->Entries.TryGet(id) : nullptr; if (messages && messages->Count() != 0) { - result = messages->At(0); + result = &messages->At(0); break; } } - if (result.IsEmpty()) - result = fallback; - return result; + if (!result) + { + // Try using fallback tables + for (auto& e : Instance.LocalizedStringTables) + { + const auto table = e.Get(); + const auto fallbackTable = table ? table->FallbackTable.Get() : nullptr; + const auto messages = fallbackTable ? fallbackTable->Entries.TryGet(id) : nullptr; + if (messages && messages->Count() != 0) + { + result = &messages->At(0); + break; + } + } + } + if (!result) + result = &fallback; + return *result; } String Localization::GetPluralString(const String& id, int32 n, const String& fallback) @@ -289,6 +304,21 @@ String Localization::GetPluralString(const String& id, int32 n, const String& fa break; } } + if (!result) + { + // Try using fallback tables + for (auto& e : Instance.LocalizedStringTables) + { + const auto table = e.Get(); + const auto fallbackTable = table ? table->FallbackTable.Get() : nullptr; + const auto messages = fallbackTable ? fallbackTable->Entries.TryGet(id) : nullptr; + if (messages && messages->Count() > n) + { + result = &messages->At(n); + break; + } + } + } if (!result) result = &fallback; return String::Format(result->GetText(), n); diff --git a/Source/Engine/Localization/LocalizedStringTable.cpp b/Source/Engine/Localization/LocalizedStringTable.cpp index c705856f4..d7ba24175 100644 --- a/Source/Engine/Localization/LocalizedStringTable.cpp +++ b/Source/Engine/Localization/LocalizedStringTable.cpp @@ -60,6 +60,12 @@ bool LocalizedStringTable::Save(const StringView& path) writer.JKEY("Locale"); writer.String(Locale); + if (FallbackTable.GetID().IsValid()) + { + writer.JKEY("FallbackTable"); + writer.Guid(FallbackTable.GetID()); + } + writer.JKEY("Entries"); writer.StartObject(); for (auto& e : Entries) @@ -102,6 +108,9 @@ Asset::LoadResult LocalizedStringTable::loadAsset() return result; JsonTools::GetString(Locale, *Data, "Locale"); + Guid fallbackTable = Guid::Empty; + JsonTools::GetGuid(fallbackTable, *Data, "FallbackTable"); + FallbackTable = fallbackTable; const auto entriesMember = SERIALIZE_FIND_MEMBER((*Data), "Entries"); if (entriesMember != Data->MemberEnd() && entriesMember->value.IsObject()) { @@ -134,5 +143,6 @@ void LocalizedStringTable::unload(bool isReloading) JsonAssetBase::unload(isReloading); Locale.Clear(); + FallbackTable = nullptr; Entries.Clear(); } diff --git a/Source/Engine/Localization/LocalizedStringTable.h b/Source/Engine/Localization/LocalizedStringTable.h index 1c1b8d795..a9d11b454 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/Scripting/SoftObjectReference.h" /// /// Contains localized strings table for a given culture. @@ -14,17 +15,24 @@ API_CLASS(NoSpawn) class FLAXENGINE_API LocalizedStringTable : public JsonAssetB { DECLARE_ASSET_HEADER(LocalizedStringTable); public: + /// /// The locale of the localized string table (eg. pl-PL). /// API_FIELD() String Locale; + /// + /// The fallback language table to use for missing keys. Eg. table for 'en-GB' can point to 'en' as a fallback to prevent problem of missing localized strings. + /// + API_FIELD() SoftObjectReference FallbackTable; + /// /// The string table. Maps the message id into the localized text. For plural messages the list contains separate items for value numbers. /// API_FIELD() Dictionary> Entries; public: + /// /// Adds the localized string to the table. ///