Browse Source

Localization: fixes, adding comments, reworking samples, partial russification of editor

Ivan K 10 years ago
parent
commit
d540f79eec

+ 4 - 0
Docs/Reference.dox

@@ -392,6 +392,10 @@ When writing new resource types, the background loading mechanism requires imple
 
 
 If a resource depends on other resources, writing efficient threaded loading for it can be hard, as calling GetResource() is not allowed inside BeginLoad() when background loading. There are a few options: it is allowed to queue new background load requests by calling BackgroundLoadResource() within BeginLoad(), or if the needed resource does not need to be permanently stored in the cache and is safe to load outside the main thread (for example Image or XMLFile, which do not possess any GPU-side data), \ref ResourceCache::GetTempResource "GetTempResource()" can be called inside BeginLoad.
 If a resource depends on other resources, writing efficient threaded loading for it can be hard, as calling GetResource() is not allowed inside BeginLoad() when background loading. There are a few options: it is allowed to queue new background load requests by calling BackgroundLoadResource() within BeginLoad(), or if the needed resource does not need to be permanently stored in the cache and is safe to load outside the main thread (for example Image or XMLFile, which do not possess any GPU-side data), \ref ResourceCache::GetTempResource "GetTempResource()" can be called inside BeginLoad.
 
 
+\page Localization Localization
+
+TODO About Localization.
+
 \page Scripting Scripting
 \page Scripting Scripting
 
 
 To enable AngelScript scripting support, the Script subsystem needs to be created and registered after initializing the Engine. This is accomplished by the following code, seen eg. in Tools/Urho3DPlayer/Urho3DPlayer.cpp:
 To enable AngelScript scripting support, the Script subsystem needs to be created and registered after initializing the Engine. This is accomplished by the following code, seen eg. in Tools/Urho3DPlayer/Urho3DPlayer.cpp:

+ 1 - 0
Docs/Urho3D.dox

@@ -25,6 +25,7 @@ For further reference, see:
 \ref MainLoop "Engine initialization and main loop" <br>
 \ref MainLoop "Engine initialization and main loop" <br>
 \ref SceneModel "Scene model" <br>
 \ref SceneModel "Scene model" <br>
 \ref Resources "Resources" <br>
 \ref Resources "Resources" <br>
+\ref Localization "Localization" <br>
 \ref Scripting "Scripting" <br>
 \ref Scripting "Scripting" <br>
 \ref LuaScripting "Lua scripting" <br>
 \ref LuaScripting "Lua scripting" <br>
 \ref Rendering "Rendering" <br>
 \ref Rendering "Rendering" <br>

+ 72 - 63
Source/Samples/40_Localization/L10n.cpp

@@ -35,6 +35,7 @@
 #include <Urho3D/UI/Text3D.h>
 #include <Urho3D/UI/Text3D.h>
 #include <Urho3D/UI/UIEvents.h>
 #include <Urho3D/UI/UIEvents.h>
 #include <Urho3D/Graphics/Zone.h>
 #include <Urho3D/Graphics/Zone.h>
+#include <Urho3D/Resource/ResourceEvents.h>
 
 
 #include "L10n.h"
 #include "L10n.h"
 
 
@@ -55,70 +56,75 @@ void L10n::Start()
     // Enable OS cursor
     // Enable OS cursor
     GetSubsystem<Input>()->SetMouseVisible(true);
     GetSubsystem<Input>()->SetMouseVisible(true);
 
 
-    // Load strings
-    Localization* l10n = GetSubsystem<Localization>();
-    l10n->LoadJSONFile("StringsEnRu.json");
-    l10n->LoadJSONFile("StringsDe.json");
-    SubscribeToEvent(E_CHANGELANGUAGE, HANDLER(L10n, HandleChangeLanguage));
+    // Load strings from JSON files and subscribe to the change language event
+    InitLocalizationSystem();
 
 
+    // Init the 3D space
     CreateScene();
     CreateScene();
+
+    // Init the user interface
     CreateGUI();
     CreateGUI();
+}
 
 
-    SubscribeToEvent(E_UPDATE, HANDLER(L10n, HandleUpdate));
+void L10n::InitLocalizationSystem()
+{
+    Localization* l10n = GetSubsystem<Localization>();
+    // JSON files must be in UTF8 encoding without BOM
+    // The first founded language will be set as current
+    l10n->LoadJSONFile("StringsEnRu.json");
+    // You can load multiple files
+    l10n->LoadJSONFile("StringsDe.json");
+    // Hook up to the change language
+    SubscribeToEvent(E_CHANGELANGUAGE, HANDLER(L10n, HandleChangeLanguage));
 }
 }
 
 
 void L10n::CreateGUI()
 void L10n::CreateGUI()
 {
 {
+    // Get localization subsystem
     Localization* l10n = GetSubsystem<Localization>();
     Localization* l10n = GetSubsystem<Localization>();
+    
     ResourceCache* cache = GetSubsystem<ResourceCache>();
     ResourceCache* cache = GetSubsystem<ResourceCache>();
-
     UIElement* root = GetSubsystem<UI>()->GetRoot();
     UIElement* root = GetSubsystem<UI>()->GetRoot();
-    // Load the style sheet from xml
     root->SetDefaultStyle(cache->GetResource<XMLFile>("UI/DefaultStyle.xml"));
     root->SetDefaultStyle(cache->GetResource<XMLFile>("UI/DefaultStyle.xml"));
 
 
-    // Create the Window and add it to the UI's root node
     Window* window = new Window(context_);
     Window* window = new Window(context_);
     root->AddChild(window);
     root->AddChild(window);
-
-    // Set Window size and layout settings
     window->SetMinSize(384, 192);
     window->SetMinSize(384, 192);
     window->SetLayout(LM_VERTICAL, 6, IntRect(6, 6, 6, 6));
     window->SetLayout(LM_VERTICAL, 6, IntRect(6, 6, 6, 6));
     window->SetAlignment(HA_CENTER, VA_CENTER);
     window->SetAlignment(HA_CENTER, VA_CENTER);
-    window->SetName("Window");
-
-    // Create Window 'titlebar' container
-    UIElement* titleBar = new UIElement(context_);
-    titleBar->SetMinSize(0, 24);
-    titleBar->SetVerticalAlignment(VA_TOP);
-    titleBar->SetLayoutMode(LM_HORIZONTAL);
+    window->SetStyleAuto();
 
 
-    // Create the Window title Text
     Text* windowTitle = new Text(context_);
     Text* windowTitle = new Text(context_);
     windowTitle->SetName("WindowTitle");
     windowTitle->SetName("WindowTitle");
-    windowTitle->SetText(l10n->Get("title") + " (" + l10n->GetLanguage() + ")");
-
-    // Add text to the title bar
-    titleBar->AddChild(windowTitle);
-
-    // Add the title bar to the Window
-    window->AddChild(titleBar);
-
-    // Apply styles
-    window->SetStyleAuto();
     windowTitle->SetStyleAuto();
     windowTitle->SetStyleAuto();
+    window->AddChild(windowTitle);
 
 
+    // In this place the current language is "en" because it was found first when loading the JSON files
+    String langName = l10n->GetLanguage();
+    // Languages are numbered in the loading order
+    int langIndex = l10n->GetLanguageIndex(); // == 0 at the beginning
+    // Get string with identifier "title" in the current language
+    String localizedString = l10n->Get("title");
+    // Localization::Get returns String::EMPTY if the id is empty.
+    // Localization::Get returns the id if translation is not found and will be added a warning into the log.
+
+    windowTitle->SetText(localizedString + " (" + String(langIndex) + " " + langName + ")");
 
 
-    // Create buttons
     Button* b = new Button(context_);
     Button* b = new Button(context_);
     window->AddChild(b);
     window->AddChild(b);
     b->SetStyle("Button");
     b->SetStyle("Button");
     b->SetMinHeight(24);
     b->SetMinHeight(24);
-    Text* t = b->CreateChild<Text>("ButtonTextLanguage");
+    
+    Text* t = b->CreateChild<Text>("ButtonTextChangeLang");
+    // The showing text value will automatically change when language is changed
+    t->SetAutoLocalizable(true);
+    // The text value used as a string identifier in this mode.
+    // Remember that a letter case of the id and of the lang name is important.
+    t->SetText("Press this button");
+    
     t->SetAlignment(HA_CENTER, VA_CENTER);
     t->SetAlignment(HA_CENTER, VA_CENTER);
     t->SetStyle("Text");
     t->SetStyle("Text");
-    t->SetAutoLocalizable(true);
-    t->SetText("lang");
-    SubscribeToEvent(b, E_RELEASED, HANDLER(L10n, HandleLangButtonPressed));
+    SubscribeToEvent(b, E_RELEASED, HANDLER(L10n, HandleChangeLangButtonPressed));
 
 
     b = new Button(context_);
     b = new Button(context_);
     window->AddChild(b);
     window->AddChild(b);
@@ -127,47 +133,52 @@ void L10n::CreateGUI()
     t = b->CreateChild<Text>("ButtonTextQuit");
     t = b->CreateChild<Text>("ButtonTextQuit");
     t->SetAlignment(HA_CENTER, VA_CENTER);
     t->SetAlignment(HA_CENTER, VA_CENTER);
     t->SetStyle("Text");
     t->SetStyle("Text");
+    
+    // Manually set text in the current language
     t->SetText(l10n->Get("quit"));
     t->SetText(l10n->Get("quit"));
+    
     SubscribeToEvent(b, E_RELEASED, HANDLER(L10n, HandleQuitButtonPressed));
     SubscribeToEvent(b, E_RELEASED, HANDLER(L10n, HandleQuitButtonPressed));
-
 }
 }
 
 
 void L10n::CreateScene()
 void L10n::CreateScene()
 {
 {
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
+    // Get localization subsystem
     Localization* l10n = GetSubsystem<Localization>();
     Localization* l10n = GetSubsystem<Localization>();
+
+    ResourceCache* cache = GetSubsystem<ResourceCache>();
     scene_ = new Scene(context_);
     scene_ = new Scene(context_);
     scene_->CreateComponent<Octree>();
     scene_->CreateComponent<Octree>();
     
     
-    Node* zoneNode = scene_->CreateChild("Zone");
-    Zone* zone = zoneNode->CreateComponent<Zone>();
+    Zone* zone = scene_->CreateComponent<Zone>();
     zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
     zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f));
