Initial commit

This commit is contained in:
SweetId 2023-10-20 20:17:31 -04:00
commit 2fd0d0034e
13 changed files with 694 additions and 0 deletions

195
.gitignore vendored Normal file
View File

@ -0,0 +1,195 @@
# xmake-related files
.xmake/*
.vs/*
.vscode/*
CMakeLists.txt
Makefile
vs*/*
vsxmake*/*
# Ignore assets (except shaders)
assets/*
!assets/examples_version.txt
!assets/readme.md
!assets/tests_version.txt
!assets/shaders
# Coverage
coverage.out
# Embed resources
src/Nazara/*/Resources/**/*.h
# Nazara binaries
bin/*
!bin/resources
!bin/resources/*
# Build files
build/*
# Nazara libraries
lib/*
# Self-hosted thirdparty libraries binaries
thirdparty/genlib/*
# Nazara plugin libraries
plugins/lib/*
# Nazara package
package/*
# Example files
examples/bin/*.exe
examples/bin/*.pdb
examples/bin/*.dll
examples/bin/*.so
examples/bin/Demo*
# Unit tests
tests/*.exe
tests/*.pdb
tests/*.dll
tests/*.so
tests/NazaraUnitTests*
# Example generated files
examples/bin/HardwareInfo.txt
# Example generated files
examples/bin/HardwareInfo.txt
# Feature page
build/scripts/features/index.html
# Documentation
doc
# Codeblocks
*.save-failed
build/**/*.cbp
build/**/*.cbp
build/**/*.cbTemp
build/**/*.cscope_file_list
build/**/*.depend
build/**/*.layout
build/**/*.workspace
# CodeLite
build/**/*.project
# GMake
build/**/*.make
build/**/*.d
# Visual Studio
build/**/*.pdb
build/**/*.filters
build/**/*.vcxproj
build/**/*.tlog
build/**/*.sln
build/**/*.vcxprojResolveAssemblyReference.cache
build/**/*.nativecodeanalysis.all.xml
build/**/*.nativecodeanalysis.xml
build/**/*.VC.opendb
build/**/*.VC.db*
build/**/*.json
build/**/*.sqlite
build/**/*.FileListAbsolute.txt
build/**/*.recipe
# Compiled Object files
build/**/*.slo
build/**/*.lo
build/**/*.o
build/**/*.obj
build/**/*.obj.enc
# Compiled Dynamic libraries
build/**/*.so
# Compiled Static libraries
build/**/*.lai
build/**/*.la
# Object files
build/**/*.o
# Windows image file caches
Thumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
# User-specific files
*.suo
*.user
*.sln.docstates
[Tt]est[Rr]esult
[Bb]uild[Ll]og.*
*_i.c
*_p.c
*.idb
*.ilk
*.meta
*.pch
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.vspscc
*.vssscc
.builds
*.pidb
*.log
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opensdf
*.sdf
# Visual Studio profiler
*.psess
*.vsp
# Guidance Automation Toolkit
*.gpState
# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
# NCrunch
*.ncrunch*
.*crunch*.local.xml
# Installshield output folder
[Ee]xpress
# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

21
LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (C) 2023
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

40
README.md Normal file
View File

