Add Localization
This commit is contained in:
@@ -35,6 +35,7 @@ public class Engine : EngineModule
|
||||
options.PublicDependencies.Add("UI");
|
||||
options.PublicDependencies.Add("Utilities");
|
||||
options.PublicDependencies.Add("Visject");
|
||||
options.PublicDependencies.Add("Localization");
|
||||
|
||||
// Use source folder per platform group
|
||||
switch (options.Platform.Target)
|
||||
|
||||
186
Source/Engine/Localization/CultureInfo.cpp
Normal file
186
Source/Engine/Localization/CultureInfo.cpp
Normal file
@@ -0,0 +1,186 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "CultureInfo.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MType.h"
|
||||
#include "Engine/Utilities/StringConverter.h"
|
||||
#if USE_MONO
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/appdomain.h>
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/culture-info.h>
|
||||
#include <ThirdParty/mono-2.0/mono/metadata/culture-info-tables.h>
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MonoObject obj;
|
||||
MonoBoolean is_read_only;
|
||||
gint32 lcid;
|
||||
//...
|
||||
} MonoCultureInfo;
|
||||
#endif
|
||||
|
||||
CultureInfo::CultureInfo(int32 lcid)
|
||||
{
|
||||
_lcid = lcid;
|
||||
_lcidParent = 0;
|
||||
_data = nullptr;
|
||||
if (lcid == 0)
|
||||
return;
|
||||
if (lcid == 127)
|
||||
{
|
||||
_englishName = TEXT("Invariant Culture");
|
||||
return;
|
||||
}
|
||||
#if USE_MONO
|
||||
for (int32 i = 0; i < NUM_CULTURE_ENTRIES; i++)
|
||||
{
|
||||
auto& e = culture_entries[i];
|
||||
if (e.lcid == lcid)
|
||||
{
|
||||
_data = (void*)&e;
|
||||
_lcidParent = (int32)e.parent_lcid;
|
||||
const char* name = idx2string(e.name);
|
||||
_name.SetUTF8(name, StringUtils::Length(name));
|
||||
const char* nativename = idx2string(e.nativename);
|
||||
_nativeName.SetUTF8(nativename, StringUtils::Length(nativename));
|
||||
const char* englishname = idx2string(e.englishname);
|
||||
_englishName.SetUTF8(englishname, StringUtils::Length(englishname));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Missing CultureInfo implementation."
|
||||
#endif
|
||||
if (!_data)
|
||||
{
|
||||
LOG(Error, "Unknown LCID {0} for CultureInfo", lcid);
|
||||
}
|
||||
}
|
||||
|
||||
CultureInfo::CultureInfo(const StringView& name)
|
||||
: CultureInfo(StringAnsiView(StringAsANSI<16>(name.Get(), name.Length()).Get(), name.Length()))
|
||||
{
|
||||
}
|
||||
|
||||
CultureInfo::CultureInfo(const StringAnsiView& name)
|
||||
{
|
||||
_data = nullptr;
|
||||
if (name.IsEmpty())
|
||||
{
|
||||
_lcid = 0;
|
||||
_lcidParent = 0;
|
||||
_englishName = TEXT("Invariant Culture");
|
||||
return;
|
||||
}
|
||||
#if USE_MONO
|
||||
for (int32 i = 0; i < NUM_CULTURE_ENTRIES; i++)
|
||||
{
|
||||
auto& e = culture_entries[i];
|
||||
if (name == idx2string(e.name))
|
||||
{
|
||||
_data = (void*)&e;
|
||||
_lcid = (int32)e.lcid;
|
||||
_lcidParent = (int32)e.parent_lcid;
|
||||
_name.SetUTF8(name.Get(), name.Length());
|
||||
const char* nativename = idx2string(e.nativename);
|
||||
_nativeName.SetUTF8(nativename, StringUtils::Length(nativename));
|
||||
const char* englishname = idx2string(e.englishname);
|
||||
_englishName.SetUTF8(englishname, StringUtils::Length(englishname));
|
||||
break;
|
||||
}
|
||||
}
|
||||
#else
|
||||
#error "Missing CultureInfo implementation."
|
||||
#endif
|
||||
if (!_data)
|
||||
{
|
||||
LOG(Error, "Unknown name {0} for CultureInfo", String(name));
|
||||
}
|
||||
}
|
||||
|
||||
int32 CultureInfo::GetLCID() const
|
||||
{
|
||||
return _lcid;
|
||||
}
|
||||
|
||||
int32 CultureInfo::GetParentLCID() const
|
||||
{
|
||||
return _lcidParent;
|
||||
}
|
||||
|
||||
const String& CultureInfo::GetName() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
const String& CultureInfo::GetNativeName() const
|
||||
{
|
||||
return _nativeName;
|
||||
}
|
||||
|
||||
const String& CultureInfo::GetEnglishName() const
|
||||
{
|
||||
return _englishName;
|
||||
}
|
||||
|
||||
bool CultureInfo::IsRightToLeft() const
|
||||
{
|
||||
#if USE_MONO
|
||||
const auto data = static_cast<CultureInfoEntry*>(_data);
|
||||
if (data)
|
||||
return data->text_info.is_right_to_left ? true : false;
|
||||
#else
|
||||
#error "Missing CultureInfo implementation."
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
String CultureInfo::ToString() const
|
||||
{
|
||||
return _name;
|
||||
}
|
||||
|
||||
bool CultureInfo::operator==(const CultureInfo& other) const
|
||||
{
|
||||
return _lcid == other._lcid;
|
||||
}
|
||||
|
||||
void* MUtils::ToManaged(const CultureInfo& value)
|
||||
{
|
||||
#if USE_MONO
|
||||
const auto klass = mono_class_from_name(mono_get_corlib(), "System.Globalization", "CultureInfo");
|
||||
if (klass)
|
||||
{
|
||||
void* iter = nullptr;
|
||||
MonoMethod* method;
|
||||
while ((method = mono_class_get_methods(klass, &iter)))
|
||||
{
|
||||
if (StringUtils::Compare(mono_method_get_name(method), ".ctor") == 0)
|
||||
{
|
||||
const auto sig = mono_method_signature(method);
|
||||
void* sigParams = nullptr;
|
||||
mono_signature_get_params(sig, &sigParams);
|
||||
if (mono_signature_get_param_count(sig) == 1 && mono_type_get_class(((MonoType**)sigParams)[0]) != mono_get_string_class())
|
||||
{
|
||||
MonoObject* obj = mono_object_new(mono_domain_get(), klass);
|
||||
int32 lcid = value.GetLCID();
|
||||
void* params[1];
|
||||
params[0] = &lcid;
|
||||
mono_runtime_invoke(method, obj, params, nullptr);
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
CultureInfo MUtils::ToNative(void* value)
|
||||
{
|
||||
int32 lcid = 127;
|
||||
#if USE_MONO
|
||||
if (value)
|
||||
lcid = static_cast<MonoCultureInfo*>(value)->lcid;
|
||||
#endif
|
||||
return CultureInfo(lcid);
|
||||
}
|
||||
82
Source/Engine/Localization/CultureInfo.h
Normal file
82
Source/Engine/Localization/CultureInfo.h
Normal file
@@ -0,0 +1,82 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Engine/Core/Types/String.h"
|
||||
#include "Engine/Core/Formatting.h"
|
||||
|
||||
/// <summary>
|
||||
/// The information about a specific culture (aka locale). The information includes the names for the culture, the writing system, the calendar used, the sort order of strings, and formatting for dates and numbers.
|
||||
/// </summary>
|
||||
API_CLASS(InBuild, Namespace="System.Globalization") class FLAXENGINE_API CultureInfo
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(CultureInfo);
|
||||
private:
|
||||
void* _data;
|
||||
int32 _lcid;
|
||||
int32 _lcidParent;
|
||||
String _name;
|
||||
String _nativeName;
|
||||
String _englishName;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CultureInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="lcid">The culture identifier.</param>
|
||||
CultureInfo(int32 lcid);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CultureInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The culture name (eg. pl-PL).</param>
|
||||
CultureInfo(const StringView& name);
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="CultureInfo"/> class.
|
||||
/// </summary>
|
||||
/// <param name="name">The culture name (eg. pl-PL).</param>
|
||||
CultureInfo(const StringAnsiView& name);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the culture identifier.
|
||||
/// </summary>
|
||||
int32 GetLCID() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent culture identifier.
|
||||
/// </summary>
|
||||
int32 GetParentLCID() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture name (eg. pl-PL).
|
||||
/// </summary>
|
||||
const String& GetName() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the full localized culture name (eg. Polish (Poland)).
|
||||
/// </summary>
|
||||
const String& GetNativeName() const;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the culture name in English (eg. polski (Polska)).
|
||||
/// </summary>
|
||||
const String& GetEnglishName() const;
|
||||
|
||||
/// <summary>
|
||||
/// Returns true if culture uses right-to-left (RTL) text layout. Otherwise it's more common left-to-right.
|
||||
/// </summary>
|
||||
bool IsRightToLeft() const;
|
||||
|
||||
String ToString() const;
|
||||
bool operator==(const CultureInfo& other) const;
|
||||
};
|
||||
|
||||
DEFINE_DEFAULT_FORMATTING_VIA_TO_STRING(CultureInfo);
|
||||
|
||||
namespace MUtils
|
||||
{
|
||||
extern void* ToManaged(const CultureInfo& value);
|
||||
extern CultureInfo ToNative(void* value);
|
||||
}
|
||||
18
Source/Engine/Localization/Localization.Build.cs
Normal file
18
Source/Engine/Localization/Localization.Build.cs
Normal file
@@ -0,0 +1,18 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
using Flax.Build;
|
||||
using Flax.Build.NativeCpp;
|
||||
|
||||
/// <summary>
|
||||
/// Localization and internalization module.
|
||||
/// </summary>
|
||||
public class Localization : EngineModule
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override void Setup(BuildOptions options)
|
||||
{
|
||||
base.Setup(options);
|
||||
|
||||
options.PublicDependencies.Add("Scripting");
|
||||
}
|
||||
}
|
||||
73
Source/Engine/Localization/Localization.cpp
Normal file
73
Source/Engine/Localization/Localization.cpp
Normal file
@@ -0,0 +1,73 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#include "Localization.h"
|
||||
#include "CultureInfo.h"
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
|
||||
class LocalizationService : public EngineService
|
||||
{
|
||||
public:
|
||||
LocalizationService()
|
||||
: EngineService(TEXT("Localization"), -950)
|
||||
{
|
||||
}
|
||||
|
||||
bool Init() override;
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
CultureInfo CurrentCulture(0);
|
||||
CultureInfo CurrentLanguage(0);
|
||||
LocalizationService LocalizationServiceInstance;
|
||||
}
|
||||
|
||||
bool LocalizationService::Init()
|
||||
{
|
||||
CurrentLanguage = CurrentCulture = CultureInfo(Platform::GetUserLocaleName());
|
||||
return false;
|
||||
}
|
||||
|
||||
Delegate<> Localization::CurrentLanguageCultureChanged;
|
||||
|
||||
const CultureInfo& Localization::GetCurrentCulture()
|
||||
{
|
||||
return CurrentCulture;
|
||||
}
|
||||
|
||||
void Localization::SetCurrentCulture(const CultureInfo& value)
|
||||
{
|
||||
if (CurrentCulture == value)
|
||||
return;
|
||||
|
||||
LOG(Info, "Changing current culture to: {0} ({1})", value.GetName(), value.GetLCID());
|
||||
CurrentCulture = value;
|
||||
CurrentLanguageCultureChanged();
|
||||
}
|
||||
|
||||
const CultureInfo& Localization::GetCurrentLanguage()
|
||||
{
|
||||
return CurrentLanguage;
|
||||
}
|
||||
|
||||
void Localization::SetCurrentLanguage(const CultureInfo& value)
|
||||
{
|
||||
if (CurrentLanguage == value)
|
||||
return;
|
||||
|
||||
LOG(Info, "Changing current language to: {0} ({1})", value.GetName(), value.GetLCID());
|
||||
CurrentLanguage = value;
|
||||
CurrentLanguageCultureChanged();
|
||||
}
|
||||
|
||||
void Localization::SetCurrentLanguageCulture(const CultureInfo& value)
|
||||
{
|
||||
if (CurrentCulture == value && CurrentLanguage == value)
|
||||
return;
|
||||
|
||||
LOG(Info, "Changing current language and culture to: {0} ({1})", value.GetName(), value.GetLCID());
|
||||
CurrentCulture = value;
|
||||
CurrentLanguage = value;
|
||||
CurrentLanguageCultureChanged();
|
||||
}
|
||||
44
Source/Engine/Localization/Localization.h
Normal file
44
Source/Engine/Localization/Localization.h
Normal file
@@ -0,0 +1,44 @@
|
||||
// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CultureInfo.h"
|
||||
#include "Engine/Core/Types/BaseTypes.h"
|
||||
|
||||
/// <summary>
|
||||
/// The language and culture localization manager.
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API Localization
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_MINIMAL(Localization);
|
||||
public:
|
||||
/// <summary>
|
||||
/// Gets the current culture (date, time, currency and values formatting locale).
|
||||
/// </summary>
|
||||
API_PROPERTY() static const CultureInfo& GetCurrentCulture();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current culture (date, time, currency and values formatting locale).
|
||||
/// </summary>
|
||||
API_PROPERTY() static void SetCurrentCulture(const CultureInfo& value);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current language (text display locale).
|
||||
/// </summary>
|
||||
API_PROPERTY() static const CultureInfo& GetCurrentLanguage();
|
||||
|
||||
/// <summary>
|
||||
/// Sets the current language (text display locale).
|
||||
/// </summary>
|
||||
API_PROPERTY() static void SetCurrentLanguage(const CultureInfo& value);
|
||||
|
||||
/// <summary>
|
||||
/// Sets both the current language (text display locale) and the current culture (date, time, currency and values formatting locale) at once.
|
||||
/// </summary>
|
||||
API_FUNCTION() static void SetCurrentLanguageCulture(const CultureInfo& value);
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when current culture or language gets changed. Can be used to refresh UI to reflect language changes.
|
||||
/// </summary>
|
||||
API_EVENT() static Delegate<> CurrentLanguageCultureChanged;
|
||||
};
|
||||
36
Source/ThirdParty/glib.h
vendored
Normal file
36
Source/ThirdParty/glib.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
// Wrapper for mono/mono/eglib/glib.h to mock the types for embedding
|
||||
|
||||
#ifndef _GLIB_H_
|
||||
#define _GLIB_H_
|
||||
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
|
||||
/*
|
||||
* Basic data types
|
||||
*/
|
||||
typedef int gint;
|
||||
typedef unsigned int guint;
|
||||
typedef short gshort;
|
||||
typedef unsigned short gushort;
|
||||
typedef long glong;
|
||||
typedef unsigned long gulong;
|
||||
typedef void * gpointer;
|
||||
typedef const void * gconstpointer;
|
||||
typedef char gchar;
|
||||
typedef unsigned char guchar;
|
||||
|
||||
/* Types defined in terms of the stdint.h */
|
||||
typedef int8_t gint8;
|
||||
typedef uint8_t guint8;
|
||||
typedef int16_t gint16;
|
||||
typedef uint16_t guint16;
|
||||
typedef int32_t gint32;
|
||||
typedef uint32_t guint32;
|
||||
typedef int64_t gint64;
|
||||
typedef uint64_t guint64;
|
||||
typedef float gfloat;
|
||||
typedef double gdouble;
|
||||
typedef int32_t gboolean;
|
||||
|
||||
#endif
|
||||
@@ -1059,6 +1059,7 @@ namespace Flax.Build.Bindings
|
||||
// Using declarations
|
||||
contents.AppendLine("using System;");
|
||||
contents.AppendLine("using System.ComponentModel;");
|
||||
contents.AppendLine("using System.Globalization;"); // TODO: using declarations based on actual types usage
|
||||
contents.AppendLine("using System.Runtime.CompilerServices;");
|
||||
contents.AppendLine("using System.Runtime.InteropServices;");
|
||||
foreach (var e in moduleInfo.Children)
|
||||
|
||||
@@ -307,6 +307,9 @@ namespace Flax.Build.Bindings
|
||||
case "MClass":
|
||||
type = "MonoReflectionType*";
|
||||
return "MUtils::GetType({0})";
|
||||
case "CultureInfo":
|
||||
type = "void*";
|
||||
return "MUtils::ToManaged({0})";
|
||||
default:
|
||||
var apiType = FindApiTypeInfo(buildData, typeInfo, caller);
|
||||
if (apiType != null)
|
||||
@@ -448,6 +451,9 @@ namespace Flax.Build.Bindings
|
||||
case "VariantType":
|
||||
type = "MonoReflectionType*";
|
||||
return "MUtils::UnboxVariantType({0})";
|
||||
case "CultureInfo":
|
||||
type = "void*";
|
||||
return "MUtils::ToNative({0})";
|
||||
default:
|
||||
// ScriptingObjectReference or AssetReference or WeakAssetReference or SoftObjectReference
|
||||
if ((typeInfo.Type == "ScriptingObjectReference" || typeInfo.Type == "AssetReference" || typeInfo.Type == "WeakAssetReference" || typeInfo.Type == "SoftObjectReference") && typeInfo.GenericArgs != null)
|
||||
|
||||
Reference in New Issue
Block a user