-    zone->SetAmbientColor(Color(0.15f, 0.15f, 0.15f));
-    zone->SetFogColor(Color(0.5f, 0.5f, 0.7f));
-    zone->SetFogStart(100.0f);
-    zone->SetFogEnd(300.0f);
+    zone->SetAmbientColor(Color(0.5f, 0.5f, 0.5f));
+    zone->SetFogColor(Color(0.4f, 0.5f, 0.8f));
+    zone->SetFogStart(1.0f);
+    zone->SetFogEnd(100.0f);
 
 
     Node* planeNode = scene_->CreateChild("Plane");
     Node* planeNode = scene_->CreateChild("Plane");
-    planeNode->SetScale(Vector3(200.0f, 1.0f, 200.0f));
+    planeNode->SetScale(Vector3(300.0f, 1.0f, 300.0f));
     StaticModel* planeObject = planeNode->CreateComponent<StaticModel>();
     StaticModel* planeObject = planeNode->CreateComponent<StaticModel>();
     planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl"));
     planeObject->SetModel(cache->GetResource<Model>("Models/Plane.mdl"));
     planeObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml"));
     planeObject->SetMaterial(cache->GetResource<Material>("Materials/StoneTiled.xml"));
 
 
     Node* lightNode = scene_->CreateChild("DirectionalLight");
     Node* lightNode = scene_->CreateChild("DirectionalLight");
-    lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f)); // The direction vector does not need to be normalized
+    lightNode->SetDirection(Vector3(0.6f, -1.0f, 0.8f));
     Light* light = lightNode->CreateComponent<Light>();
     Light* light = lightNode->CreateComponent<Light>();
     light->SetLightType(LIGHT_DIRECTIONAL);
     light->SetLightType(LIGHT_DIRECTIONAL);
+    light->SetColor(Color(0.8f, 0.8f, 0.8f));
 
 
     cameraNode_ = scene_->CreateChild("Camera");
     cameraNode_ = scene_->CreateChild("Camera");
     cameraNode_->CreateComponent<Camera>();
     cameraNode_->CreateComponent<Camera>();
-
-    // Set an initial position for the camera scene node above the plane
     cameraNode_->SetPosition(Vector3(0.0f, 10.0f, -30.0f));
     cameraNode_->SetPosition(Vector3(0.0f, 10.0f, -30.0f));
 
 
     Node* text3DNode = scene_->CreateChild("Text3D");
     Node* text3DNode = scene_->CreateChild("Text3D");
-    text3DNode->SetPosition(Vector3(0.0f, 0.0f, 30.0f));
+    text3DNode->SetPosition(Vector3(0.0f, 0.1f, 30.0f));
     Text3D* text3D = text3DNode->CreateComponent<Text3D>();
     Text3D* text3D = text3DNode->CreateComponent<Text3D>();
-    text3D->SetText(l10n->Get("long text"));
+
+    // Manually set text in the current language.
+    text3D->SetText(l10n->Get("lang"));
+
     text3D->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 30);
     text3D->SetFont(cache->GetResource<Font>("Fonts/Anonymous Pro.ttf"), 30);
     text3D->SetColor(Color::BLACK);
     text3D->SetColor(Color::BLACK);
     text3D->SetAlignment(HA_CENTER, VA_BOTTOM);
     text3D->SetAlignment(HA_CENTER, VA_BOTTOM);
@@ -176,35 +187,28 @@ void L10n::CreateScene()
     Renderer* renderer = GetSubsystem<Renderer>();
     Renderer* renderer = GetSubsystem<Renderer>();
     SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
     SharedPtr<Viewport> viewport(new Viewport(context_, scene_, cameraNode_->GetComponent<Camera>()));
     renderer->SetViewport(0, viewport);
     renderer->SetViewport(0, viewport);
+
+    SubscribeToEvent(E_UPDATE, HANDLER(L10n, HandleUpdate));
 }
 }
 
 
 void L10n::HandleUpdate(StringHash eventType, VariantMap& eventData)
 void L10n::HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
     using namespace Update;
     using namespace Update;
     float timeStep = eventData[P_TIMESTEP].GetFloat();
     float timeStep = eventData[P_TIMESTEP].GetFloat();