@ -0,0 +1,40 @@
# Nazara Localization
Nazara Localization is a [Nazara Engine](https://github.com/NazaraEngine/NazaraEngine) module that allows you to add localized text to your project.
You can use it in any kind of commercial and non-commercial applications without any restriction ([MIT license](http://opensource.org/licenses/MIT)).
## Authors
Sid - main developper
## How to Use
```
// Add NazaraImgui.hpp to your includes
#include <NazaraLocalization/NazaraLocalization.hpp>
// main.cpp
{
// Add Nz::Localization to the modules list
Nz::Modules<Nz::Graphics, Nz::Localization,...> nazara;
Nz::LocalizedText hello = "LOC_HELLO_WORLD";
printf("text: %s\n", hello.c_str());
}
```
## Contribute
##### Don't hesitate to contribute to Nazara Engine by:
- Extending the [wiki](https://github.com/NazaraEngine/NazaraEngine/wiki)
- Submitting a patch to GitHub
- Post suggestions/bugs on the forum or the [GitHub tracker](https://github.com/NazaraEngine/NazaraEngine/issues)
- [Fork the project](https://github.com/NazaraEngine/NazaraEngine/fork) on GitHub and [push your changes](https://github.com/NazaraEngine/NazaraEngine/pulls)
- Talking about Nazara Engine to other people, spread the word!
- Doing anything else that might help us
## Links
[Discord](https://discord.gg/MvwNx73)

View File

@ -0,0 +1,3 @@
;fr-FR;en-US;pt-BR
LOC_HELLO_WORLD;Bonjour le monde!;Hello World!;Olá Mundo!
LOC_TEXT_ARG;{1:.2f} est la valeur de {0}!;{pi} is the value of {}!;O valor de {} é {}!
1 fr-FR en-US pt-BR
2 LOC_HELLO_WORLD Bonjour le monde! Hello World! Olá Mundo!
3 LOC_TEXT_ARG {1:.2f} est la valeur de {0}! {pi} is the value of {}! O valor de {} é {}!

34
examples/Demo/main.cpp Normal file
View File

@ -0,0 +1,34 @@
#include <Nazara/Core/Application.hpp>
#include <NazaraLocalization/Localization.hpp>
#include <NazaraLocalization/LocalizedText.hpp>
#include <iostream>
int main(int argc, char* argv[])
{
NazaraUnused(argc);
NazaraUnused(argv);
Nz::Application<Nz::Localization> nazara;
Nz::LocalizedText helloWorld("LOC_HELLO_WORLD");
Nz::LocalizedText textWithArguments = Nz::LocalizedText("LOC_TEXT_ARG").Arg("PI").Arg("pi", Nz::Pi<float>);
// Works with printf as if you were printing a string
// There is no localization data loaded so it will just print the key
printf("%s\n%s\n", helloWorld.c_str(), textWithArguments.c_str());
// We load the CSV containing our data
Nz::Localization::Instance()->LoadLocalizationFile("localization.csv");
Nz::Localization::Instance()->SetLocale("fr-FR");
std::cout << helloWorld << std::endl << textWithArguments << std::endl;
Nz::Localization::Instance()->SetLocale("en-US");
std::cout << helloWorld << std::endl << textWithArguments << std::endl;
Nz::Localization::Instance()->SetLocale("pt-BR");
std::cout << helloWorld << std::endl << textWithArguments << std::endl;
return 0;
}

7
examples/Demo/xmake.lua Normal file
View File

@ -0,0 +1,7 @@
target("NzLocalization-demo")
set_group("Examples")
add_files("main.cpp")
add_packages("nazara")
add_deps("NazaraLocalization")
set_rundir(".")

9
examples/xmake.lua Normal file
View File

@ -0,0 +1,9 @@
option("examples")
set_default(false)
set_showmenu(true)
set_description("Build examples")
option_end()
if has_config("examples") then
includes("*/xmake.lua")
end

View File

@ -0,0 +1,13 @@
#pragma once
#include <NazaraUtils/Prerequisites.hpp>
#if defined(NAZARA_LOCALIZATION_STATIC)
#define NAZARA_LOCALIZATION_API
#else
#ifdef NAZARA_LOCALIZATION_BUILD
#define NAZARA_LOCALIZATION_API NAZARA_EXPORT
#else
#define NAZARA_LOCALIZATION_API NAZARA_IMPORT
#endif
#endif

View File

@ -0,0 +1,46 @@
#pragma once
#include <Nazara/Core/Core.hpp>
#include <NazaraLocalization/Config.hpp>
#include <map>
namespace Nz
{
class NAZARA_LOCALIZATION_API Localization : public Nz::ModuleBase<Localization>
{
friend ModuleBase;
public:
using Dependencies = TypeList<Core>;
struct Config {};
Localization(Config config);
~Localization();
// Loads a CSV containing the localization strings
bool LoadLocalizationFile(const std::filesystem::path& filepath);
// Changes the locale
bool SetLocale(const std::string& locale);
bool FindIndexForKey(std::string_view key, size_t& index) const;
const std::string& GetStringAtIndex(size_t index) const;
private:
struct Locale
{
std::string name;
std::vector<std::string> localizedStrings;
};
std::vector<Locale> m_locales;
std::unordered_map<std::string, size_t> m_lookupTable;
Locale* m_currentLocale;
static Localization* s_instance;
friend class LocalizedText;
};
}

View File

@ -0,0 +1,71 @@
#pragma once
#include <NazaraLocalization/Config.hpp>
#include <NazaraLocalization/Localization.hpp>
#include <variant>
#include <optional>
#include <ostream>
#include <string_view>
namespace Nz
{
class NAZARA_LOCALIZATION_API LocalizedText
{
public:
LocalizedText();
LocalizedText(std::string_view str);
template <typename T> LocalizedText& Arg(T&& v)
{
NamedParameter p;
p.first = v;
m_parameters.push_back(std::move(p));
return *this;
}
template <> LocalizedText& Arg(const char*&& v) { return Arg(std::string(v)); }
template <typename T> LocalizedText& Arg(std::string_view name, T&& v)
{
NamedParameter p;
p.first = v;
p.second = name;
m_parameters.push_back(std::move(p));
return *this;
}
template <> LocalizedText& Arg(std::string_view name, const char*&& v) { return Arg(name, std::string(v)); }
const char* c_str() const;
const char* data() const;
size_t length() const;
size_t size() const;
const std::string& ToString() const;
friend std::ostream& operator<<(std::ostream& out, const Nz::LocalizedText& dt)
{
return out << dt.ToString();
}
private:
void Update() const;
std::string BuildFormattedString() const;
// Cache for localization
mutable std::optional<size_t> m_index; // index in the lookup array
mutable Localization::Locale* m_locale; // current locale to avoid fetching pointer every time
mutable std::optional<std::string> m_cachedStr; // computed string (loca + parameters formatting)
std::string m_str;
using Parameter = std::variant<
Int8, Int16, Int32, Int64,
UInt8, UInt16, UInt32, UInt64,
float, double,
std::string
>;
using NamedParameter = std::pair<Parameter, std::optional<std::string>>;
std::vector<NamedParameter> m_parameters;
};
}

View File

@ -0,0 +1,87 @@
#include <NazaraLocalization/Localization.hpp>
#include <Nazara/Core/StringExt.hpp>
#include <fstream>
namespace Nz
{
Localization* Localization::s_instance = nullptr;
Localization::Localization(Config /*config*/)
: ModuleBase("Localization", this)
, m_currentLocale(nullptr)
{ }
Localization::~Localization()
{}
bool Localization::LoadLocalizationFile(const std::filesystem::path& filepath)
{
std::ifstream file(filepath);
if (!file)
return false;
// read header
std::string line;
std::getline(file, line);
m_locales.clear();
SplitString(line, ";", [this](std::string_view str) {
if (!str.empty())
m_locales.push_back({ std::string(str) });
return true;
});
size_t index = 0;
while (std::getline(file, line))
{
std::vector<std::string_view> values;
SplitString(line, ";", [&values](std::string_view str) {
values.push_back(str);
return true;
});
m_lookupTable[std::string(values[0])] = index++;
for (size_t i = 0; i < m_locales.size() && i < values.size() - 1; ++i)
{
m_locales[i].localizedStrings.push_back(std::string(values[i + 1]));
}
}
return true;
}
bool Localization::SetLocale(const std::string& locale)
{
auto it = std::find_if(m_locales.begin(), m_locales.end(), [locale](auto&& loc) { return loc.name == locale; });
if (it == m_locales.end())
{
m_currentLocale = nullptr;
return false;
}
m_currentLocale = &(*it);
return true;
}
bool Localization::FindIndexForKey(std::string_view key, size_t& index) const
{
auto it = m_lookupTable.find(std::string(key));
if (it == m_lookupTable.end())
return false;
index = it->second;
return true;
}
const std::string& Localization::GetStringAtIndex(size_t index) const
{
static std::string empty = "";
if (m_currentLocale == nullptr)
return empty;
return m_currentLocale->localizedStrings.at(index);
}
} // end of namespace Nz

View File

@ -0,0 +1,94 @@
#include <NazaraLocalization/LocalizedText.hpp>
#include <NazaraLocalization/Localization.hpp>
#include <fmt/format.h>
#include <fmt/args.h>
namespace Nz
{
LocalizedText::LocalizedText()
: LocalizedText("")
{ }
LocalizedText::LocalizedText(std::string_view str)
: m_str(str)
, m_locale(nullptr)
{}
void LocalizedText::Update() const
{
if (m_locale != Nz::Localization::Instance()->m_currentLocale)
{
m_cachedStr = {};
m_locale = Nz::Localization::Instance()->m_currentLocale;
if (!m_index.has_value())
{
size_t index;
if (Nz::Localization::Instance()->FindIndexForKey(m_str, index))
m_index = index;
}
if (m_index.has_value())
{
m_cachedStr = BuildFormattedString();
}
}
}
std::string LocalizedText::BuildFormattedString() const
{
if (m_parameters.empty()) // No parameters, no formatting
return m_locale->localizedStrings[m_index.value()];
fmt::dynamic_format_arg_store<fmt::format_context> store;
for (auto& p : m_parameters)
{
if (p.second)
{
std::visit(Overloaded{
[&](auto&& value) {
store.push_back(fmt::arg(p.second->c_str(), value));
}
}, p.first);
}
else
{
std::visit(Overloaded{
[&](auto&& value) {
store.push_back(value);
}
}, p.first);
}
}
return fmt::vformat(m_locale->localizedStrings[m_index.value()], store);
}
const char* LocalizedText::c_str() const
{
Update();
return (m_cachedStr) ? m_cachedStr->c_str() : m_str.c_str();
}
const char* LocalizedText::data() const
{
Update();
return (m_cachedStr) ? m_cachedStr->data() : m_str.data();
}
size_t LocalizedText::length() const
{
Update();
return (m_cachedStr) ? m_cachedStr->length() : m_str.length();
}
size_t LocalizedText::size() const
{
Update();
return (m_cachedStr) ? m_cachedStr->size() : m_str.size();
}
const std::string& LocalizedText::ToString() const
{
Update();
return (m_cachedStr) ? m_cachedStr.value() : m_str;
}
}

74
xmake.lua Normal file
View File

@ -0,0 +1,74 @@
set_project("NazaraLocalization")
add_rules("mode.asan", "mode.tsan", "mode.coverage", "mode.debug", "mode.releasedbg", "mode.release")
add_rules("plugin.vsxmake.autoupdate")
add_repositories("nazara-engine-repo https://github.com/NazaraEngine/xmake-repo")
add_requires("nazaraengine", { alias = "nazara", debug = is_mode("debug") })
add_requires("fmt")
add_includedirs("include", "src")
set_languages("c89", "c++20")
set_rundir("./bin/$(plat)_$(arch)_$(mode)")
set_targetdir("./bin/$(plat)_$(arch)_$(mode)")
if has_config("erronwarn") then
set_warnings("allextra", "error")
else
set_warnings("allextra")
end
if is_plat("mingw", "linux") then
add_cxflags("-Wno-subobject-linkage")
end
if is_plat("windows") then
add_defines("_CRT_SECURE_NO_WARNINGS")
add_cxxflags("/bigobj", "/permissive-", "/Zc:__cplusplus", "/Zc:externConstexpr", "/Zc:inline", "/Zc:lambda", "/Zc:preprocessor", "/Zc:referenceBinding", "/Zc:strictStrings", "/Zc:throwingNew")
add_cxflags("/w44062") -- Enable warning: switch case not handled
add_cxflags("/wd4251") -- Disable warning: class needs to have dll-interface to be used by clients of class blah blah blah
add_cxflags("/wd4275") -- Disable warning: DLL-interface class 'class_1' used as base for DLL-interface blah
elseif is_plat("mingw") then
add_cxflags("-Og", "-Wa,-mbig-obj")
add_ldflags("-Wa,-mbig-obj")
end
set_runtimes(is_mode("debug") and "MDd" or "MD")
if is_mode("asan", "tsan", "ubsan") then
set_optimize("none") -- by default xmake will optimize asan builds
elseif is_mode("coverage") then
if not is_plat("windows") then
add_links("gcov")
end
elseif is_mode("releasedbg") then
set_fpmodels("fast")
add_vectorexts("sse", "sse2", "sse3", "ssse3")
end
target("NazaraLocalization")
add_packages("nazara", {public = true})
add_packages("fmt", {public = false})
set_kind("$(kind)")
set_group("Libraries")
add_headerfiles("include/(NazaraLocalization/**.hpp)")
add_headerfiles("include/(NazaraLocalization/**.inl)")
add_headerfiles("src/NazaraLocalization/**.h", { prefixdir = "private", install = false })
add_headerfiles("src/NazaraLocalization/**.hpp", { prefixdir = "private", install = false })
add_headerfiles("src/NazaraLocalization/**.inl", { prefixdir = "private", install = false })
add_files("src/NazaraLocalization/**.cpp")
-- for now only shared compilation is supported (except on platforms like wasm)
if not is_plat("wasm") then
set_kind("shared")
else
set_kind("static")
add_defines("NAZARA_LOCALIZATION_STATIC", { public = true })
end
add_defines("NAZARA_LOCALIZATION_BUILD")
if is_mode("debug") then
add_defines("NAZARA_LOCALIZATION_DEBUG")
end
includes("examples/xmake.lua")