-    MoveCamera(timeStep);
-}
-
-void L10n::MoveCamera(float timeStep)
-{
     Input* input = GetSubsystem<Input>();
     Input* input = GetSubsystem<Input>();
-
     const float MOUSE_SENSITIVITY = 0.1f;
     const float MOUSE_SENSITIVITY = 0.1f;
-
-    // Use this frame's mouse motion to adjust camera node yaw and pitch. Clamp the pitch between -90 and 90 degrees
     IntVector2 mouseMove = input->GetMouseMove();
     IntVector2 mouseMove = input->GetMouseMove();
     yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
     yaw_ += MOUSE_SENSITIVITY * mouseMove.x_;
     pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
     pitch_ += MOUSE_SENSITIVITY * mouseMove.y_;
     pitch_ = Clamp(pitch_, -90.0f, 90.0f);
     pitch_ = Clamp(pitch_, -90.0f, 90.0f);
-
-    // Construct new orientation for the camera scene node from yaw and pitch. Roll is fixed to zero
     cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
     cameraNode_->SetRotation(Quaternion(pitch_, yaw_, 0.0f));
 }
 }
 
 
-void L10n::HandleLangButtonPressed(StringHash eventType, VariantMap& eventData)
+void L10n::HandleChangeLangButtonPressed(StringHash eventType, VariantMap& eventData)
 {
 {
     Localization* l10n = GetSubsystem<Localization>();
     Localization* l10n = GetSubsystem<Localization>();
-    unsigned lang = l10n->GetLanguageIndex();
+    // Languages are numbered in the loading order
+    int lang = l10n->GetLanguageIndex();
     lang++;
     lang++;
     if (lang >= l10n->GetNumLanguages())
     if (lang >= l10n->GetNumLanguages())
         lang = 0;
         lang = 0;
@@ -216,15 +220,20 @@ void L10n::HandleQuitButtonPressed(StringHash eventType, VariantMap& eventData)
     engine_->Exit();
     engine_->Exit();
 }
 }
 
 
+// You can manually change texts, sprites and other aspects of the game when language is changed
 void L10n::HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 void L10n::HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 {
 {
     Localization* l10n = GetSubsystem<Localization>();
     Localization* l10n = GetSubsystem<Localization>();
-    ResourceCache* cache = GetSubsystem<ResourceCache>();
     UIElement* uiRoot = GetSubsystem<UI>()->GetRoot();
     UIElement* uiRoot = GetSubsystem<UI>()->GetRoot();
+
     Text* windowTitle = static_cast<Text*>(uiRoot->GetChild("WindowTitle", true));
     Text* windowTitle = static_cast<Text*>(uiRoot->GetChild("WindowTitle", true));
-    windowTitle->SetText(l10n->Get("title") + " (" + l10n->GetLanguage() + ")");
+    windowTitle->SetText(l10n->Get("title") + " (" + String(l10n->GetLanguageIndex()) + " " + l10n->GetLanguage() + ")");
+
     Text* buttonText = static_cast<Text*>(uiRoot->GetChild("ButtonTextQuit", true));
     Text* buttonText = static_cast<Text*>(uiRoot->GetChild("ButtonTextQuit", true));
     buttonText->SetText(l10n->Get("quit"));
     buttonText->SetText(l10n->Get("quit"));
+
     Text3D* text3D = scene_->GetChild("Text3D")->GetComponent<Text3D>();
     Text3D* text3D = scene_->GetChild("Text3D")->GetComponent<Text3D>();
-    text3D->SetText(l10n->Get("long text"));
+    text3D->SetText(l10n->Get("lang"));
+
+    // A text on the button "Press this button" changes automatically
 }
 }

+ 18 - 4
Source/Samples/40_Localization/L10n.h

@@ -24,22 +24,36 @@
 
 
 #include "Sample.h"
 #include "Sample.h"
 
 
+/// Localization example.
+/// This sample demonstrates:
+///     - Loading a collection of strings from JSON-files
+///     - Creating text elements that automatically translates itself by changing the language
+///     - The manually reaction to change language
 class L10n : public Sample
 class L10n : public Sample
 {
 {
     OBJECT(L10n);
     OBJECT(L10n);
 
 
 public:
 public:
+    /// Construct.
     L10n(Context* context);
     L10n(Context* context);
+
+    /// Setup after engine initialization and before running the main loop.
     virtual void Start();
     virtual void Start();
 
 
 private:
 private:
-    void CreateGUI();
+    // Load strings from JSON files and subscribe to the change language event
+    void InitLocalizationSystem();
+    // Init the 3D space
     void CreateScene();
     void CreateScene();
+    // Init the user interface
+    void CreateGUI();
+    /// Handle the logic update event.
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
     void HandleUpdate(StringHash eventType, VariantMap& eventData);
-    void MoveCamera(float timeStep);
-    void HandleLangButtonPressed(StringHash eventType, VariantMap& eventData);
-    void HandleQuitButtonPressed(StringHash eventType, VariantMap& eventData);
+    // Handle the change language event.
     void HandleChangeLanguage(StringHash eventType, VariantMap& eventData);
     void HandleChangeLanguage(StringHash eventType, VariantMap& eventData);
+    // Hook up to the buttons pressing
+    void HandleChangeLangButtonPressed(StringHash eventType, VariantMap& eventData);
+    void HandleQuitButtonPressed(StringHash eventType, VariantMap& eventData);
 };
 };
 
 
 
 

+ 11 - 2
Source/Urho3D/Resource/Localization.cpp

@@ -25,6 +25,7 @@
 #include "../Resource/Localization.h"
 #include "../Resource/Localization.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/ResourceCache.h"
 #include "../Resource/JSONFile.h"
 #include "../Resource/JSONFile.h"
+#include "../Resource/ResourceEvents.h"
 #include "../IO/Log.h"
 #include "../IO/Log.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
@@ -38,6 +39,10 @@ Localization::Localization(Context* context) :
 {
 {
 }
 }
 
 
+Localization::~Localization()
+{
+}
+
 int Localization::GetLanguageIndex(const String &language)
 int Localization::GetLanguageIndex(const String &language)
 {
 {
     assert(GetNumLanguages() > 0 && !language.Empty());
     assert(GetNumLanguages() > 0 && !language.Empty());
@@ -79,10 +84,14 @@ void Localization::SetLanguage(const String &language)
 
 
 String Localization::Get(const String &id)
 String Localization::Get(const String &id)
 {
 {
-    assert(!id.Empty());
+    if (id.Empty())
+        return String::EMPTY;
     String result = strings_[StringHash(GetLanguage())][StringHash(id)];
     String result = strings_[StringHash(GetLanguage())][StringHash(id)];
     if (result.Empty())
     if (result.Empty())
-        LOGWARNING("Localization::Get returns empty result: language=" + GetLanguage() + ", id=" + id);
+    {
+        LOGWARNING("Localization::Get(\"" + id + "\") not found translation, language=\"" + GetLanguage() + "\"");
+        return id;
+    }
     return result;
     return result;
 }
 }
 
 

+ 18 - 4
Source/Urho3D/Resource/Localization.h

@@ -28,31 +28,45 @@
 namespace Urho3D
 namespace Urho3D
 {
 {
 
 
-EVENT(E_CHANGELANGUAGE, ChangeLanguage)
-{
-}
-
+/// %Localization subsystem. Stores all the strings in all languages.
 class URHO3D_API Localization : public Object
 class URHO3D_API Localization : public Object
 {
 {
     OBJECT(Localization);
     OBJECT(Localization);
 
 
 public:
 public:
+    /// Construct.
     Localization(Context* context);
     Localization(Context* context);
+    /// Destruct. Free all resources.
+    virtual ~Localization();
+    /// Return the number of languages.
     int GetNumLanguages() const { return (int)languages_.Size(); }
     int GetNumLanguages() const { return (int)languages_.Size(); }
+    /// Return the index number of current language. The index is determined by the order of loading.
     int GetLanguageIndex() const { return languageIndex_; }
     int GetLanguageIndex() const { return languageIndex_; }
+    /// Return the index number of language. The index is determined by the order of loading.
     int GetLanguageIndex(const String &language);
     int GetLanguageIndex(const String &language);
+    /// Return the name of current language.
     String GetLanguage();
     String GetLanguage();
+    /// Return the name of language.
     String GetLanguage(int index);
     String GetLanguage(int index);
+    /// Set current language.
     void SetLanguage(int index);
     void SetLanguage(int index);
+    /// Set current language.
     void SetLanguage(const String &language);
     void SetLanguage(const String &language);
+    /// Return a string in the current language. Returns String::EMPTY if id is empty. Returns id if translation is not found.
     String Get(const String &id);
     String Get(const String &id);
+    /// Clear all loaded strings.
     void Reset();
     void Reset();
+    /// Load strings from JSONValue.
     void LoadJSON(const JSONValue &source);
     void LoadJSON(const JSONValue &source);
+    /// Load strings from JSONFile. The file should be UTF8 without BOM.
     void LoadJSONFile(const String &name);
     void LoadJSONFile(const String &name);
 
 
 private:
 private:
+    /// Language names.
     Vector<String> languages_;
     Vector<String> languages_;
+    /// Index of current language.
     int languageIndex_;
     int languageIndex_;
+    /// Storage strings: <Language <StringId, Value> >.
     HashMap<StringHash, HashMap<StringHash, String> > strings_;
     HashMap<StringHash, HashMap<StringHash, String> > strings_;
 };
 };
 
 

+ 5 - 0
Source/Urho3D/Resource/ResourceEvents.h

@@ -75,4 +75,9 @@ EVENT(E_RESOURCEBACKGROUNDLOADED, ResourceBackgroundLoaded)
     PARAM(P_RESOURCE, Resource);                    // Resource pointer
     PARAM(P_RESOURCE, Resource);                    // Resource pointer
 }
 }
 
 
+/// Language changed.
+EVENT(E_CHANGELANGUAGE, ChangeLanguage)
+{
+}
+
 }
 }

+ 5 - 12
Source/Urho3D/UI/Text.cpp

@@ -31,6 +31,7 @@
 #include "../UI/FontFace.h"
 #include "../UI/FontFace.h"
 #include "../UI/Text.h"
 #include "../UI/Text.h"
 #include "../Resource/Localization.h"
 #include "../Resource/Localization.h"
+#include "../Resource/ResourceEvents.h"
 
 
 #include "../DebugNew.h"
 #include "../DebugNew.h"
 
 
@@ -264,11 +265,8 @@ void Text::SetText(const String& text)
     if (autoLocalizable_)
     if (autoLocalizable_)
     {
     {
         stringId_ = text;
         stringId_ = text;
-        if (!stringId_.Empty())
-        {
-            Localization* l10n = GetSubsystem<Localization>();
-            text_ = l10n->Get(stringId_);
-        }
+        Localization* l10n = GetSubsystem<Localization>();
+        text_ = l10n->Get(stringId_);
     }
     }
     else
     else
     {
     {
@@ -315,11 +313,8 @@ void Text::SetAutoLocalizable(bool enable)
         if (enable)
         if (enable)
         {
         {
             stringId_ = text_;
             stringId_ = text_;
-            if (!stringId_.Empty())
-            {
-                Localization* l10n = GetSubsystem<Localization>();
-                text_ = l10n->Get(stringId_);
-            }
+            Localization* l10n = GetSubsystem<Localization>();
+            text_ = l10n->Get(stringId_);
             SubscribeToEvent(E_CHANGELANGUAGE, HANDLER(Text, HandleChangeLanguage));
             SubscribeToEvent(E_CHANGELANGUAGE, HANDLER(Text, HandleChangeLanguage));
         }
         }
         else
         else
@@ -336,8 +331,6 @@ void Text::SetAutoLocalizable(bool enable)
 
 
 void Text::HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 void Text::HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 {
 {
-    if (!autoLocalizable_ || stringId_.Empty())
-        return;
     Localization* l10n = GetSubsystem<Localization>();
     Localization* l10n = GetSubsystem<Localization>();
     text_ = l10n->Get(stringId_);
     text_ = l10n->Get(stringId_);
     DecodeToUnicode();
     DecodeToUnicode();

+ 3 - 4
Source/Urho3D/UI/Text.h

@@ -252,12 +252,11 @@ protected:
     PODVector<CharLocation> charLocations_;
     PODVector<CharLocation> charLocations_;
     /// The text will be automatically translated.
     /// The text will be automatically translated.
     bool autoLocalizable_;
     bool autoLocalizable_;
-    /// Storage string id.
+    /// Storage string id. Used when enabled autoLocalizable.
     String stringId_;
     String stringId_;
-    /// Handle change Language;
+    /// Handle change Language.
     void HandleChangeLanguage(StringHash eventType, VariantMap& eventData);
     void HandleChangeLanguage(StringHash eventType, VariantMap& eventData);
-
-private:
+    /// UTF8 to Unicode.
     void DecodeToUnicode();
     void DecodeToUnicode();
 };
 };
 
 

+ 304 - 1
bin/Data/EditorStrings.json

@@ -1,7 +1,310 @@
 {
 {
+	"Open scene...":{
+		"en":"Open scene...",
+		"ru":"Открыть сцену..."
+	},
+	"Save scene":{
+		"en":"Save scene",
+		"ru":"Сохранить сцену"
+	},
+	"Save scene as...":{
+		"en":"Save scene as...",
+		"ru":"Сохранить сцену как..."
+	},
+	"Open recent scene":{
+		"en":"Open recent scene",
+		"ru":"Открыть прежнюю сцену"
+	},
+	"Load node":{
+		"en":"Load node",
+		"ru":"Загрузить ноду"
+	},
+	"As replicated...":{
+		"en":"As replicated...",
+		"ru":"Реплицируемую..."
+	},
+	"As local...":{
+		"en":"As local...",
+		"ru":"Локальную..."
+	},
+	"Save node as...":{
+		"en":"Save node as...",
+		"ru":"Сохранить ноду как..."
+	},
+	"Import model...":{
+		"en":"Import model...",
+		"ru":"Импортировать модель..."
+	},
+	"Import scene...":{
+		"en":"Import scene...",
+		"ru":"Импортировать сцену..."
+	},
+	"Run script...":{
+		"en":"Run script...",
+		"ru":"Запустить скрипт..."
+	},
+	"Set resource path...":{
+		"en":"Set resource path...",
+		"ru":"Указать путь к ресурсам..."
+	},
+	"Exit":{
+		"en":"Exit",
+		"ru":"Выход"
+	},
+	"Edit":{
+		"en":"Edit",
+		"ru":"Редактирование"
+	},
+	"Undo":{
+		"en":"Undo",
+		"ru":"Отменить"
+	},
+	"Redo":{
+		"en":"Redo",
+		"ru":"Повторить"
+	},
+	"Cut":{
+		"en":"Cut",
+		"ru":"Вырезать"
+	},
+	"Duplicate":{
+		"en":"Duplicate",
+		"ru":"Дублировать"
+	},
+	"Copy":{
+		"en":"Copy",
+		"ru":"Копировать"
+	},
+	"Paste":{
+		"en":"Paste",
+		"ru":"Вставить"
+	},
+	"Delete":{
+		"en":"Delete",
+		"ru":"Удалить"
+	},
+	"Select all":{
+		"en":"Select all",
+		"ru":"Выделить всё"
+	},
+	"Deselect all":{
+		"en":"Deselect all",
+		"ru":"Снять все выделения"
+	},
+	"Reset to default":{
+		"en":"Reset to default",
+		"ru":"Сбросить"
+	},
+	"Reset position":{
+		"en":"Reset position",
+		"ru":"Сбросить положение"
+	},
+	"Reset rotation":{
+		"en":"Reset rotation",
+		"ru":"Сбросить поворот"
+	},
+	"Reset scale":{
+		"en":"Reset scale",
+		"ru":"Сбросить масштаб"
+	},
+	"Enable/disable":{
+		"en":"Enable/disable",
+		"ru":"Включить/Отключить"
+	},
+	"Unparent":{
+		"en":"Unparent",
+		"ru":"Разорвать родительскую связь"
+	},
+	"Toggle update":{
+		"en":"Toggle update",
+		"ru":"Вкл/откл обновление"
+	},
+	"Stop test animation":{
+		"en":"Stop test animation",
+		"ru":"Остановить тестовую анимацию"
+	},
+	"Rebuild navigation data":{
+		"en":"Rebuild navigation data",
+		"ru":"Реконструировать навигационные данные"
+	},
+	"Add children to SM-group":{
+		"en":"Add children to SM-group",
+		"ru":"Добавить потомка к StaticModelGroup"
+	},
+	"Set children as spline path":{
+		"en":"Set children as spline path",
+		"ru":"Установить потомка как сплайновый путь"
+	},
+	"Non-cyclic":{
+		"en":"Non-cyclic",
+		"ru":"Нецикличный"
+	},
+	"Cyclic":{
+		"en":"Cyclic",
+		"ru":"Цикличный"
+	},
+	"Create":{
+		"en":"Create",
+		"ru":"Создать"
+	},
+	"Replicated node":{
+		"en":"Replicated node",
+		"ru":"Реплицируемую ноду"
+	},
+	"Local node":{
+		"en":"Local node",
+		"ru":"Локальную ноду"
+	},
+	"Component":{
+		"en":"Component",
+		"ru":"Компонент"
+	},
+	"Audio":{
+		"en":"Audio",
+		"ru":"Аудио"
+	},
+	"Geometry":{
+		"en":"Geometry",
+		"ru":"Геометрия"
+	},
+	"Logic":{
+		"en":"Logic",
+		"ru":"Логика"
+	},
+	"Navigation":{
+		"en":"Navigation",
+		"ru":"Навигация"
+	},
+	"Network":{
+		"en":"Network",
+		"ru":"Сеть"
+	},
+	"Physics":{
+		"en":"Physics",
+		"ru":"Физика"
+	},
+	"Scene":{
+		"en":"Scene",
+		"ru":"Сцена"
+	},
+	"Subsystem":{
+		"en":"Subsystem",
+		"ru":"Подсистема"
+	},
+	"Urho2D":{
+		"en":"Urho2D",
+		"ru":"Urho2D"
+	},
+	"Builtin object":{
+		"en":"Builtin object",
+		"ru":"Встроенный объект"
+	},
+	"UI-element":{
+		"en":"UI-element",
+		"ru":"Элемент интерфейса"
+	},
+	"UI-layout":{
+		"en":"UI-layout",
+		"ru":"Разметка интерфейса"
+	},
+	"Open UI-layout...":{
+		"en":"Open UI-layout...",
+		"ru":"Открыть разметку..."
+	},
+	"Save UI-layout":{
+		"en":"Save UI-layout",
+		"ru":"Сохранить разметку"
+	},
+	"Save UI-layout as...":{
+		"en":"Save UI-layout as...",
+		"ru":"Сохранить разметку как..."
+	},
+	"Close UI-layout":{
+		"en":"Close UI-layout",
+		"ru":"Закрыть разметку"
+	},
+	"Close all UI-layouts":{
+		"en":"Close all UI-layouts",
+		"ru":"Закрыть все разметки"
+	},
+	"Load child element...":{
+		"en":"Load child element...",
+		"ru":"Загрузить дочерний элемент..."
+	},
+	"Save child element as...":{
+		"en":"Save child element as...",
+		"ru":"Сохранить дочерний элемент как..."
+	},
+	"Set default style...":{
+		"en":"Set default style...",
+		"ru":"Установить стандартный стиль..."
+	},
+	"View":{
+		"en":"View",
+		"ru":"Вид"
+	},
+	"Hierarchy":{
+		"en":"Hierarchy",
+		"ru":"Иерархия"
+	},
+	"Attribute inspector":{
+		"en":"Attribute inspector",
+		"ru":"Аттрибуты"
+	},
+	"Material editor":{
+		"en":"Material editor",
+		"ru":"Редактор материалов"
+	},
+	"Particle editor":{
+		"en":"Particle editor",
+		"ru":"Редактор частиц"
+	},
+	"Spawn editor":{
+		"en":"Spawn editor",
+		"ru":"Редактор спауна"
+	},
+	"Sound Type editor":{
+		"en":"Sound Type editor",
+		"ru":"Тип звука"
+	},
+	"Editor settings":{
+		"en":"Editor settings",
+		"ru":"Настройки редактора"
+	},
+	"Editor preferences":{
+		"en":"Editor preferences",
+		"ru":"Установки редактора"
+	},
+	"Hide editor":{
+		"en":"Hide editor",
+		"ru":"Скрыть редактор"
+	},
 	"Resource Browser":{
 	"Resource Browser":{
 		"en":"Resource Browser",
 		"en":"Resource Browser",
 		"ru":"Браузер ресурсов"
 		"ru":"Браузер ресурсов"
+	},
+	"New scene":{
+		"en":"New scene",
+		"ru":"Создать новую сцену"
+	},
+	"Files left to scan: ":{
+		"en":"Files left to scan: ",
+		"ru":"Файлов до конца сканирования: "
+	},
+	"Scan complete":{
+		"en":"Scan complete",
+		"ru":"Сканирование окончено"
+	},
+	"Root":{
+		"en":"Root",
+		"ru":"Корень"
+	},
+	"Showing files: ":{
+		"en":"Showing files: ",
+		"ru":"Показано файлов: "
+	},
+	"File":{
+		"en":"File",
+		"ru":"Файл"
 	}
 	}
 }
 }
-

+ 153 - 31
bin/Data/LuaScripts/40_Localization.lua

@@ -1,54 +1,176 @@
+-- Localization example.
+-- This sample demonstrates:
+--     - Loading a collection of strings from JSON-files
+--     - Creating text elements that automatically translates itself by changing the language
+--     - The manually reaction to change language
+
 require "LuaScripts/Utilities/Sample"
 require "LuaScripts/Utilities/Sample"
 
 
 function Start()
 function Start()
     -- Execute the common startup for samples
     -- Execute the common startup for samples
     SampleStart()
     SampleStart()
+    
+    -- Enable OS cursor
+    input.mouseVisible = true
+    
+    -- Load strings from JSON files and subscribe to the change language event
+    InitLocalizationSystem()
+
+    -- Init the 3D space
+    CreateScene()
+
+    -- Init the user interface
+    CreateGUI()
+end
 
 
+function InitLocalizationSystem()
+    -- JSON files must be in UTF8 encoding without BOM
+    -- The first founded language will be set as current
     localization:LoadJSONFile("StringsEnRu.json")
     localization:LoadJSONFile("StringsEnRu.json")
+    -- You can load multiple files
     localization:LoadJSONFile("StringsDe.json")
     localization:LoadJSONFile("StringsDe.json")
-    -- Create "Hello World" Text
-    CreateText()
-
-    -- Finally, hook-up this HelloWorld instance to handle update events
-    SubscribeToEvents()
+    -- Hook up to the change language
+    SubscribeToEvent("ChangeLanguage", "HandleChangeLanguage")
 end
 end
 
 
-function CreateText()
-    -- Construct new Text object
-    local helloText = Text:new()
+function CreateGUI()
+    ui.root.defaultStyle = cache:GetResource("XMLFile", "UI/DefaultStyle.xml")
+
+    local window = Window:new()
+    ui.root:AddChild(window)
+    window:SetMinSize(384, 192)
+    window:SetLayout(LM_VERTICAL, 6, IntRect(6, 6, 6, 6))
+    window:SetAlignment(HA_CENTER, VA_CENTER)
+    window:SetStyleAuto()
 
 
-    -- Set String to display
-    -- helloText.text = localization:Get("lang")
-    helloText.text = "lang"
-    helloText.autoLocalizable = true
+    local windowTitle = Text:new()
+    windowTitle.name = "WindowTitle"
+    windowTitle:SetStyleAuto()
+    window:AddChild(windowTitle)
 
 
-    -- Set font and text color
-    helloText:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30)
-    helloText.color = Color(0.0, 1.0, 0.0)
+    -- In this place the current language is "en" because it was found first when loading the JSON files
+    local langName = localization.language
+    -- Languages are numbered in the loading order
+    local langIndex = localization.languageIndex -- == 0 at the beginning
+    -- Get string with identifier "title" in the current language
+    local localizedString = localization:Get("title")
+    -- Localization:Get returns String.EMPTY if the id is empty.
+    -- Localization:Get returns the id if translation is not found and will be added a warning into the log.
 
 
-    -- Align Text center-screen
-    helloText.horizontalAlignment = HA_CENTER
-    helloText.verticalAlignment = VA_CENTER
+    windowTitle.text = localizedString .. " (" .. langIndex .. " " .. langName .. ")"
 
 
-    -- Add Text instance to the UI root element
-    ui.root:AddChild(helloText)
+    local b = Button:new()
+    window:AddChild(b)
+    b:SetStyle("Button")
+    b.minHeight = 24
+    
+    local t = b:CreateChild("Text", "ButtonTextChangeLang")
+    -- The showing text value will automatically change when language is changed
+    t.autoLocalizable = true
+    -- The text value used as a string identifier in this mode.
+    -- Remember that a letter case of the id and of the lang name is important.
+    t.text = "Press this button"
+    
+    t:SetAlignment(HA_CENTER, VA_CENTER)
+    t:SetStyle("Text")
+    SubscribeToEvent(b, "Released", "HandleChangeLangButtonPressed")
+
+    b = Button:new()
+    window:AddChild(b)
+    b:SetStyle("Button")
+    b.minHeight = 24
+    t = b:CreateChild("Text", "ButtonTextQuit")
+    t:SetAlignment(HA_CENTER, VA_CENTER)
+    t:SetStyle("Text")
+    
+    -- Manually set text in the current language
+    t.text = localization:Get("quit")
+    
+    SubscribeToEvent(b, "Released", "HandleQuitButtonPressed")
 end
 end
 
 
-function SubscribeToEvents()
-    -- Subscribe HandleUpdate() function for processing update events
+function CreateScene()
+    scene_ = Scene:new()
+    scene_:CreateComponent("Octree")
+    
+    local zone = scene_:CreateComponent("Zone")
+    zone.boundingBox = BoundingBox:new(-1000.0, 1000.0)
+    zone.ambientColor = Color:new(0.5, 0.5, 0.5)
+    zone.fogColor = Color:new(0.4, 0.5, 0.8)
+    zone.fogStart = 1.0
+    zone.fogEnd = 100.0
+
+    local planeNode = scene_:CreateChild("Plane")
+    planeNode.scale = Vector3:new(300.0, 1.0, 300.0)
+    local planeObject = planeNode:CreateComponent("StaticModel")
+    planeObject.model = cache:GetResource("Model", "Models/Plane.mdl")
+    planeObject.material = cache:GetResource("Material", "Materials/StoneTiled.xml")
+
+    local lightNode = scene_:CreateChild("DirectionalLight")
+    lightNode.direction = Vector3:new(0.6, -1.0, 0.8)
+    local light = lightNode:CreateComponent("Light")
+    light.lightType = LIGHT_DIRECTIONAL
+    light.color = Color:new(0.8, 0.8, 0.8)
+
+    cameraNode = scene_:CreateChild("Camera")
+    cameraNode:CreateComponent("Camera")
+    cameraNode.position = Vector3:new(0.0, 10.0, -30.0)
+
+    local text3DNode = scene_:CreateChild("Text3D")
+    text3DNode.position = Vector3:new(0.0, 0.1, 30.0)
+    text3DNode:SetScale(15)
+    local text3D = text3DNode:CreateComponent("Text3D")
+
+    -- Manually set text in the current language.
+    text3D.text = localization:Get("lang")
+
+    text3D:SetFont(cache:GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30)
+    text3D.color = Color.BLACK
+    text3D:SetAlignment(HA_CENTER, VA_BOTTOM)
+
+    local viewport = Viewport:new(scene_, cameraNode:GetComponent("Camera"))
+    renderer:SetViewport(0, viewport)
+
     SubscribeToEvent("Update", "HandleUpdate")
     SubscribeToEvent("Update", "HandleUpdate")
 end
 end
 
 
 function HandleUpdate(eventType, eventData)
 function HandleUpdate(eventType, eventData)
-    -- Do nothing for now, could be extended to eg. animate the display
+    local timeStep = eventData:GetFloat("TimeStep")
+    local MOUSE_SENSITIVITY = 0.1
+    local mouseMove = input.mouseMove
+    yaw = yaw + MOUSE_SENSITIVITY * mouseMove.x
+    pitch = pitch + MOUSE_SENSITIVITY * mouseMove.y
+    pitch = Clamp(pitch, -90.0, 90.0)
+    cameraNode.rotation = Quaternion(pitch, yaw, 0.0)
 end
 end
 
 
--- Create XML patch instructions for screen joystick layout specific to this sample app
-function GetScreenJoystickPatchString()
-    return
-        "<patch>" ..
-        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" ..
-        "        <attribute name=\"Is Visible\" value=\"false\" />" ..
-        "    </add>" ..
-        "</patch>"
+function HandleChangeLangButtonPressed(eventType, eventData)
+    -- Languages are numbered in the loading order
+    local lang = localization.languageIndex
+    lang = lang + 1
+    if lang >= localization.numLanguages then
+        lang = 0
+    end
+    localization:SetLanguage(lang)
 end
 end
+
+function HandleQuitButtonPressed(eventType, eventData)
+    engine:Exit()
+end
+
+-- You can manually change texts, sprites and other aspects of the game when language is changed
+function HandleChangeLanguage(eventType, eventData)
+    local windowTitle = ui.root:GetChild("WindowTitle", true);
+    windowTitle.text = localization:Get("title") .. " (" ..
+                           localization.languageIndex .. " " ..
+                           localization.language .. ")"
+
+    local buttonText = ui.root:GetChild("ButtonTextQuit", true)
+    buttonText.text = localization:Get("quit")
+
+    local text3D = scene_:GetChild("Text3D"):GetComponent("Text3D")
+    text3D.text = localization:Get("lang")
+
+    -- A text on the button "Press this button" changes automatically
+end
+

+ 150 - 41
bin/Data/Scripts/40_Localization.as

@@ -1,75 +1,184 @@
-#include "Scripts/Utilities/Sample.as"
+// Localization example.
+// This sample demonstrates:
+//     - Loading a collection of strings from JSON-files
+//     - Creating text elements that automatically translates itself by changing the language
+//     - The manually reaction to change language
 
 
-Text@ helloText;
+#include "Scripts/Utilities/Sample.as"
 
 
 void Start()
 void Start()
 {
 {
     // Execute the common startup for samples
     // Execute the common startup for samples
     SampleStart();
     SampleStart();
     
     
+    // Enable OS cursor
     input.mouseVisible = true;
     input.mouseVisible = true;
     
     
+    // Load strings from JSON files and subscribe to the change language event
+    InitLocalizationSystem();
+
+    // Init the 3D space
+    CreateScene();
+
+    // Init the user interface
+    CreateGUI();
+}
+
+void InitLocalizationSystem()
+{
+    // JSON files must be in UTF8 encoding without BOM
+    // The first founded language will be set as current
     localization.LoadJSONFile("StringsEnRu.json");
     localization.LoadJSONFile("StringsEnRu.json");
+    // You can load multiple files
     localization.LoadJSONFile("StringsDe.json");
     localization.LoadJSONFile("StringsDe.json");
+    // Hook up to the change language
     SubscribeToEvent("ChangeLanguage", "HandleChangeLanguage");
     SubscribeToEvent("ChangeLanguage", "HandleChangeLanguage");
-
-    // Create "Hello World" Text
-    CreateText();
-
-    // Finally, hook-up this HelloWorld instance to handle update events
-    SubscribeToEvents();
 }
 }
 
 
-void CreateText()
+void CreateGUI()
 {
 {
-    // Construct new Text object
-    helloText = Text();
+    ui.root.defaultStyle = cache.GetResource("XMLFile", "UI/DefaultStyle.xml");
+
+    Window@ window = Window();
+    ui.root.AddChild(window);
+    window.SetMinSize(384, 192);
+    window.SetLayout(LM_VERTICAL, 6, IntRect(6, 6, 6, 6));
+    window.SetAlignment(HA_CENTER, VA_CENTER);
+    window.SetStyleAuto();
 
 
-    // Set String to display
-    //helloText.text = localization.Get("lang");
-    helloText.text = "lang";
-    helloText.autoLocalizable = true;
+    Text@ windowTitle = Text();
+    windowTitle.name = "WindowTitle";
+    windowTitle.SetStyleAuto();
+    window.AddChild(windowTitle);
 
 
-    // Set font and text color
-    helloText.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30);
-    helloText.color = Color(0.0f, 1.0f, 0.0f);
+    // In this place the current language is "en" because it was found first when loading the JSON files
+    String langName = localization.language;
+    // Languages are numbered in the loading order
+    int langIndex = localization.languageIndex; // == 0 at the beginning
+    // Get string with identifier "title" in the current language
+    String localizedString = localization.Get("title");
+    // Localization.Get returns String::EMPTY if the id is empty.
+    // Localization.Get returns the id if translation is not found and will be added a warning into the log.
 
 
-    // Align Text center-screen
-    helloText.horizontalAlignment = HA_CENTER;
-    helloText.verticalAlignment = VA_CENTER;
+    windowTitle.text = localizedString + " (" + String(langIndex) + " " + langName + ")";
+
+    Button@ b = Button();
+    window.AddChild(b);
+    b.SetStyle("Button");
+    b.minHeight = 24;
+    
+    Text@ t = b.CreateChild("Text", "ButtonTextChangeLang");
+    // The showing text value will automatically change when language is changed
+    t.autoLocalizable = true;
+    // The text value used as a string identifier in this mode.
+    // Remember that a letter case of the id and of the lang name is important.
+    t.text = "Press this button";
+    
+    t.SetAlignment(HA_CENTER, VA_CENTER);
+    t.SetStyle("Text");
+    SubscribeToEvent(b, "Released", "HandleChangeLangButtonPressed");
 
 
-    // Add Text instance to the UI root element
-    ui.root.AddChild(helloText);
+    b = Button();
+    window.AddChild(b);
+    b.SetStyle("Button");
+    b.minHeight = 24;
+    t = b.CreateChild("Text", "ButtonTextQuit");
+    t.SetAlignment(HA_CENTER, VA_CENTER);
+    t.SetStyle("Text");
+    
+    // Manually set text in the current language
+    t.text = localization.Get("quit");
+    
+    SubscribeToEvent(b, "Released", "HandleQuitButtonPressed");
 }
 }
 
 
-void SubscribeToEvents()
+void CreateScene()
 {
 {
-    // Subscribe HandleUpdate() function for processing update events
+    scene_ = Scene();
+    scene_.CreateComponent("Octree");
+    
+    Zone@ zone = scene_.CreateComponent("Zone");
+    zone.boundingBox = BoundingBox(-1000.0f, 1000.0f);
+    zone.ambientColor = Color(0.5f, 0.5f, 0.5f);
+    zone.fogColor = Color(0.4f, 0.5f, 0.8f);
+    zone.fogStart = 1.0f;
+    zone.fogEnd = 100.0f;
+
+    Node@ planeNode = scene_.CreateChild("Plane");
+    planeNode.scale = Vector3(300.0f, 1.0f, 300.0f);
+    StaticModel@ planeObject = planeNode.CreateComponent("StaticModel");
+    planeObject.model = cache.GetResource("Model", "Models/Plane.mdl");
+    planeObject.material = cache.GetResource("Material", "Materials/StoneTiled.xml");
+
+    Node@ lightNode = scene_.CreateChild("DirectionalLight");
+    lightNode.direction = Vector3(0.6f, -1.0f, 0.8f);
+    Light@ light = lightNode.CreateComponent("Light");
+    light.lightType = LIGHT_DIRECTIONAL;
+    light.color = Color(0.8f, 0.8f, 0.8f);
+
+    cameraNode = scene_.CreateChild("Camera");
+    cameraNode.CreateComponent("Camera");
+    cameraNode.position = Vector3(0.0f, 10.0f, -30.0f);
+
+    Node@ text3DNode = scene_.CreateChild("Text3D");
+    text3DNode.position = Vector3(0.0f, 0.1f, 30.0f);
+    text3DNode.SetScale(15);
+    Text3D@ text3D = text3DNode.CreateComponent("Text3D");
+
+    // Manually set text in the current language.
+    text3D.text = localization.Get("lang");
+
+    text3D.SetFont(cache.GetResource("Font", "Fonts/Anonymous Pro.ttf"), 30);
+    text3D.color = Color::BLACK;
+    text3D.SetAlignment(HA_CENTER, VA_BOTTOM);
+
+    Viewport@ viewport = Viewport(scene_, cameraNode.GetComponent("Camera"));
+    renderer.viewports[0] = viewport;
+
     SubscribeToEvent("Update", "HandleUpdate");
     SubscribeToEvent("Update", "HandleUpdate");
 }
 }
 
 
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 void HandleUpdate(StringHash eventType, VariantMap& eventData)
 {
 {
-   if (input.keyPress[KEY_SPACE])
-   {
-       uint lang = localization.languageIndex;
-       lang++;
-       if (lang >= localization.numLanguages)
-           lang = 0;
-       localization.SetLanguage(lang);
-   }
+    float timeStep = eventData["TimeStep"].GetFloat();
+    const float MOUSE_SENSITIVITY = 0.1f;
+    IntVector2 mouseMove = input.mouseMove;
+    yaw += MOUSE_SENSITIVITY * mouseMove.x;
+    pitch += MOUSE_SENSITIVITY * mouseMove.y;
+    pitch = Clamp(pitch, -90.0f, 90.0f);
+    cameraNode.rotation = Quaternion(pitch, yaw, 0.0f);
 }
 }
 
 
+void HandleChangeLangButtonPressed(StringHash eventType, VariantMap& eventData)
+{
+    // Languages are numbered in the loading order
+    int lang = localization.languageIndex;
+    lang++;
+    if (lang >= localization.numLanguages)
+        lang = 0;
+    localization.SetLanguage(lang);
+}
+
+void HandleQuitButtonPressed(StringHash eventType, VariantMap& eventData)
+{
+    engine.Exit();
+}
 
 
+// You can manually change texts, sprites and other aspects of the game when language is changed
 void HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 void HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
 {
 {
-    //helloText.text = localization.Get("lang");
+    Text@ windowTitle = ui.root.GetChild("WindowTitle", true);
+    windowTitle.text = localization.Get("title") + " (" +
+                           String(localization.languageIndex) + " " +
+                           localization.language + ")";
+
+    Text@ buttonText = ui.root.GetChild("ButtonTextQuit", true);
+    buttonText.text = localization.Get("quit");
+
+    Text3D@ text3D = scene_.GetChild("Text3D").GetComponent("Text3D");
+    text3D.text = localization.Get("lang");
+
+    // A text on the button "Press this button" changes automatically
 }
 }
 
 
-// Create XML patch instructions for screen joystick layout specific to this sample app
-String patchInstructions =
-        "<patch>" +
-        "    <add sel=\"/element/element[./attribute[@name='Name' and @value='Hat0']]\">" +
-        "        <attribute name=\"Is Visible\" value=\"false\" />" +
-        "    </add>" +
-        "</patch>";
+String patchInstructions = "";

+ 4 - 4
bin/Data/Scripts/Editor/EditorResourceBrowser.as

@@ -184,9 +184,9 @@ void DoResourceBrowserWork()
     }
     }
 
 
     if (browserFilesToScan.length > 0)
     if (browserFilesToScan.length > 0)
-        browserStatusMessage.text = "Files left to scan: " + browserFilesToScan.length;
+        browserStatusMessage.text = localization.Get("Files left to scan: " )+ browserFilesToScan.length;
     else
     else
-        browserStatusMessage.text = "Scan complete";
+        browserStatusMessage.text = localization.Get("Scan complete");
 
 
 }
 }
 
 
@@ -279,7 +279,7 @@ void CreateDirList(BrowserDir@ dir, UIElement@ parentUI = null)
     Text@ dirText = Text();
     Text@ dirText = Text();
     browserDirList.InsertItem(browserDirList.numItems, dirText, parentUI);
     browserDirList.InsertItem(browserDirList.numItems, dirText, parentUI);
     dirText.style = "FileSelectorListText";
     dirText.style = "FileSelectorListText";
-    dirText.text = dir.resourceKey.empty ? "Root" : dir.name;
+    dirText.text = dir.resourceKey.empty ? localization.Get("Root") : dir.name;
     dirText.name = dir.resourceKey;
     dirText.name = dir.resourceKey;
     dirText.vars[TEXT_VAR_DIR_ID] = dir.resourceKey;
     dirText.vars[TEXT_VAR_DIR_ID] = dir.resourceKey;
 
 
@@ -601,7 +601,7 @@ void PopulateResourceBrowserFilesByDirectory(BrowserDir@ dir)
     browserSearchSortMode = BROWSER_SORT_MODE_ALPHA;
     browserSearchSortMode = BROWSER_SORT_MODE_ALPHA;
     files.Sort();
     files.Sort();
     PopulateResourceBrowserResults(files);
     PopulateResourceBrowserResults(files);
-    browserResultsMessage.text = "Showing " + files.length + " files";
+    browserResultsMessage.text = localization.Get("Showing files: ") + files.length;
 }
 }
 
 
 
 

+ 22 - 1
bin/Data/Scripts/Editor/EditorUI.as

@@ -92,6 +92,7 @@ void CreateUI()
     SubscribeToEvent("KeyDown", "HandleKeyDown");
     SubscribeToEvent("KeyDown", "HandleKeyDown");
     SubscribeToEvent("KeyUp", "UnfadeUI");
     SubscribeToEvent("KeyUp", "UnfadeUI");
     SubscribeToEvent("MouseButtonUp", "UnfadeUI");
     SubscribeToEvent("MouseButtonUp", "UnfadeUI");
+    SubscribeToEvent("ChangeLanguage", "HandleChangeLanguage");
 }
 }
 
 
 void ResizeUI()
 void ResizeUI()
@@ -707,6 +708,7 @@ Menu@ CreateMenuItem(const String&in title, MENU_CALLBACK@ callback = null, int
     menu.AddChild(menuText);
     menu.AddChild(menuText);
     menuText.style = "EditorMenuText";
     menuText.style = "EditorMenuText";
     menuText.text = title;
     menuText.text = title;
+    menuText.autoLocalizable = true;
 
 
     if (addToQuickMenu)
     if (addToQuickMenu)
         AddQuickMenuItem(callback, quickMenuText.empty ? title : quickMenuText);
         AddQuickMenuItem(callback, quickMenuText.empty ? title : quickMenuText);
@@ -796,12 +798,31 @@ Window@ CreatePopup(Menu@ baseMenu)
 Menu@ CreateMenu(const String&in title)
 Menu@ CreateMenu(const String&in title)
 {
 {
     Menu@ menu = CreateMenuItem(title);
     Menu@ menu = CreateMenuItem(title);
-    menu.SetFixedWidth(menu.width);
+    Text@ text = menu.children[0];
+    menu.maxWidth = text.width + 20;
     CreatePopup(menu);
     CreatePopup(menu);
 
 
     return menu;
     return menu;
 }
 }
 
 
+void HandleChangeLanguage(StringHash eventType, VariantMap& eventData)
+{
+    Array<UIElement@> children = uiMenuBar.GetChildren();
+
+    for (uint i = 0; i < children.length - 2; ++i) // last 2 elements is not menu
+    {
+        // dirty hack: force recalc text size
+        children[i].maxWidth = 1000;
+        Text@ text = children[i].children[0];
+        text.minWidth = 0;
+        text.maxWidth = 1;
+        text.ApplyAttributes();
+        children[i].maxWidth = text.width + 20;
+    }
+
+    RebuildResourceDatabase();
+}
+
 Text@ CreateAccelKeyText(int accelKey, int accelQual)
 Text@ CreateAccelKeyText(int accelKey, int accelQual)
 {
 {
     Text@ accelKeyText = Text();
     Text@ accelKeyText = Text();

+ 2 - 2
bin/Data/StringsDe.json

@@ -5,8 +5,8 @@
 	"quit":{
 	"quit":{
 		"de":"Verlassen"
 		"de":"Verlassen"
 	},
 	},
-	"long text":{
-		"de":"Einige Langtext blah blah blah"
+	"Press this button":{
+		"de":"Drücken Sie diese Taste"
 	},
 	},
 	"title":{
 	"title":{
 		"de":"Titel"
 		"de":"Titel"

+ 3 - 3
bin/Data/StringsEnRu.json

@@ -7,9 +7,9 @@
 		"en":"Quit",
 		"en":"Quit",
 		"ru":"Выйти"
 		"ru":"Выйти"
 	},
 	},
-	"long text":{
-		"en":"Some long text blah blah blah",
-		"ru":"Некоторый длинный текст бла бла бла"
+	"Press this button":{
+		"en":"Press this button",
+		"ru":"Нажмите эту кнопку"
 	},
 	},
 	"title":{
 	"title":{
 		"en":"Title",
 		"en":"Title",