forked from 0ad/0ad
Initial i18n integration
This was SVN commit r1029.
This commit is contained in:
parent
b981542a51
commit
0e55379b36
@ -169,6 +169,6 @@ sub cpp_files {
|
|||||||
opendir my $d, $_[0] or die "Error opening directory '$_[0]' ($!)";
|
opendir my $d, $_[0] or die "Error opening directory '$_[0]' ($!)";
|
||||||
my @f = readdir $d;
|
my @f = readdir $d;
|
||||||
my @files = map "$_[0]/$_", grep /\.(?:cpp|h)$/, @f;
|
my @files = map "$_[0]/$_", grep /\.(?:cpp|h)$/, @f;
|
||||||
push @files, cpp_files("$_[0]/$_") for grep { /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f;
|
push @files, cpp_files("$_[0]/$_") for grep { !/^(?:workspaces|tools|ape|sced)$/ and /^[a-zA-Z0-9]+$/ and -d "$_[0]/$_" } @f;
|
||||||
return @files;
|
return @files;
|
||||||
}
|
}
|
||||||
|
133
source/i18n/BufferVariable.cpp
Executable file
133
source/i18n/BufferVariable.cpp
Executable file
@ -0,0 +1,133 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "BufferVariable.h"
|
||||||
|
|
||||||
|
#include "DataTypes.h"
|
||||||
|
#include "CLocale.h"
|
||||||
|
|
||||||
|
// Hashing functions are currently 32-bit only
|
||||||
|
cassert(sizeof(int) == 4);
|
||||||
|
cassert(sizeof(double) == 8);
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
namespace I18n {
|
||||||
|
// These bits don't seem to work without the explicit namespace{}
|
||||||
|
|
||||||
|
BufferVariable* NewBufferVariable(int v) { return new BufferVariable_int(v); }
|
||||||
|
BufferVariable* NewBufferVariable(double v) { return new BufferVariable_double(v); }
|
||||||
|
BufferVariable* NewBufferVariable(const wchar_t* v) { return new BufferVariable_string(v); }
|
||||||
|
BufferVariable* NewBufferVariable(const char* v) { return new BufferVariable_string(v); }
|
||||||
|
BufferVariable* NewBufferVariable(I18n::Name v) { return new BufferVariable_rawstring(v.value); }
|
||||||
|
}
|
||||||
|
|
||||||
|
StrImW BufferVariable_int::ToString(CLocale*)
|
||||||
|
{
|
||||||
|
wchar_t buffer[16];
|
||||||
|
swprintf(buffer, 16, L"%d", value);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
u32 BufferVariable_int::Hash()
|
||||||
|
{
|
||||||
|
return (u32)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StrImW BufferVariable_double::ToString(CLocale*)
|
||||||
|
{
|
||||||
|
// TODO: Work out how big the buffer should be
|
||||||
|
wchar_t buffer[256];
|
||||||
|
swprintf(buffer, 256, L"%f", value);
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
u32 BufferVariable_double::Hash()
|
||||||
|
{
|
||||||
|
// Add the two four-bytes of the double
|
||||||
|
return *((u32*)&value) + *((u32*)&value + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StrImW BufferVariable_string::ToString(CLocale* locale)
|
||||||
|
{
|
||||||
|
// Attempt noun translations
|
||||||
|
const Str nouns = L"nouns";
|
||||||
|
const Str wordname (value.str());
|
||||||
|
const CLocale::LookupType* word = locale->LookupWord(nouns, wordname);
|
||||||
|
// Couldn't find; just return the original word
|
||||||
|
if (! word)
|
||||||
|
return value;
|
||||||
|
|
||||||
|
const Str propname = L"singular";
|
||||||
|
Str ret;
|
||||||
|
if (! locale->LookupProperty(word, propname, ret))
|
||||||
|
{
|
||||||
|
delete word;
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
delete word;
|
||||||
|
return ret.c_str();
|
||||||
|
}
|
||||||
|
u32 BufferVariable_string::Hash()
|
||||||
|
{
|
||||||
|
// Do nothing very clever
|
||||||
|
int hash = 0;
|
||||||
|
const wchar_t* str = value.str();
|
||||||
|
size_t len = wcslen(str);
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
hash += str[i];
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
StrImW BufferVariable_rawstring::ToString(CLocale*)
|
||||||
|
{
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
u32 BufferVariable_rawstring::Hash()
|
||||||
|
{
|
||||||
|
int hash = 0;
|
||||||
|
const wchar_t* str = value.str();
|
||||||
|
size_t len = wcslen(str);
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
hash += str[i];
|
||||||
|
return hash;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool I18n::operator== (BufferVariable& a, BufferVariable& b)
|
||||||
|
{
|
||||||
|
if (a.Type != b.Type)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (a.Type)
|
||||||
|
{
|
||||||
|
case vartype_int:
|
||||||
|
return *static_cast<BufferVariable_int*>(&a) == *static_cast<BufferVariable_int*>(&b);
|
||||||
|
case vartype_double:
|
||||||
|
return *static_cast<BufferVariable_double*>(&a) == *static_cast<BufferVariable_double*>(&b);
|
||||||
|
case vartype_string:
|
||||||
|
return *static_cast<BufferVariable_string*>(&a) == *static_cast<BufferVariable_string*>(&b);
|
||||||
|
case vartype_rawstring:
|
||||||
|
return *static_cast<BufferVariable_rawstring*>(&a) == *static_cast<BufferVariable_rawstring*>(&b);
|
||||||
|
}
|
||||||
|
debug_warn("Invalid buffer variable vartype");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferVariable_int::operator== (BufferVariable_int& a)
|
||||||
|
{
|
||||||
|
return a.value == this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferVariable_double::operator== (BufferVariable_double& a)
|
||||||
|
{
|
||||||
|
return a.value == this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferVariable_string::operator== (BufferVariable_string& a)
|
||||||
|
{
|
||||||
|
return a.value == this->value;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BufferVariable_rawstring::operator== (BufferVariable_rawstring& a)
|
||||||
|
{
|
||||||
|
return a.value == this->value;
|
||||||
|
}
|
87
source/i18n/BufferVariable.h
Executable file
87
source/i18n/BufferVariable.h
Executable file
@ -0,0 +1,87 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
BufferVariable stores the parameters that have been passed to a StringBuffer,
|
||||||
|
providing hashes and strings on request.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_BUFFERVARIABLE_H
|
||||||
|
#define I18N_BUFFERVARIABLE_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include "StrImmutable.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
class CLocale;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
vartype_int,
|
||||||
|
vartype_double,
|
||||||
|
vartype_string,
|
||||||
|
vartype_rawstring, // won't be translated automatically
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
char Type;
|
||||||
|
virtual StrImW ToString(CLocale*) = 0;
|
||||||
|
virtual u32 Hash() = 0;
|
||||||
|
virtual ~BufferVariable() {};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Factory constructor type thing, sort of
|
||||||
|
template<typename T>
|
||||||
|
BufferVariable* NewBufferVariable(T v);
|
||||||
|
|
||||||
|
class BufferVariable_int : public BufferVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int value;
|
||||||
|
BufferVariable_int(int v) : value(v) { Type = vartype_int; }
|
||||||
|
StrImW ToString(CLocale*);
|
||||||
|
u32 Hash();
|
||||||
|
// Equality testing is required by the cache
|
||||||
|
bool operator== (BufferVariable_int&);
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferVariable_double : public BufferVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
double value;
|
||||||
|
BufferVariable_double(double v) : value(v) { Type = vartype_double; }
|
||||||
|
StrImW ToString(CLocale*);
|
||||||
|
u32 Hash();
|
||||||
|
bool operator== (BufferVariable_double&);
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferVariable_string : public BufferVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StrImW value;
|
||||||
|
BufferVariable_string(StrImW v) : value(v) { Type = vartype_string; }
|
||||||
|
StrImW ToString(CLocale*);
|
||||||
|
u32 Hash();
|
||||||
|
bool operator== (BufferVariable_string&);
|
||||||
|
};
|
||||||
|
|
||||||
|
class BufferVariable_rawstring : public BufferVariable
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
StrImW value;
|
||||||
|
BufferVariable_rawstring(StrImW v) : value(v) { Type = vartype_rawstring; }
|
||||||
|
StrImW ToString(CLocale*);
|
||||||
|
u32 Hash();
|
||||||
|
bool operator== (BufferVariable_rawstring&);
|
||||||
|
};
|
||||||
|
|
||||||
|
// BufferVariable==BufferVariable compares their Types, and then
|
||||||
|
// uses the appropriate operator== if they're the same
|
||||||
|
bool operator== (BufferVariable&, BufferVariable&);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_BUFFERVARIABLE_H
|
431
source/i18n/CLocale.cpp
Executable file
431
source/i18n/CLocale.cpp
Executable file
@ -0,0 +1,431 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "CLocale.h"
|
||||||
|
#include "TSComponent.h"
|
||||||
|
#include "StringConvert.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#define LOG_CATEGORY "i18n"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
// Vaguely useful utility function for deleting stuff
|
||||||
|
template<typename T> void delete_fn(T* v) { delete v; }
|
||||||
|
|
||||||
|
// These could be optimised for little-endian sizeof(wchar_t)==2 systems:
|
||||||
|
static inline void ReadWString8_(const char*& data, Str& str)
|
||||||
|
{
|
||||||
|
u8 length = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
StringConvert::ucs2le_to_wstring(data, data+length, str);
|
||||||
|
data += length;
|
||||||
|
}
|
||||||
|
static inline void ReadWString16_(const char*& data, Str& str)
|
||||||
|
{
|
||||||
|
u16 length = *(u16*)data;
|
||||||
|
data += 2;
|
||||||
|
StringConvert::ucs2le_to_wstring(data, data+length, str);
|
||||||
|
data += length;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define ReadWString8(s) Str s; ReadWString8_(data, s);
|
||||||
|
#define ReadWString16(s) Str s; ReadWString16_(data, s);
|
||||||
|
|
||||||
|
|
||||||
|
bool CLocale::LoadStrings(const char* data)
|
||||||
|
{
|
||||||
|
// TODO: More robust file format (so errors can be detected in a
|
||||||
|
// nicer way than watching for access violations)
|
||||||
|
|
||||||
|
u16 PhraseCount = *(u16*)data;
|
||||||
|
data += 2;
|
||||||
|
|
||||||
|
for (int i = 0; i < PhraseCount; ++i)
|
||||||
|
{
|
||||||
|
ReadWString16(Key);
|
||||||
|
|
||||||
|
u8 VarCount = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
// Get the relevant entry in the string hash, creating it if it doesn't exist
|
||||||
|
TranslatedString* String = Strings[Key];
|
||||||
|
if (! String)
|
||||||
|
String = Strings[Key] = new TranslatedString;
|
||||||
|
// If this is a redefined string, make sure it's empty
|
||||||
|
String->Parts.clear();
|
||||||
|
// Store the number of variables, so translate(x)<<y<<z can check for validity
|
||||||
|
String->VarCount = VarCount;
|
||||||
|
|
||||||
|
u8 SectionCount = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
for (int j = 0; j < SectionCount; ++j)
|
||||||
|
{
|
||||||
|
u8 SectionType = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
switch (SectionType)
|
||||||
|
{
|
||||||
|
case 0: // Constant string
|
||||||
|
{
|
||||||
|
ReadWString16(StringText);
|
||||||
|
|
||||||
|
String->Parts.push_back(new TSComponentString(StringText.c_str()));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1: // Variable
|
||||||
|
{
|
||||||
|
u8 VarID = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
String->Parts.push_back(new TSComponentVariable(VarID));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: // Function
|
||||||
|
{
|
||||||
|
u8 NameLength = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
std::string NameText ((char*)data, (char*)(data + NameLength));
|
||||||
|
data += NameLength;
|
||||||
|
|
||||||
|
u8 ParamCount = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
TSComponentFunction* Func = new TSComponentFunction(NameText.c_str());
|
||||||
|
|
||||||
|
for (int k = 0; k < ParamCount; ++k)
|
||||||
|
{
|
||||||
|
u8 ParamType = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
switch (ParamType)
|
||||||
|
{
|
||||||
|
case 0: // String
|
||||||
|
{
|
||||||
|
ReadWString8(StrText);
|
||||||
|
|
||||||
|
Func->AddParam(new ScriptValueString(Script, StrText.c_str()));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 1: // Variable
|
||||||
|
{
|
||||||
|
u8 ID = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
Func->AddParam(new ScriptValueVariable(Script, ID));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 2: // Integer
|
||||||
|
{
|
||||||
|
u32 Num = *(u32*)data;
|
||||||
|
data += 4;
|
||||||
|
|
||||||
|
Func->AddParam(new ScriptValueInteger(Script, Num));
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: // Argh!
|
||||||
|
debug_warn("Invalid function parameter type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String->Parts.push_back(Func);
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default: // Argh!
|
||||||
|
debug_warn("Invalid translation string section type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CLocale::LoadFunctions(const char* data, size_t len, const char* filename)
|
||||||
|
{
|
||||||
|
// Insist on little-endian UTF16 files containing a BOM (e.g. as generated
|
||||||
|
// by Notepad when saving in Unicode format)
|
||||||
|
|
||||||
|
// TODO: Support more Unicode file formats
|
||||||
|
|
||||||
|
if (len < 2)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "I18n: Functions file '%s' is too short", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (*(jschar*)data != 0xFEFF)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "I18n: Functions file '%s' has invalid Unicode format (lacking little-endian BOM)", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! Script.ExecuteCode((jschar*)(data+2), len/2, filename))
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "I18n: JS errors in functions file '%s'", filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool CLocale::LoadDictionary(const char* data, const wchar_t* name)
|
||||||
|
{
|
||||||
|
u8 PropertyCount = *(u8*)data;
|
||||||
|
data += 1;
|
||||||
|
|
||||||
|
DictData& dict = Dictionaries[name];
|
||||||
|
|
||||||
|
// Read the names of the properties
|
||||||
|
|
||||||
|
for (int i = 0; i < PropertyCount; ++i)
|
||||||
|
{
|
||||||
|
ReadWString8(Property);
|
||||||
|
dict.DictProperties[Property] = i;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 ValueCount = *(u16*)data;
|
||||||
|
data += 2;
|
||||||
|
|
||||||
|
// Read each 'value' (word + properties)
|
||||||
|
|
||||||
|
for (int i = 0; i < ValueCount; ++i)
|
||||||
|
{
|
||||||
|
ReadWString8(Word);
|
||||||
|
|
||||||
|
std::vector<std::wstring>& props = dict.DictWords[Word];
|
||||||
|
|
||||||
|
for (int j = 0; j < PropertyCount; ++j)
|
||||||
|
{
|
||||||
|
ReadWString8(Value);
|
||||||
|
props.push_back(Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const CLocale::LookupType* CLocale::LookupWord(const Str& dictname, const Str& word)
|
||||||
|
{
|
||||||
|
std::map<Str, DictData>::const_iterator dictit = Dictionaries.find(dictname);
|
||||||
|
if (dictit == Dictionaries.end())
|
||||||
|
{
|
||||||
|
assert(! "invalid dictionary");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
std::map<Str, std::vector<Str> >::const_iterator wordit = dictit->second.DictWords.find(word);
|
||||||
|
if (wordit == dictit->second.DictWords.end())
|
||||||
|
{
|
||||||
|
// Word not found. Respond quietly, so JS code can handle missing
|
||||||
|
// words in a more appropriate way.
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return some data that can later be passed to LookupProperty
|
||||||
|
return new LookupType(&dictit->second, &wordit->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLocale::LookupProperty(const LookupType* data, const Str& property, Str& result)
|
||||||
|
{
|
||||||
|
std::map<Str, int>::const_iterator propit = data->first->DictProperties.find(property);
|
||||||
|
if (propit == data->first->DictProperties.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Return the appropriate string
|
||||||
|
result = (*data->second)[propit->second];
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const StrImW CLocale::CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params)
|
||||||
|
{
|
||||||
|
return Script.CallFunction(name, vars, params);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
StringBuffer CLocale::Translate(const wchar_t* id)
|
||||||
|
{
|
||||||
|
if (++CacheAge > CacheAgeLimit)
|
||||||
|
{
|
||||||
|
CacheAge = 0;
|
||||||
|
ClearCache();
|
||||||
|
}
|
||||||
|
|
||||||
|
StringsType::iterator TransStr = Strings.find(Str(id));
|
||||||
|
if (TransStr == Strings.end())
|
||||||
|
{
|
||||||
|
LOG(WARNING, LOG_CATEGORY, "I18n: No translation found for string '%ls'", id);
|
||||||
|
|
||||||
|
// Just use the ID string directly, and remember it for the future
|
||||||
|
return StringBuffer(AddDefaultString(id), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
return StringBuffer(*(*TransStr).second, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void CLocale::AddToCache(StringBuffer* sb, Str& str)
|
||||||
|
{
|
||||||
|
CacheData& d = TranslationCache[&sb->String];
|
||||||
|
|
||||||
|
// Clean up any earlier cache entry
|
||||||
|
std::for_each(d.vars.begin(), d.vars.end(), delete_fn<BufferVariable>);
|
||||||
|
|
||||||
|
// Set the data for the new cache entry
|
||||||
|
d.hash = sb->Hash();
|
||||||
|
d.vars = sb->Variables;
|
||||||
|
d.output = str;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CLocale::ReadCached(StringBuffer* sb, Str& str)
|
||||||
|
{
|
||||||
|
// Look for a string with the right key in the cache
|
||||||
|
std::map<TranslatedString*, CacheData>::iterator it =
|
||||||
|
TranslationCache.find(&sb->String);
|
||||||
|
|
||||||
|
// See if it actually exists
|
||||||
|
if (it == TranslationCache.end())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check quickly whether the hashes match
|
||||||
|
if (sb->Hash() != (*it).second.hash)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Check every variable to see whether they're identical
|
||||||
|
assert(sb->Variables.size() == (*it).second.vars.size()); // this should always be true
|
||||||
|
size_t count = sb->Variables.size();
|
||||||
|
for (size_t i = 0; i < count; ++i)
|
||||||
|
if (! ( *sb->Variables[i] == *(*it).second.vars[i] ) )
|
||||||
|
return false;
|
||||||
|
|
||||||
|
str = (*it).second.output;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CLocale::ClearCache()
|
||||||
|
{
|
||||||
|
// Deallocate cached data
|
||||||
|
for (std::map<TranslatedString*, CacheData>::iterator it = TranslationCache.begin(); it != TranslationCache.end(); ++it)
|
||||||
|
std::for_each((*it).second.vars.begin(), (*it).second.vars.end(), delete_fn<BufferVariable>);
|
||||||
|
|
||||||
|
TranslationCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool is_valid_variable_char(wchar_t c)
|
||||||
|
{
|
||||||
|
// c =~ /[a-zA-Z0-9_]/
|
||||||
|
// (Hurrah for internationalisation.)
|
||||||
|
return
|
||||||
|
(c >= 'a' && c <= 'z')
|
||||||
|
|| (c >= 'A' && c <= 'Z')
|
||||||
|
|| (c >= '0' && c <= '9')
|
||||||
|
|| (c == '_');
|
||||||
|
}
|
||||||
|
|
||||||
|
TranslatedString& CLocale::AddDefaultString(const wchar_t* id)
|
||||||
|
{
|
||||||
|
// Parse a string involving $variables and $$ (=$)
|
||||||
|
|
||||||
|
enum ParseState {
|
||||||
|
st_default,
|
||||||
|
st_afterdollar,
|
||||||
|
st_variable
|
||||||
|
};
|
||||||
|
ParseState state = st_default;
|
||||||
|
|
||||||
|
TranslatedString* str = new TranslatedString;
|
||||||
|
str->VarCount = 0;
|
||||||
|
|
||||||
|
std::wstring tempstr;
|
||||||
|
|
||||||
|
for (const wchar_t* ch = id; *ch != '\0'; ++ch)
|
||||||
|
{
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case st_default:
|
||||||
|
if (*ch == '$')
|
||||||
|
{
|
||||||
|
state = st_afterdollar;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
tempstr += *ch;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case st_afterdollar:
|
||||||
|
if (*ch == '$')
|
||||||
|
{
|
||||||
|
tempstr += '$';
|
||||||
|
state = st_default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Start of a variable name.
|
||||||
|
// Push the old string onto the component stack
|
||||||
|
if (tempstr.length())
|
||||||
|
{
|
||||||
|
str->Parts.push_back(new TSComponentString(tempstr.c_str()));
|
||||||
|
tempstr.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set the ID (starting at 0) and increment the count
|
||||||
|
str->Parts.push_back(new TSComponentVariable(str->VarCount++));
|
||||||
|
|
||||||
|
state = st_variable;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case st_variable:
|
||||||
|
if (*ch == '$')
|
||||||
|
{
|
||||||
|
state = st_afterdollar;
|
||||||
|
}
|
||||||
|
else if (! is_valid_variable_char(*ch))
|
||||||
|
{
|
||||||
|
state = st_default;
|
||||||
|
tempstr = *ch;
|
||||||
|
}
|
||||||
|
// We don't care about the actual name of the variable, so just ignore it.
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Make sure the last string is added to the parts list
|
||||||
|
if (tempstr.length())
|
||||||
|
str->Parts.push_back(new TSComponentString(tempstr.c_str()));
|
||||||
|
|
||||||
|
Strings[id] = str;
|
||||||
|
|
||||||
|
return *str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
CLocale::~CLocale()
|
||||||
|
{
|
||||||
|
// Clean up the list of strings
|
||||||
|
for (StringsType::iterator it = Strings.begin(); it != Strings.end(); ++it)
|
||||||
|
delete (*it).second;
|
||||||
|
|
||||||
|
ClearCache();
|
||||||
|
}
|
106
source/i18n/CLocale.h
Executable file
106
source/i18n/CLocale.h
Executable file
@ -0,0 +1,106 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
CLocale contains all the data about a locale (/language).
|
||||||
|
LoadFunctions/LoadStrings/LoadDictionary are used to specify data to be used
|
||||||
|
when translating, and Translate is used to perform translations.
|
||||||
|
|
||||||
|
All other methods are used internally by other I18n components.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_CLOCALE_H
|
||||||
|
#define I18N_CLOCALE_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
|
||||||
|
#include "Interface.h"
|
||||||
|
#include "StringBuffer.h"
|
||||||
|
#include "StrImmutable.h"
|
||||||
|
#include "ScriptInterface.h"
|
||||||
|
|
||||||
|
#include "lib/sysdep/sysdep.h" // STL_HASH_MAP
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
struct JSContext;
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
|
||||||
|
class CLocale : public CLocale_interface
|
||||||
|
{
|
||||||
|
friend StringBuffer;
|
||||||
|
|
||||||
|
private:
|
||||||
|
typedef STL_HASH_MAP<Str, TranslatedString*> StringsType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
StringBuffer Translate(const wchar_t* id);
|
||||||
|
|
||||||
|
bool LoadFunctions(const char* filedata, size_t len, const char* filename);
|
||||||
|
bool LoadStrings(const char* filedata);
|
||||||
|
bool LoadDictionary(const char* filedata, const wchar_t* name);
|
||||||
|
|
||||||
|
const StrImW CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params);
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct DictData;
|
||||||
|
public:
|
||||||
|
typedef std::pair<const DictData*, const std::vector<Str>*> LookupType;
|
||||||
|
|
||||||
|
// Returns a new'ed structure. Please remember to delete it.
|
||||||
|
const LookupType* LookupWord(const Str& dictname, const Str& word);
|
||||||
|
|
||||||
|
bool LookupProperty(const LookupType* data, const Str& property, Str& result);
|
||||||
|
|
||||||
|
// Disable the "'this' used in base member initializer list" warning: only the
|
||||||
|
// pointer (and not the data it points to) is accessed by ScriptObject's
|
||||||
|
// constructor, so it shouldn't cause any problems.
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (disable: 4355)
|
||||||
|
#endif
|
||||||
|
CLocale(JSContext* context) : Script(this, context), CacheAge(0) {}
|
||||||
|
#ifdef _MSC_VER
|
||||||
|
# pragma warning (default: 4355)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
~CLocale();
|
||||||
|
|
||||||
|
void AddToCache(StringBuffer*, Str&);
|
||||||
|
bool ReadCached(StringBuffer*, Str&);
|
||||||
|
void ClearCache();
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
TranslatedString& AddDefaultString(const wchar_t* id);
|
||||||
|
|
||||||
|
StringsType Strings;
|
||||||
|
|
||||||
|
// Incremented by every call to Translate(), with
|
||||||
|
// ClearCache() being run after a certain number of calls.
|
||||||
|
// TODO: Replace with a better caching system.
|
||||||
|
int CacheAge;
|
||||||
|
static const int CacheAgeLimit = 1024;
|
||||||
|
|
||||||
|
struct CacheData
|
||||||
|
{
|
||||||
|
u32 hash;
|
||||||
|
std::vector<BufferVariable*> vars;
|
||||||
|
Str output;
|
||||||
|
};
|
||||||
|
std::map<TranslatedString*, CacheData> TranslationCache;
|
||||||
|
|
||||||
|
struct DictData
|
||||||
|
{
|
||||||
|
std::map< Str /* property name */, int /* property id */ > DictProperties;
|
||||||
|
std::map< Str /* word */, std::vector<Str> /* properties */ > DictWords;
|
||||||
|
};
|
||||||
|
std::map<Str /* dictionary name */, DictData> Dictionaries;
|
||||||
|
|
||||||
|
ScriptObject Script;
|
||||||
|
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_CLOCALE_H
|
20
source/i18n/Common.h
Executable file
20
source/i18n/Common.h
Executable file
@ -0,0 +1,20 @@
|
|||||||
|
// Things that are used by most I18n code:
|
||||||
|
|
||||||
|
#ifndef I18N_COMMON_H
|
||||||
|
#define I18N_COMMON_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include "Errors.h"
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
// Define an 'internal' string type, for no particular reason
|
||||||
|
typedef std::wstring Str;
|
||||||
|
}
|
||||||
|
|
||||||
|
ERROR_GROUP(I18n);
|
||||||
|
|
||||||
|
// That was exciting.
|
||||||
|
|
||||||
|
#endif // I18N_COMMON_H
|
19
source/i18n/DataTypes.h
Executable file
19
source/i18n/DataTypes.h
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
#ifndef I18N_DATATYPES_H
|
||||||
|
#define I18N_DATATYPES_H
|
||||||
|
|
||||||
|
#include "StrImmutable.h"
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
// Allow translate("Hello $you")<<I18n::Name(playername), which
|
||||||
|
// won't attempt to automatically translate the player's name.
|
||||||
|
// Templated to allow char* and wchar_t*
|
||||||
|
struct Name
|
||||||
|
{
|
||||||
|
template<typename T> Name(T d) : value(d) {}
|
||||||
|
StrImW value;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endif // I18N_DATATYPES_H
|
76
source/i18n/Interface.cpp
Executable file
76
source/i18n/Interface.cpp
Executable file
@ -0,0 +1,76 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Vague overview of the i18n code:
|
||||||
|
(TODO: Improve the documentation)
|
||||||
|
|
||||||
|
CLocale stores all locale-specific (locale==language, roughly) data.
|
||||||
|
Usually there's only going to be one in existence.
|
||||||
|
|
||||||
|
A language needs to define:
|
||||||
|
|
||||||
|
* String files that define translations for phrases.
|
||||||
|
* Dictionary files that translate individual words (usually names of objects),
|
||||||
|
including extra information that the grammar requires (whether to use 'a' or
|
||||||
|
'an' in English, gender in lots of European languages, etc)
|
||||||
|
* .js files containing functions that apply grammatical rules
|
||||||
|
(e.g. choosing singular vs plural depending on a number)
|
||||||
|
|
||||||
|
CLocale::LoadStrings / LoadDictionary / LoadFunctions are used to input the
|
||||||
|
data from the appropriate files. Call multiple times if desired.
|
||||||
|
|
||||||
|
CLocale::Translate is the primary interface. Pass it a unique identifier
|
||||||
|
string, and it'll read the appropriate translated data from the
|
||||||
|
loaded data files. A StringBuffer is returned.
|
||||||
|
|
||||||
|
To allow variables embedded in text, StringBuffer::operator<< is used
|
||||||
|
in a similar way to in cout, storing the variable in the StringBuffer and
|
||||||
|
returning the StringBuffer again.
|
||||||
|
StringBuffer::operator Str() does the final insertion of variables into
|
||||||
|
the translated phrase, either grabbing the final result from a cache or
|
||||||
|
doing all the variable-to-string conversions.
|
||||||
|
|
||||||
|
|
||||||
|
The strings read from disk are each stored as a TranslatedString,
|
||||||
|
containing several TSComponents, which are either static strings or
|
||||||
|
default-formatted variables or functions.
|
||||||
|
|
||||||
|
TSComponentFunction is the most complex of the TSComponents, containing a
|
||||||
|
list of ScriptValues -- these allow numbers, strings and variables to be
|
||||||
|
passed as parameters into a JS function.
|
||||||
|
|
||||||
|
|
||||||
|
StringBuffer::operator<< stores BufferVariable*s in the StringBuffer.
|
||||||
|
These provide access to a hash of the variable (for caching), and can
|
||||||
|
be converted into a string (for display).
|
||||||
|
|
||||||
|
|
||||||
|
StrImW is used in various places, just as a more efficient alternative
|
||||||
|
to std::wstring.
|
||||||
|
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "Interface.h"
|
||||||
|
#include "CLocale.h"
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#define LOG_CATEGORY "i18n"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
struct JSContext;
|
||||||
|
|
||||||
|
CLocale_interface* I18n::NewLocale(JSContext* cx)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new CLocale(cx);
|
||||||
|
}
|
||||||
|
catch (PSERROR_I18n& e)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "Error creating locale object ('%ls')", GetErrorString(e.code));
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
43
source/i18n/Interface.h
Executable file
43
source/i18n/Interface.h
Executable file
@ -0,0 +1,43 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
The only file that external code should need to include.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_INTERFACE_H
|
||||||
|
#define I18N_INTERFACE_H
|
||||||
|
|
||||||
|
#include "StringBuffer.h"
|
||||||
|
#include "DataTypes.h"
|
||||||
|
|
||||||
|
struct JSContext;
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
// Use an interface class, so minimal headers are required by
|
||||||
|
// anybody who only wants to make use of Translate()
|
||||||
|
class CLocale_interface
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual StringBuffer Translate(const wchar_t* id) = 0;
|
||||||
|
|
||||||
|
// Load* functions return true for success, false for failure
|
||||||
|
|
||||||
|
// Pass the contents of a UTF-16LE BOMmed .js file.
|
||||||
|
// The filename is just used to give more useful error messages from JS.
|
||||||
|
virtual bool LoadFunctions(const char* filedata, size_t len, const char* filename) = 0;
|
||||||
|
|
||||||
|
// Desires a .lng file, as produced by convert.pl
|
||||||
|
virtual bool LoadStrings(const char* filedata) = 0;
|
||||||
|
|
||||||
|
// Needs .wrd files generated through tables.pl
|
||||||
|
virtual bool LoadDictionary(const char* filedata, const wchar_t* name) = 0;
|
||||||
|
|
||||||
|
virtual ~CLocale_interface() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Build a CLocale. Returns NULL on failure.
|
||||||
|
CLocale_interface* NewLocale(JSContext* cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_INTERFACE_H
|
345
source/i18n/ScriptInterface.cpp
Executable file
345
source/i18n/ScriptInterface.cpp
Executable file
@ -0,0 +1,345 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "ScriptInterface.h"
|
||||||
|
#include "CLocale.h"
|
||||||
|
#include "StringConvert.h"
|
||||||
|
|
||||||
|
#include "jsapi.h"
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#define LOG_CATEGORY "i18n"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
namespace LookedupWord {
|
||||||
|
|
||||||
|
JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
|
||||||
|
{
|
||||||
|
CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj);
|
||||||
|
assert(lookedup);
|
||||||
|
|
||||||
|
JSObject* parent = JS_GetParent(cx, obj);
|
||||||
|
assert(parent);
|
||||||
|
CLocale* locale = (CLocale*)JS_GetPrivate(cx, parent);
|
||||||
|
assert(locale);
|
||||||
|
|
||||||
|
JSString* prop = JS_ValueToString(cx, id);
|
||||||
|
if (!prop)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() property failed to convert to string");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
jschar* prop_chars = JS_GetStringChars(prop);
|
||||||
|
if (!prop_chars)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() property failed to convert string to chars");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
Str prop_str;
|
||||||
|
StringConvert::jschars_to_wstring(prop_chars, JS_GetStringLength(prop), prop_str);
|
||||||
|
|
||||||
|
Str result;
|
||||||
|
if (! locale->LookupProperty(lookedup, prop_str, result))
|
||||||
|
result = L"(unrecognised property)";
|
||||||
|
|
||||||
|
JSString* result_str = StringConvert::wstring_to_jsstring(cx, result);
|
||||||
|
if (!result_str)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() property failed to create string");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*vp = STRING_TO_JSVAL(result_str);
|
||||||
|
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Finalize(JSContext *cx, JSObject *obj)
|
||||||
|
{
|
||||||
|
// Free the LookupType that was allocated when building this object
|
||||||
|
|
||||||
|
CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj);
|
||||||
|
assert(lookedup);
|
||||||
|
delete lookedup;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSClass JSI_class = {
|
||||||
|
"LookedupWord", JSCLASS_HAS_PRIVATE,
|
||||||
|
JS_PropertyStub, JS_PropertyStub,
|
||||||
|
GetProperty, JS_PropertyStub,
|
||||||
|
JS_EnumerateStub, JS_ResolveStub,
|
||||||
|
JS_ConvertStub, Finalize,
|
||||||
|
NULL, NULL, NULL, NULL
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSBool JSFunc_LookupWord(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
|
||||||
|
{
|
||||||
|
if (argc != 2)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "Incorrect number of parameters to lookup(dictionary, word) function");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the strings
|
||||||
|
JSString* dictname = JS_ValueToString(cx, argv[0]);
|
||||||
|
JSString* word = JS_ValueToString(cx, argv[1]);
|
||||||
|
if (!dictname || !word)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() failed to convert parameters to strings");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
// and the characters from the strings
|
||||||
|
jschar* dictname_chars = JS_GetStringChars(dictname);
|
||||||
|
jschar* word_chars = JS_GetStringChars(word);
|
||||||
|
if (!dictname_chars || !word_chars)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() failed to get parameter string data");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
// and convert those characters into to wstrings
|
||||||
|
Str dictname_str, word_str;
|
||||||
|
StringConvert::jschars_to_wstring(dictname_chars, JS_GetStringLength(dictname), dictname_str);
|
||||||
|
StringConvert::jschars_to_wstring(word_chars, JS_GetStringLength(word), word_str);
|
||||||
|
|
||||||
|
// Extract the CLocale* from the 'global' object
|
||||||
|
CLocale* locale = (CLocale*)JS_GetPrivate(cx, obj);
|
||||||
|
|
||||||
|
const CLocale::LookupType* lookedup = locale->LookupWord(dictname_str, word_str);
|
||||||
|
if (! lookedup)
|
||||||
|
{
|
||||||
|
// Couldn't find the word in the table
|
||||||
|
*rval = JSVAL_NULL;
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create an object to be returned, containing enough data to access properties of the found word
|
||||||
|
JSObject* wordobj = JS_NewObject(cx, &LookedupWord::JSI_class, NULL, obj);
|
||||||
|
if (!wordobj)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "lookup() failed to create object");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
// Associate the looked-up word data with the JS object
|
||||||
|
JS_SetPrivate(cx, wordobj, (void*)lookedup);
|
||||||
|
|
||||||
|
*rval = OBJECT_TO_JSVAL(wordobj);
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSFunctionSpec JSFunc_list[] = {
|
||||||
|
{"lookup", JSFunc_LookupWord, 2, 0, 0},
|
||||||
|
{0,0,0,0,0},
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static JSClass JSI_class_scriptglobal = {
|
||||||
|
"", JSCLASS_HAS_PRIVATE,
|
||||||
|
JS_PropertyStub, JS_PropertyStub,
|
||||||
|
JS_PropertyStub, JS_PropertyStub,
|
||||||
|
JS_EnumerateStub, JS_ResolveStub,
|
||||||
|
JS_ConvertStub, JS_FinalizeStub,
|
||||||
|
NULL, NULL, NULL, NULL
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
ScriptObject::ScriptObject(CLocale* locale, JSContext* cx)
|
||||||
|
: Context(cx)
|
||||||
|
{
|
||||||
|
// This should, in theory, never fail
|
||||||
|
|
||||||
|
Object = JS_NewObject(Context, &JSI_class_scriptglobal, NULL, NULL);
|
||||||
|
if (! Object)
|
||||||
|
{
|
||||||
|
debug_warn("Object creation failed");
|
||||||
|
throw PSERROR_I18n_Script_SetupFailed();
|
||||||
|
}
|
||||||
|
JS_AddRoot(Context, &Object);
|
||||||
|
|
||||||
|
// Register the 'global' functions
|
||||||
|
if (! JS_DefineFunctions(Context, Object, JSFunc_list))
|
||||||
|
{
|
||||||
|
debug_warn("JS_DefineFunctions failed");
|
||||||
|
throw PSERROR_I18n_Script_SetupFailed();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store the CLocale* ins in the script-object's private area
|
||||||
|
JS_SetPrivate(Context, Object, locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptObject::~ScriptObject()
|
||||||
|
{
|
||||||
|
JS_RemoveRoot(Context, &Object);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ScriptObject::ExecuteCode(const jschar* data, size_t len, const char* filename)
|
||||||
|
{
|
||||||
|
jsval ret;
|
||||||
|
return (JS_EvaluateUCScript(Context, Object, data, (int)len-1, filename, 1, &ret) ? true : false);
|
||||||
|
// use len-1 to stop JS crashing. *shrug* ^^^^^
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const StrImW ScriptObject::CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params)
|
||||||
|
{
|
||||||
|
int argc = (int)params.size();
|
||||||
|
|
||||||
|
// Construct argv, converting each parameter into a jsval
|
||||||
|
jsval* argv = new jsval[argc];
|
||||||
|
for (int i = 0; i < argc; ++i)
|
||||||
|
argv[i] = params[i]->GetJsval(vars);
|
||||||
|
|
||||||
|
jsval rval;
|
||||||
|
bool called = JS_CallFunctionName(Context, Object, name, argc, argv, &rval) ? true : false;
|
||||||
|
|
||||||
|
delete[] argv;
|
||||||
|
|
||||||
|
if (! called)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "I18n: Error executing JS function '%s'", name);
|
||||||
|
return L"(JS error)";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert rval to a string and return
|
||||||
|
|
||||||
|
JSString* str = JS_ValueToString(Context, rval);
|
||||||
|
if (! str)
|
||||||
|
{
|
||||||
|
debug_warn("Conversion to string failed");
|
||||||
|
return L"(JS error)";
|
||||||
|
}
|
||||||
|
jschar* chars = JS_GetStringChars(str);
|
||||||
|
if (! chars)
|
||||||
|
{
|
||||||
|
debug_warn("GetStringChars failed");
|
||||||
|
return L"(JS error)";
|
||||||
|
}
|
||||||
|
return StrImW(chars, JS_GetStringLength(str));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScriptValueString::ScriptValueString(ScriptObject& script, const wchar_t* s)
|
||||||
|
{
|
||||||
|
Context = script.Context;
|
||||||
|
|
||||||
|
JSString* str = StringConvert::wchars_to_jsstring(Context, s);
|
||||||
|
if (!str)
|
||||||
|
{
|
||||||
|
debug_warn("Error creating JS string");
|
||||||
|
Value = JSVAL_NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
JS_AddRoot(Context, str);
|
||||||
|
Value = STRING_TO_JSVAL(str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jsval ScriptValueString::GetJsval(const std::vector<BufferVariable*>& vars)
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ScriptValueString::~ScriptValueString()
|
||||||
|
{
|
||||||
|
if (! JSVAL_IS_NULL(Value))
|
||||||
|
{
|
||||||
|
JSString* str = JSVAL_TO_STRING(Value);
|
||||||
|
JS_RemoveRoot(Context, str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/************/
|
||||||
|
|
||||||
|
ScriptValueInteger::ScriptValueInteger(ScriptObject& script, const int v)
|
||||||
|
{
|
||||||
|
Context = script.Context;
|
||||||
|
Value = INT_TO_JSVAL(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
jsval ScriptValueInteger::GetJsval(const std::vector<BufferVariable*>& vars)
|
||||||
|
{
|
||||||
|
return Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptValueInteger::~ScriptValueInteger()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
/************/
|
||||||
|
|
||||||
|
|
||||||
|
ScriptValueVariable::ScriptValueVariable(ScriptObject& script, const unsigned char id)
|
||||||
|
{
|
||||||
|
Context = script.Context;
|
||||||
|
ID = id;
|
||||||
|
GCVal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsval ScriptValueVariable::GetJsval(const std::vector<BufferVariable*>& vars)
|
||||||
|
{
|
||||||
|
// Clean up from earlier invocations
|
||||||
|
if (GCVal)
|
||||||
|
{
|
||||||
|
JS_RemoveRoot(Context, GCVal);
|
||||||
|
GCVal = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (vars[ID]->Type)
|
||||||
|
{
|
||||||
|
case vartype_int:
|
||||||
|
{
|
||||||
|
int val = ((BufferVariable_int*)vars[ID])->value;
|
||||||
|
return INT_TO_JSVAL(val);
|
||||||
|
}
|
||||||
|
case vartype_double:
|
||||||
|
{
|
||||||
|
jsdouble* val = JS_NewDouble(Context, ((BufferVariable_double*)vars[ID])->value);
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
debug_warn("Error creating JS double");
|
||||||
|
return JSVAL_NULL;
|
||||||
|
}
|
||||||
|
GCVal = (void*)val;
|
||||||
|
JS_AddRoot(Context, val);
|
||||||
|
return DOUBLE_TO_JSVAL(val);
|
||||||
|
}
|
||||||
|
case vartype_string:
|
||||||
|
{
|
||||||
|
JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_string*)vars[ID])->value.str());
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
debug_warn("Error creating JS string");
|
||||||
|
return JSVAL_NULL;
|
||||||
|
}
|
||||||
|
GCVal = (void*)val;
|
||||||
|
JS_AddRoot(Context, val);
|
||||||
|
return STRING_TO_JSVAL(val);
|
||||||
|
}
|
||||||
|
case vartype_rawstring:
|
||||||
|
{
|
||||||
|
JSString* val = StringConvert::wchars_to_jsstring(Context, ((BufferVariable_rawstring*)vars[ID])->value.str());
|
||||||
|
if (!val)
|
||||||
|
{
|
||||||
|
debug_warn("Error creating JS string");
|
||||||
|
return JSVAL_NULL;
|
||||||
|
}
|
||||||
|
GCVal = (void*)val;
|
||||||
|
JS_AddRoot(Context, val);
|
||||||
|
return STRING_TO_JSVAL(val);
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
debug_warn("Invalid type");
|
||||||
|
return JSVAL_NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptValueVariable::~ScriptValueVariable()
|
||||||
|
{
|
||||||
|
if (GCVal)
|
||||||
|
JS_RemoveRoot(Context, GCVal);
|
||||||
|
}
|
94
source/i18n/ScriptInterface.h
Executable file
94
source/i18n/ScriptInterface.h
Executable file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
ScriptObject provides an interface to the JavaScript engine.
|
||||||
|
|
||||||
|
ScriptValues are used to generate jsvals from 'constants' (strings, ints)
|
||||||
|
and 'variables' (dependent on the type passed with "translate(...) <<").
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_SCRIPTINTERFACE_H
|
||||||
|
#define I18N_SCRIPTINTERFACE_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "BufferVariable.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
ERROR_SUBGROUP(I18n, Script);
|
||||||
|
ERROR_TYPE(I18n_Script, SetupFailed);
|
||||||
|
|
||||||
|
typedef _W64 long jsval;
|
||||||
|
typedef unsigned short jschar;
|
||||||
|
struct JSContext;
|
||||||
|
struct JSObject;
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
|
||||||
|
class ScriptValue;
|
||||||
|
class CLocale;
|
||||||
|
|
||||||
|
class ScriptObject
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptObject(CLocale* locale, JSContext* cx);
|
||||||
|
~ScriptObject();
|
||||||
|
|
||||||
|
bool ExecuteCode(const jschar* data, size_t len, const char* filename);
|
||||||
|
|
||||||
|
const StrImW CallFunction(const char* name, const std::vector<BufferVariable*>& vars, const std::vector<ScriptValue*>& params);
|
||||||
|
|
||||||
|
JSContext* Context;
|
||||||
|
JSObject* Object;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Virtual base class for script values
|
||||||
|
class ScriptValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual jsval GetJsval(const std::vector<BufferVariable*>& vars) = 0;
|
||||||
|
virtual ~ScriptValue() {};
|
||||||
|
|
||||||
|
protected:
|
||||||
|
JSContext* Context;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Particular types of script value:
|
||||||
|
|
||||||
|
class ScriptValueString : public ScriptValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptValueString(ScriptObject& script, const wchar_t* s);
|
||||||
|
~ScriptValueString();
|
||||||
|
jsval GetJsval(const std::vector<BufferVariable*>& vars);
|
||||||
|
|
||||||
|
private:
|
||||||
|
jsval Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptValueInteger : public ScriptValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptValueInteger(ScriptObject& script, const int v);
|
||||||
|
~ScriptValueInteger();
|
||||||
|
jsval GetJsval(const std::vector<BufferVariable*>& vars);
|
||||||
|
|
||||||
|
private:
|
||||||
|
jsval Value;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ScriptValueVariable : public ScriptValue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ScriptValueVariable(ScriptObject& script, const unsigned char id);
|
||||||
|
~ScriptValueVariable();
|
||||||
|
jsval GetJsval(const std::vector<BufferVariable*>& vars);
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char ID;
|
||||||
|
void* GCVal; // something that's being garbage-collected
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_SCRIPTINTERFACE_H
|
104
source/i18n/StrImmutable.h
Executable file
104
source/i18n/StrImmutable.h
Executable file
@ -0,0 +1,104 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
A reference-counted immutable string; more efficient than std::wstring
|
||||||
|
when returning strings from functions. (My i18n test program went ~20% faster,
|
||||||
|
which is not insignificant).
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_STRIMMUTABLE_H
|
||||||
|
#define I18N_STRIMMUTABLE_H
|
||||||
|
|
||||||
|
#include <wchar.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
typedef unsigned short jschar;
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
struct strImW_data
|
||||||
|
{
|
||||||
|
int refs;
|
||||||
|
wchar_t* data;
|
||||||
|
strImW_data() : refs(1) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StrImW
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
// I'm lazy (elsewhere), so allow construction from a variety
|
||||||
|
// of data types:
|
||||||
|
|
||||||
|
const StrImW(const wchar_t* s)
|
||||||
|
{
|
||||||
|
ref = new strImW_data;
|
||||||
|
size_t len = wcslen(s)+1;
|
||||||
|
ref->data = new wchar_t[len];
|
||||||
|
memcpy((void*)ref->data, s, len*sizeof(wchar_t));
|
||||||
|
}
|
||||||
|
|
||||||
|
const StrImW(const char* s)
|
||||||
|
{
|
||||||
|
ref = new strImW_data;
|
||||||
|
size_t len = strlen(s)+1;
|
||||||
|
ref->data = new wchar_t[len];
|
||||||
|
for (size_t i=0; i<len; ++i)
|
||||||
|
ref->data[i] = s[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// On non-MSVC, or on MSVC with a native wchar_t type, define jschar separately
|
||||||
|
#if !defined(_MSC_VER) || !defined(_WCHAR_T_DEFINED)
|
||||||
|
const StrImW(const jschar* s)
|
||||||
|
{
|
||||||
|
ref = new strImW_data;
|
||||||
|
size_t len = 0;
|
||||||
|
while (s[len] != '\0') ++len;
|
||||||
|
++len; // include the \0
|
||||||
|
ref->data = new wchar_t[len];
|
||||||
|
for (size_t i=0; i<len; ++i)
|
||||||
|
ref->data[i] = s[i];
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const StrImW(const jschar* s, size_t len)
|
||||||
|
{
|
||||||
|
ref = new strImW_data;
|
||||||
|
ref->data = new wchar_t[len+1];
|
||||||
|
for (size_t i=0; i<len; ++i)
|
||||||
|
ref->data[i] = s[i];
|
||||||
|
ref->data[len] = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
~StrImW()
|
||||||
|
{
|
||||||
|
if (--ref->refs == 0)
|
||||||
|
{
|
||||||
|
delete[] ref->data;
|
||||||
|
delete ref;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy constructor
|
||||||
|
const StrImW(const StrImW& s)
|
||||||
|
{
|
||||||
|
ref = s.ref;
|
||||||
|
++ref->refs;
|
||||||
|
}
|
||||||
|
|
||||||
|
const wchar_t* str() const
|
||||||
|
{
|
||||||
|
return ref->data;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const StrImW& s)
|
||||||
|
{
|
||||||
|
return s.ref==this->ref || wcscmp(s.ref->data, this->ref->data)==0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
strImW_data* ref;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_STRIMMUTABLE_H
|
70
source/i18n/StringBuffer.cpp
Executable file
70
source/i18n/StringBuffer.cpp
Executable file
@ -0,0 +1,70 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "CLocale.h"
|
||||||
|
#include "TSComponent.h"
|
||||||
|
#include "StringBuffer.h"
|
||||||
|
|
||||||
|
// Vaguely useful utility function for deleting stuff
|
||||||
|
template<typename T> void delete_fn(T* v) { delete v; }
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#define LOG_CATEGORY "i18n"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
I18n::StringBuffer::operator Str()
|
||||||
|
{
|
||||||
|
if (Variables.size() != String.VarCount)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "I18n: Incorrect number of parameters passed to Translate");
|
||||||
|
|
||||||
|
// Tidy up
|
||||||
|
std::for_each(Variables.begin(), Variables.end(), delete_fn<BufferVariable>);
|
||||||
|
|
||||||
|
return L"(translation error)";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (String.VarCount == 0)
|
||||||
|
return String.Parts[0]->ToString(Locale, Variables).str();
|
||||||
|
|
||||||
|
Str ret;
|
||||||
|
|
||||||
|
if (Locale->ReadCached(this, ret))
|
||||||
|
{
|
||||||
|
// Found in cache
|
||||||
|
|
||||||
|
// Clean up the current allocated data
|
||||||
|
std::for_each(Variables.begin(), Variables.end(), delete_fn<BufferVariable>);
|
||||||
|
// Return the cached string
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Not in cache - construct the string
|
||||||
|
for (std::vector<const TSComponent*>::iterator it = String.Parts.begin(),
|
||||||
|
end = String.Parts.end();
|
||||||
|
it != end; ++it)
|
||||||
|
{
|
||||||
|
ret += (*it)->ToString(Locale, Variables).str();
|
||||||
|
}
|
||||||
|
|
||||||
|
Locale->AddToCache(this, ret);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
I18n::StringBuffer::StringBuffer(TranslatedString& str, CLocale* loc)
|
||||||
|
: String(str), Locale(loc)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 I18n::StringBuffer::Hash()
|
||||||
|
{
|
||||||
|
u32 hash = 0;
|
||||||
|
size_t max = Variables.size();
|
||||||
|
for (size_t i = 0; i < max; ++i)
|
||||||
|
hash += Variables[i]->Hash();
|
||||||
|
return hash;
|
||||||
|
}
|
49
source/i18n/StringBuffer.h
Executable file
49
source/i18n/StringBuffer.h
Executable file
@ -0,0 +1,49 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Returned by Translate(), and used to collect variables from the user
|
||||||
|
before converting into a string.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_STRINGBUF_H
|
||||||
|
#define I18N_STRINGBUF_H
|
||||||
|
|
||||||
|
#include "Common.h"
|
||||||
|
#include "TranslatedString.h"
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
class CLocale;
|
||||||
|
class BufferVariable;
|
||||||
|
|
||||||
|
template<typename T> BufferVariable* NewBufferVariable(T v);
|
||||||
|
|
||||||
|
class StringBuffer
|
||||||
|
{
|
||||||
|
friend CLocale;
|
||||||
|
public:
|
||||||
|
// Builds and returns the finished string
|
||||||
|
operator Str();
|
||||||
|
|
||||||
|
// Stores the variable inside the StringBuffer,
|
||||||
|
// for later conversion into a string
|
||||||
|
template<typename T> StringBuffer& operator<< (T v)
|
||||||
|
{
|
||||||
|
// This BufferVariable is freed by the caching system (yuck)
|
||||||
|
Variables.push_back(NewBufferVariable(v));
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Returns a simple hash of the contents of Variables
|
||||||
|
u32 Hash();
|
||||||
|
|
||||||
|
private:
|
||||||
|
StringBuffer(TranslatedString&, CLocale*);
|
||||||
|
TranslatedString& String;
|
||||||
|
std::vector<BufferVariable*> Variables;
|
||||||
|
CLocale* Locale;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif I18N_STRINGBUF_H
|
41
source/i18n/TSComponent.cpp
Executable file
41
source/i18n/TSComponent.cpp
Executable file
@ -0,0 +1,41 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "TSComponent.h"
|
||||||
|
#include "CLocale.h"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
// Vaguely useful utility function for deleting stuff
|
||||||
|
template<typename T> void delete_fn(T* v) { delete v; }
|
||||||
|
|
||||||
|
const StrImW TSComponentString::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
|
||||||
|
{
|
||||||
|
return String;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************/
|
||||||
|
|
||||||
|
const StrImW TSComponentVariable::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
|
||||||
|
{
|
||||||
|
// This will never be out-of-bounds -- the number
|
||||||
|
// of parameters has been checked earlier
|
||||||
|
return vars[ID]->ToString(locale);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**************/
|
||||||
|
|
||||||
|
const StrImW TSComponentFunction::ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const
|
||||||
|
{
|
||||||
|
return locale->CallFunction(Name.c_str(), vars, Params);
|
||||||
|
}
|
||||||
|
|
||||||
|
void TSComponentFunction::AddParam(ScriptValue* p)
|
||||||
|
{
|
||||||
|
Params.push_back(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
TSComponentFunction::~TSComponentFunction()
|
||||||
|
{
|
||||||
|
for_each(Params.begin(), Params.end(), delete_fn<ScriptValue>);
|
||||||
|
}
|
||||||
|
|
73
source/i18n/TSComponent.h
Executable file
73
source/i18n/TSComponent.h
Executable file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Stores sections of a translated string: constant strings, simple variables,
|
||||||
|
and JS function calls, allowing conversion to strings.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_TSCOMPONENT_H
|
||||||
|
#define I18N_TSCOMPONENT_H
|
||||||
|
|
||||||
|
#include "StrImmutable.h"
|
||||||
|
#include "BufferVariable.h"
|
||||||
|
#include "ScriptInterface.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
class CLocale;
|
||||||
|
|
||||||
|
class TSComponent
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
virtual const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const = 0;
|
||||||
|
virtual ~TSComponent() {}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TSComponentString : public TSComponent {
|
||||||
|
public:
|
||||||
|
TSComponentString(const wchar_t* s) : String(s) {};
|
||||||
|
|
||||||
|
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
const StrImW String;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class TSComponentVariable : public TSComponent {
|
||||||
|
public:
|
||||||
|
TSComponentVariable(unsigned char id) : ID(id) {};
|
||||||
|
|
||||||
|
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned char ID;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TSComponentFunction : public TSComponent {
|
||||||
|
public:
|
||||||
|
TSComponentFunction(const char* name) : Name(name) {};
|
||||||
|
~TSComponentFunction();
|
||||||
|
|
||||||
|
const StrImW ToString(CLocale* locale, std::vector<BufferVariable*>& vars) const;
|
||||||
|
|
||||||
|
// AddParam is called when loading the function, building up the
|
||||||
|
// internal list of parameters (strings / ints / variables)
|
||||||
|
void AddParam(ScriptValue* p);
|
||||||
|
|
||||||
|
private:
|
||||||
|
const std::string Name;
|
||||||
|
std::vector<ScriptValue*> Params;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_TSCOMPONENT_H
|
12
source/i18n/TranslatedString.cpp
Executable file
12
source/i18n/TranslatedString.cpp
Executable file
@ -0,0 +1,12 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "TranslatedString.h"
|
||||||
|
#include "TSComponent.h"
|
||||||
|
|
||||||
|
using namespace I18n;
|
||||||
|
|
||||||
|
TranslatedString::~TranslatedString()
|
||||||
|
{
|
||||||
|
for (std::vector<const TSComponent*>::iterator it = Parts.begin(); it != Parts.end(); ++it)
|
||||||
|
delete *it;
|
||||||
|
}
|
28
source/i18n/TranslatedString.h
Executable file
28
source/i18n/TranslatedString.h
Executable file
@ -0,0 +1,28 @@
|
|||||||
|
/*
|
||||||
|
|
||||||
|
Simple storage for translated phrases, made up of lots of TSComponents
|
||||||
|
(strings / variables / functions)
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef I18N_TSTRING_H
|
||||||
|
#define I18N_TSTRING_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
class TSComponent;
|
||||||
|
|
||||||
|
class TranslatedString
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::vector<const TSComponent*> Parts;
|
||||||
|
unsigned char VarCount;
|
||||||
|
|
||||||
|
~TranslatedString();
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif // I18N_TSTRING_H
|
@ -56,6 +56,8 @@
|
|||||||
#include "ConfigDB.h"
|
#include "ConfigDB.h"
|
||||||
#include "CLogger.h"
|
#include "CLogger.h"
|
||||||
|
|
||||||
|
#include "ps/i18n.h"
|
||||||
|
|
||||||
#define LOG_CATEGORY "main"
|
#define LOG_CATEGORY "main"
|
||||||
|
|
||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
@ -485,7 +487,9 @@ static void Render()
|
|||||||
glTranslatef(10.0f, 10.0f, 0.0f);
|
glTranslatef(10.0f, 10.0f, 0.0f);
|
||||||
|
|
||||||
glScalef(1.0, -1.0, 1.0);
|
glScalef(1.0, -1.0, 1.0);
|
||||||
glwprintf( L"%d FPS", fps);
|
|
||||||
|
CStrW fps_display = translate(L"$num FPS") << fps;
|
||||||
|
glwprintf(fps_display);
|
||||||
|
|
||||||
oglCheck();
|
oglCheck();
|
||||||
|
|
||||||
@ -666,6 +670,8 @@ static void psInit()
|
|||||||
g_Console->m_iFontHeight = unifont_linespacing(g_Font_Console);
|
g_Console->m_iFontHeight = unifont_linespacing(g_Font_Console);
|
||||||
g_Console->m_iFontOffset = 9;
|
g_Console->m_iFontOffset = 9;
|
||||||
|
|
||||||
|
I18n::LoadLanguage("pseudogreek");
|
||||||
|
|
||||||
loadHotkeys();
|
loadHotkeys();
|
||||||
|
|
||||||
#ifndef NO_GUI
|
#ifndef NO_GUI
|
||||||
@ -694,6 +700,8 @@ static void psShutdown()
|
|||||||
CXeromyces::Terminate();
|
CXeromyces::Terminate();
|
||||||
|
|
||||||
MusicPlayer.release();
|
MusicPlayer.release();
|
||||||
|
|
||||||
|
I18n::Shutdown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -7,12 +7,14 @@
|
|||||||
class PSERROR_CVFSFile : public PSERROR {};
|
class PSERROR_CVFSFile : public PSERROR {};
|
||||||
class PSERROR_GUI : public PSERROR {};
|
class PSERROR_GUI : public PSERROR {};
|
||||||
class PSERROR_Game : public PSERROR {};
|
class PSERROR_Game : public PSERROR {};
|
||||||
|
class PSERROR_I18n : public PSERROR {};
|
||||||
class PSERROR_Renderer : public PSERROR {};
|
class PSERROR_Renderer : public PSERROR {};
|
||||||
class PSERROR_Scripting : public PSERROR {};
|
class PSERROR_Scripting : public PSERROR {};
|
||||||
class PSERROR_System : public PSERROR {};
|
class PSERROR_System : public PSERROR {};
|
||||||
class PSERROR_Xeromyces : public PSERROR {};
|
class PSERROR_Xeromyces : public PSERROR {};
|
||||||
|
|
||||||
class PSERROR_Game_World : public PSERROR_Game {};
|
class PSERROR_Game_World : public PSERROR_Game {};
|
||||||
|
class PSERROR_I18n_Script : public PSERROR_I18n {};
|
||||||
class PSERROR_Scripting_DefineType : public PSERROR_Scripting {};
|
class PSERROR_Scripting_DefineType : public PSERROR_Scripting {};
|
||||||
class PSERROR_Scripting_LoadFile : public PSERROR_Scripting {};
|
class PSERROR_Scripting_LoadFile : public PSERROR_Scripting {};
|
||||||
|
|
||||||
@ -22,6 +24,7 @@ class PSERROR_CVFSFile_LoadFailed : public PSERROR_CVFSFile { public: PSERROR_CV
|
|||||||
class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSERROR_GUI_JSOpenFailed(); };
|
class PSERROR_GUI_JSOpenFailed : public PSERROR_GUI { public: PSERROR_GUI_JSOpenFailed(); };
|
||||||
class PSERROR_GUI_TextureLoadFailed : public PSERROR_GUI { public: PSERROR_GUI_TextureLoadFailed(); };
|
class PSERROR_GUI_TextureLoadFailed : public PSERROR_GUI { public: PSERROR_GUI_TextureLoadFailed(); };
|
||||||
class PSERROR_Game_World_MapLoadFailed : public PSERROR_Game_World { public: PSERROR_Game_World_MapLoadFailed(); };
|
class PSERROR_Game_World_MapLoadFailed : public PSERROR_Game_World { public: PSERROR_Game_World_MapLoadFailed(); };
|
||||||
|
class PSERROR_I18n_Script_SetupFailed : public PSERROR_I18n_Script { public: PSERROR_I18n_Script_SetupFailed(); };
|
||||||
class PSERROR_Renderer_VBOFailed : public PSERROR_Renderer { public: PSERROR_Renderer_VBOFailed(); };
|
class PSERROR_Renderer_VBOFailed : public PSERROR_Renderer { public: PSERROR_Renderer_VBOFailed(); };
|
||||||
class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CallFunctionFailed(); };
|
class PSERROR_Scripting_CallFunctionFailed : public PSERROR_Scripting { public: PSERROR_Scripting_CallFunctionFailed(); };
|
||||||
class PSERROR_Scripting_ContextCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ContextCreationFailed(); };
|
class PSERROR_Scripting_ContextCreationFailed : public PSERROR_Scripting { public: PSERROR_Scripting_ContextCreationFailed(); };
|
||||||
@ -49,28 +52,29 @@ extern const PSRETURN PSRETURN_CVFSFile_InvalidBufferAccess = 0x01000002;
|
|||||||
extern const PSRETURN PSRETURN_CVFSFile_LoadFailed = 0x01000003;
|
extern const PSRETURN PSRETURN_CVFSFile_LoadFailed = 0x01000003;
|
||||||
extern const PSRETURN PSRETURN_GUI_JSOpenFailed = 0x02000001;
|
extern const PSRETURN PSRETURN_GUI_JSOpenFailed = 0x02000001;
|
||||||
extern const PSRETURN PSRETURN_GUI_TextureLoadFailed = 0x02000002;
|
extern const PSRETURN PSRETURN_GUI_TextureLoadFailed = 0x02000002;
|
||||||
extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x03030001;
|
extern const PSRETURN PSRETURN_Game_World_MapLoadFailed = 0x03040001;
|
||||||
extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x04000001;
|
extern const PSRETURN PSRETURN_I18n_Script_SetupFailed = 0x04030001;
|
||||||
extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x05010001;
|
extern const PSRETURN PSRETURN_Renderer_VBOFailed = 0x05000001;
|
||||||
extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x05010002;
|
extern const PSRETURN PSRETURN_Scripting_DefineType_AlreadyExists = 0x06010001;
|
||||||
extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x05020001;
|
extern const PSRETURN PSRETURN_Scripting_DefineType_CreationFailed = 0x06010002;
|
||||||
extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x05020002;
|
extern const PSRETURN PSRETURN_Scripting_LoadFile_EvalErrors = 0x06020001;
|
||||||
extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x05000001;
|
extern const PSRETURN PSRETURN_Scripting_LoadFile_OpenFailed = 0x06020002;
|
||||||
extern const PSRETURN PSRETURN_Scripting_ContextCreationFailed = 0x05000002;
|
extern const PSRETURN PSRETURN_Scripting_CallFunctionFailed = 0x06000001;
|
||||||
extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x05000003;
|
extern const PSRETURN PSRETURN_Scripting_ContextCreationFailed = 0x06000002;
|
||||||
extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x05000004;
|
extern const PSRETURN PSRETURN_Scripting_ConversionFailed = 0x06000003;
|
||||||
extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x05000005;
|
extern const PSRETURN PSRETURN_Scripting_CreateObjectFailed = 0x06000004;
|
||||||
extern const PSRETURN PSRETURN_Scripting_GlobalObjectCreationFailed = 0x05000006;
|
extern const PSRETURN PSRETURN_Scripting_DefineConstantFailed = 0x06000005;
|
||||||
extern const PSRETURN PSRETURN_Scripting_NativeFunctionSetupFailed = 0x05000007;
|
extern const PSRETURN PSRETURN_Scripting_GlobalObjectCreationFailed = 0x06000006;
|
||||||
extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x05000008;
|
extern const PSRETURN PSRETURN_Scripting_NativeFunctionSetupFailed = 0x06000007;
|
||||||
extern const PSRETURN PSRETURN_Scripting_RuntimeCreationFailed = 0x05000009;
|
extern const PSRETURN PSRETURN_Scripting_RegisterFunctionFailed = 0x06000008;
|
||||||
extern const PSRETURN PSRETURN_Scripting_StandardClassSetupFailed = 0x0500000a;
|
extern const PSRETURN PSRETURN_Scripting_RuntimeCreationFailed = 0x06000009;
|
||||||
extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x0500000b;
|
extern const PSRETURN PSRETURN_Scripting_StandardClassSetupFailed = 0x0600000a;
|
||||||
extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x06000001;
|
extern const PSRETURN PSRETURN_Scripting_TypeDoesNotExist = 0x0600000b;
|
||||||
extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x06000002;
|
extern const PSRETURN PSRETURN_System_RequiredExtensionsMissing = 0x07000001;
|
||||||
extern const PSRETURN PSRETURN_System_VmodeFailed = 0x06000003;
|
extern const PSRETURN PSRETURN_System_SDLInitFailed = 0x07000002;
|
||||||
extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x07000001;
|
extern const PSRETURN PSRETURN_System_VmodeFailed = 0x07000003;
|
||||||
extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x07000002;
|
extern const PSRETURN PSRETURN_Xeromyces_XMLOpenFailed = 0x08000001;
|
||||||
|
extern const PSRETURN PSRETURN_Xeromyces_XMLParseError = 0x08000002;
|
||||||
|
|
||||||
extern const PSRETURN MASK__PSRETURN_CVFSFile = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_CVFSFile = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_CVFSFile = 0x01000000;
|
extern const PSRETURN CODE__PSRETURN_CVFSFile = 0x01000000;
|
||||||
@ -78,20 +82,24 @@ extern const PSRETURN MASK__PSRETURN_GUI = 0xff000000;
|
|||||||
extern const PSRETURN CODE__PSRETURN_GUI = 0x02000000;
|
extern const PSRETURN CODE__PSRETURN_GUI = 0x02000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Game = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_Game = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Game = 0x03000000;
|
extern const PSRETURN CODE__PSRETURN_Game = 0x03000000;
|
||||||
|
extern const PSRETURN MASK__PSRETURN_I18n = 0xff000000;
|
||||||
|
extern const PSRETURN CODE__PSRETURN_I18n = 0x04000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Renderer = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_Renderer = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Renderer = 0x04000000;
|
extern const PSRETURN CODE__PSRETURN_Renderer = 0x05000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_Scripting = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting = 0x05000000;
|
extern const PSRETURN CODE__PSRETURN_Scripting = 0x06000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_System = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_System = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_System = 0x06000000;
|
extern const PSRETURN CODE__PSRETURN_System = 0x07000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Xeromyces = 0xff000000;
|
extern const PSRETURN MASK__PSRETURN_Xeromyces = 0xff000000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x07000000;
|
extern const PSRETURN CODE__PSRETURN_Xeromyces = 0x08000000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Game_World = 0xffff0000;
|
extern const PSRETURN MASK__PSRETURN_Game_World = 0xffff0000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Game_World = 0x03030000;
|
extern const PSRETURN CODE__PSRETURN_Game_World = 0x03040000;
|
||||||
|
extern const PSRETURN MASK__PSRETURN_I18n_Script = 0xffff0000;
|
||||||
|
extern const PSRETURN CODE__PSRETURN_I18n_Script = 0x04030000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType = 0xffff0000;
|
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType = 0xffff0000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x05010000;
|
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType = 0x06010000;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile = 0xffff0000;
|
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile = 0xffff0000;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x05020000;
|
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile = 0x06020000;
|
||||||
|
|
||||||
extern const PSRETURN MASK__PSRETURN_CVFSFile_AlreadyLoaded = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_CVFSFile_AlreadyLoaded = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_CVFSFile_AlreadyLoaded = 0x01000001;
|
extern const PSRETURN CODE__PSRETURN_CVFSFile_AlreadyLoaded = 0x01000001;
|
||||||
@ -104,77 +112,80 @@ extern const PSRETURN CODE__PSRETURN_GUI_JSOpenFailed = 0x02000001;
|
|||||||
extern const PSRETURN MASK__PSRETURN_GUI_TextureLoadFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_GUI_TextureLoadFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_GUI_TextureLoadFailed = 0x02000002;
|
extern const PSRETURN CODE__PSRETURN_GUI_TextureLoadFailed = 0x02000002;
|
||||||
extern const PSRETURN MASK__PSRETURN_Game_World_MapLoadFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Game_World_MapLoadFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x03030001;
|
extern const PSRETURN CODE__PSRETURN_Game_World_MapLoadFailed = 0x03040001;
|
||||||
|
extern const PSRETURN MASK__PSRETURN_I18n_Script_SetupFailed = 0xffffffff;
|
||||||
|
extern const PSRETURN CODE__PSRETURN_I18n_Script_SetupFailed = 0x04030001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Renderer_VBOFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Renderer_VBOFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x04000001;
|
extern const PSRETURN CODE__PSRETURN_Renderer_VBOFailed = 0x05000001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_AlreadyExists = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_AlreadyExists = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x05010001;
|
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_AlreadyExists = 0x06010001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_CreationFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_DefineType_CreationFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x05010002;
|
extern const PSRETURN CODE__PSRETURN_Scripting_DefineType_CreationFailed = 0x06010002;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_EvalErrors = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_EvalErrors = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x05020001;
|
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_EvalErrors = 0x06020001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_OpenFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_LoadFile_OpenFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x05020002;
|
extern const PSRETURN CODE__PSRETURN_Scripting_LoadFile_OpenFailed = 0x06020002;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_CallFunctionFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_CallFunctionFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x05000001;
|
extern const PSRETURN CODE__PSRETURN_Scripting_CallFunctionFailed = 0x06000001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_ContextCreationFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_ContextCreationFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_ContextCreationFailed = 0x05000002;
|
extern const PSRETURN CODE__PSRETURN_Scripting_ContextCreationFailed = 0x06000002;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_ConversionFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_ConversionFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x05000003;
|
extern const PSRETURN CODE__PSRETURN_Scripting_ConversionFailed = 0x06000003;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_CreateObjectFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_CreateObjectFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x05000004;
|
extern const PSRETURN CODE__PSRETURN_Scripting_CreateObjectFailed = 0x06000004;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_DefineConstantFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_DefineConstantFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x05000005;
|
extern const PSRETURN CODE__PSRETURN_Scripting_DefineConstantFailed = 0x06000005;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_GlobalObjectCreationFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_GlobalObjectCreationFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_GlobalObjectCreationFailed = 0x05000006;
|
extern const PSRETURN CODE__PSRETURN_Scripting_GlobalObjectCreationFailed = 0x06000006;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_NativeFunctionSetupFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_NativeFunctionSetupFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_NativeFunctionSetupFailed = 0x05000007;
|
extern const PSRETURN CODE__PSRETURN_Scripting_NativeFunctionSetupFailed = 0x06000007;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_RegisterFunctionFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_RegisterFunctionFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x05000008;
|
extern const PSRETURN CODE__PSRETURN_Scripting_RegisterFunctionFailed = 0x06000008;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_RuntimeCreationFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_RuntimeCreationFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_RuntimeCreationFailed = 0x05000009;
|
extern const PSRETURN CODE__PSRETURN_Scripting_RuntimeCreationFailed = 0x06000009;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_StandardClassSetupFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_StandardClassSetupFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_StandardClassSetupFailed = 0x0500000a;
|
extern const PSRETURN CODE__PSRETURN_Scripting_StandardClassSetupFailed = 0x0600000a;
|
||||||
extern const PSRETURN MASK__PSRETURN_Scripting_TypeDoesNotExist = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Scripting_TypeDoesNotExist = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x0500000b;
|
extern const PSRETURN CODE__PSRETURN_Scripting_TypeDoesNotExist = 0x0600000b;
|
||||||
extern const PSRETURN MASK__PSRETURN_System_RequiredExtensionsMissing = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_System_RequiredExtensionsMissing = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x06000001;
|
extern const PSRETURN CODE__PSRETURN_System_RequiredExtensionsMissing = 0x07000001;
|
||||||
extern const PSRETURN MASK__PSRETURN_System_SDLInitFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_System_SDLInitFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x06000002;
|
extern const PSRETURN CODE__PSRETURN_System_SDLInitFailed = 0x07000002;
|
||||||
extern const PSRETURN MASK__PSRETURN_System_VmodeFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_System_VmodeFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x06000003;
|
extern const PSRETURN CODE__PSRETURN_System_VmodeFailed = 0x07000003;
|
||||||
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLOpenFailed = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLOpenFailed = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x07000001;
|
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLOpenFailed = 0x08000001;
|
||||||
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLParseError = 0xffffffff;
|
extern const PSRETURN MASK__PSRETURN_Xeromyces_XMLParseError = 0xffffffff;
|
||||||
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x07000002;
|
extern const PSRETURN CODE__PSRETURN_Xeromyces_XMLParseError = 0x08000002;
|
||||||
|
|
||||||
PSERROR_CVFSFile_AlreadyLoaded::PSERROR_CVFSFile_AlreadyLoaded() { magic=0x45725221; code=0x01000001; }
|
PSERROR_CVFSFile_AlreadyLoaded::PSERROR_CVFSFile_AlreadyLoaded() { magic=0x45725221; code=0x01000001; }
|
||||||
PSERROR_CVFSFile_InvalidBufferAccess::PSERROR_CVFSFile_InvalidBufferAccess() { magic=0x45725221; code=0x01000002; }
|
PSERROR_CVFSFile_InvalidBufferAccess::PSERROR_CVFSFile_InvalidBufferAccess() { magic=0x45725221; code=0x01000002; }
|
||||||
PSERROR_CVFSFile_LoadFailed::PSERROR_CVFSFile_LoadFailed() { magic=0x45725221; code=0x01000003; }
|
PSERROR_CVFSFile_LoadFailed::PSERROR_CVFSFile_LoadFailed() { magic=0x45725221; code=0x01000003; }
|
||||||
PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed() { magic=0x45725221; code=0x02000001; }
|
PSERROR_GUI_JSOpenFailed::PSERROR_GUI_JSOpenFailed() { magic=0x45725221; code=0x02000001; }
|
||||||
PSERROR_GUI_TextureLoadFailed::PSERROR_GUI_TextureLoadFailed() { magic=0x45725221; code=0x02000002; }
|
PSERROR_GUI_TextureLoadFailed::PSERROR_GUI_TextureLoadFailed() { magic=0x45725221; code=0x02000002; }
|
||||||
PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed() { magic=0x45725221; code=0x03030001; }
|
PSERROR_Game_World_MapLoadFailed::PSERROR_Game_World_MapLoadFailed() { magic=0x45725221; code=0x03040001; }
|
||||||
PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed() { magic=0x45725221; code=0x04000001; }
|
PSERROR_I18n_Script_SetupFailed::PSERROR_I18n_Script_SetupFailed() { magic=0x45725221; code=0x04030001; }
|
||||||
PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() { magic=0x45725221; code=0x05010001; }
|
PSERROR_Renderer_VBOFailed::PSERROR_Renderer_VBOFailed() { magic=0x45725221; code=0x05000001; }
|
||||||
PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() { magic=0x45725221; code=0x05010002; }
|
PSERROR_Scripting_DefineType_AlreadyExists::PSERROR_Scripting_DefineType_AlreadyExists() { magic=0x45725221; code=0x06010001; }
|
||||||
PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() { magic=0x45725221; code=0x05020001; }
|
PSERROR_Scripting_DefineType_CreationFailed::PSERROR_Scripting_DefineType_CreationFailed() { magic=0x45725221; code=0x06010002; }
|
||||||
PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() { magic=0x45725221; code=0x05020002; }
|
PSERROR_Scripting_LoadFile_EvalErrors::PSERROR_Scripting_LoadFile_EvalErrors() { magic=0x45725221; code=0x06020001; }
|
||||||
PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() { magic=0x45725221; code=0x05000001; }
|
PSERROR_Scripting_LoadFile_OpenFailed::PSERROR_Scripting_LoadFile_OpenFailed() { magic=0x45725221; code=0x06020002; }
|
||||||
PSERROR_Scripting_ContextCreationFailed::PSERROR_Scripting_ContextCreationFailed() { magic=0x45725221; code=0x05000002; }
|
PSERROR_Scripting_CallFunctionFailed::PSERROR_Scripting_CallFunctionFailed() { magic=0x45725221; code=0x06000001; }
|
||||||
PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() { magic=0x45725221; code=0x05000003; }
|
PSERROR_Scripting_ContextCreationFailed::PSERROR_Scripting_ContextCreationFailed() { magic=0x45725221; code=0x06000002; }
|
||||||
PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() { magic=0x45725221; code=0x05000004; }
|
PSERROR_Scripting_ConversionFailed::PSERROR_Scripting_ConversionFailed() { magic=0x45725221; code=0x06000003; }
|
||||||
PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() { magic=0x45725221; code=0x05000005; }
|
PSERROR_Scripting_CreateObjectFailed::PSERROR_Scripting_CreateObjectFailed() { magic=0x45725221; code=0x06000004; }
|
||||||
PSERROR_Scripting_GlobalObjectCreationFailed::PSERROR_Scripting_GlobalObjectCreationFailed() { magic=0x45725221; code=0x05000006; }
|
PSERROR_Scripting_DefineConstantFailed::PSERROR_Scripting_DefineConstantFailed() { magic=0x45725221; code=0x06000005; }
|
||||||
PSERROR_Scripting_NativeFunctionSetupFailed::PSERROR_Scripting_NativeFunctionSetupFailed() { magic=0x45725221; code=0x05000007; }
|
PSERROR_Scripting_GlobalObjectCreationFailed::PSERROR_Scripting_GlobalObjectCreationFailed() { magic=0x45725221; code=0x06000006; }
|
||||||
PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() { magic=0x45725221; code=0x05000008; }
|
PSERROR_Scripting_NativeFunctionSetupFailed::PSERROR_Scripting_NativeFunctionSetupFailed() { magic=0x45725221; code=0x06000007; }
|
||||||
PSERROR_Scripting_RuntimeCreationFailed::PSERROR_Scripting_RuntimeCreationFailed() { magic=0x45725221; code=0x05000009; }
|
PSERROR_Scripting_RegisterFunctionFailed::PSERROR_Scripting_RegisterFunctionFailed() { magic=0x45725221; code=0x06000008; }
|
||||||
PSERROR_Scripting_StandardClassSetupFailed::PSERROR_Scripting_StandardClassSetupFailed() { magic=0x45725221; code=0x0500000a; }
|
PSERROR_Scripting_RuntimeCreationFailed::PSERROR_Scripting_RuntimeCreationFailed() { magic=0x45725221; code=0x06000009; }
|
||||||
PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() { magic=0x45725221; code=0x0500000b; }
|
PSERROR_Scripting_StandardClassSetupFailed::PSERROR_Scripting_StandardClassSetupFailed() { magic=0x45725221; code=0x0600000a; }
|
||||||
PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing() { magic=0x45725221; code=0x06000001; }
|
PSERROR_Scripting_TypeDoesNotExist::PSERROR_Scripting_TypeDoesNotExist() { magic=0x45725221; code=0x0600000b; }
|
||||||
PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed() { magic=0x45725221; code=0x06000002; }
|
PSERROR_System_RequiredExtensionsMissing::PSERROR_System_RequiredExtensionsMissing() { magic=0x45725221; code=0x07000001; }
|
||||||
PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed() { magic=0x45725221; code=0x06000003; }
|
PSERROR_System_SDLInitFailed::PSERROR_System_SDLInitFailed() { magic=0x45725221; code=0x07000002; }
|
||||||
PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x45725221; code=0x07000001; }
|
PSERROR_System_VmodeFailed::PSERROR_System_VmodeFailed() { magic=0x45725221; code=0x07000003; }
|
||||||
PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x45725221; code=0x07000002; }
|
PSERROR_Xeromyces_XMLOpenFailed::PSERROR_Xeromyces_XMLOpenFailed() { magic=0x45725221; code=0x08000001; }
|
||||||
|
PSERROR_Xeromyces_XMLParseError::PSERROR_Xeromyces_XMLParseError() { magic=0x45725221; code=0x08000002; }
|
||||||
|
|
||||||
const wchar_t* GetErrorString(PSRETURN code)
|
const wchar_t* GetErrorString(PSRETURN code)
|
||||||
{
|
{
|
||||||
@ -185,28 +196,29 @@ const wchar_t* GetErrorString(PSRETURN code)
|
|||||||
case 0x01000003: return L"CVFSFile_LoadFailed"; break;
|
case 0x01000003: return L"CVFSFile_LoadFailed"; break;
|
||||||
case 0x02000001: return L"GUI_JSOpenFailed"; break;
|
case 0x02000001: return L"GUI_JSOpenFailed"; break;
|
||||||
case 0x02000002: return L"GUI_TextureLoadFailed"; break;
|
case 0x02000002: return L"GUI_TextureLoadFailed"; break;
|
||||||
case 0x03030001: return L"Game_World_MapLoadFailed"; break;
|
case 0x03040001: return L"Game_World_MapLoadFailed"; break;
|
||||||
case 0x04000001: return L"Renderer_VBOFailed"; break;
|
case 0x04030001: return L"I18n_Script_SetupFailed"; break;
|
||||||
case 0x05010001: return L"Scripting_DefineType_AlreadyExists"; break;
|
case 0x05000001: return L"Renderer_VBOFailed"; break;
|
||||||
case 0x05010002: return L"Scripting_DefineType_CreationFailed"; break;
|
case 0x06010001: return L"Scripting_DefineType_AlreadyExists"; break;
|
||||||
case 0x05020001: return L"Scripting_LoadFile_EvalErrors"; break;
|
case 0x06010002: return L"Scripting_DefineType_CreationFailed"; break;
|
||||||
case 0x05020002: return L"Scripting_LoadFile_OpenFailed"; break;
|
case 0x06020001: return L"Scripting_LoadFile_EvalErrors"; break;
|
||||||
case 0x05000001: return L"Scripting_CallFunctionFailed"; break;
|
case 0x06020002: return L"Scripting_LoadFile_OpenFailed"; break;
|
||||||
case 0x05000002: return L"Scripting_ContextCreationFailed"; break;
|
case 0x06000001: return L"Scripting_CallFunctionFailed"; break;
|
||||||
case 0x05000003: return L"Scripting_ConversionFailed"; break;
|
case 0x06000002: return L"Scripting_ContextCreationFailed"; break;
|
||||||
case 0x05000004: return L"Scripting_CreateObjectFailed"; break;
|
case 0x06000003: return L"Scripting_ConversionFailed"; break;
|
||||||
case 0x05000005: return L"Scripting_DefineConstantFailed"; break;
|
case 0x06000004: return L"Scripting_CreateObjectFailed"; break;
|
||||||
case 0x05000006: return L"Scripting_GlobalObjectCreationFailed"; break;
|
case 0x06000005: return L"Scripting_DefineConstantFailed"; break;
|
||||||
case 0x05000007: return L"Scripting_NativeFunctionSetupFailed"; break;
|
case 0x06000006: return L"Scripting_GlobalObjectCreationFailed"; break;
|
||||||
case 0x05000008: return L"Scripting_RegisterFunctionFailed"; break;
|
case 0x06000007: return L"Scripting_NativeFunctionSetupFailed"; break;
|
||||||
case 0x05000009: return L"Scripting_RuntimeCreationFailed"; break;
|
case 0x06000008: return L"Scripting_RegisterFunctionFailed"; break;
|
||||||
case 0x0500000a: return L"Scripting_StandardClassSetupFailed"; break;
|
case 0x06000009: return L"Scripting_RuntimeCreationFailed"; break;
|
||||||
case 0x0500000b: return L"Scripting_TypeDoesNotExist"; break;
|
case 0x0600000a: return L"Scripting_StandardClassSetupFailed"; break;
|
||||||
case 0x06000001: return L"System_RequiredExtensionsMissing"; break;
|
case 0x0600000b: return L"Scripting_TypeDoesNotExist"; break;
|
||||||
case 0x06000002: return L"System_SDLInitFailed"; break;
|
case 0x07000001: return L"System_RequiredExtensionsMissing"; break;
|
||||||
case 0x06000003: return L"System_VmodeFailed"; break;
|
case 0x07000002: return L"System_SDLInitFailed"; break;
|
||||||
case 0x07000001: return L"Xeromyces_XMLOpenFailed"; break;
|
case 0x07000003: return L"System_VmodeFailed"; break;
|
||||||
case 0x07000002: return L"Xeromyces_XMLParseError"; break;
|
case 0x08000001: return L"Xeromyces_XMLOpenFailed"; break;
|
||||||
|
case 0x08000002: return L"Xeromyces_XMLParseError"; break;
|
||||||
}
|
}
|
||||||
return L"Unrecognised error";
|
return L"Unrecognised error";
|
||||||
}
|
}
|
||||||
|
53
source/ps/StringConvert.cpp
Executable file
53
source/ps/StringConvert.cpp
Executable file
@ -0,0 +1,53 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "StringConvert.h"
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include "jsapi.h"
|
||||||
|
|
||||||
|
#if SDL_BYTE_ORDER == SDL_BIG_ENDIAN
|
||||||
|
#define ucs2le_to_wchart(ptr) (wchar_t)( (u16) ((u8*)ptr)[0] | (u16) ( ((u8*)ptr)[1] << 8) )
|
||||||
|
#else
|
||||||
|
#define ucs2le_to_wchart(ptr) (wchar_t)(*ptr);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
JSString* StringConvert::wstring_to_jsstring(JSContext* cx, const std::wstring& str)
|
||||||
|
{
|
||||||
|
size_t len = str.length();
|
||||||
|
jschar* data = (jschar*)JS_malloc(cx, len*sizeof(jschar));
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
for (size_t i=0; i<len; ++i)
|
||||||
|
data[i] = str[i];
|
||||||
|
return JS_NewUCString(cx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
JSString* StringConvert::wchars_to_jsstring(JSContext* cx, const wchar_t* chars)
|
||||||
|
{
|
||||||
|
size_t len = wcslen(chars);
|
||||||
|
jschar* data = (jschar*)JS_malloc(cx, len*sizeof(jschar));
|
||||||
|
if (!data)
|
||||||
|
return NULL;
|
||||||
|
for (size_t i=0; i<len; ++i)
|
||||||
|
data[i] = chars[i];
|
||||||
|
return JS_NewUCString(cx, data, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StringConvert::jschars_to_wstring(const jschar* chars, size_t len, std::wstring& result)
|
||||||
|
{
|
||||||
|
assert(result.empty());
|
||||||
|
result.reserve(len);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < len; ++i)
|
||||||
|
result += chars[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void StringConvert::ucs2le_to_wstring(const char* start, const char* end, std::wstring& result)
|
||||||
|
{
|
||||||
|
assert(result.empty());
|
||||||
|
result.reserve(end-start);
|
||||||
|
|
||||||
|
for (const char* pos = start; pos < end; pos += 2)
|
||||||
|
result += ucs2le_to_wchart(pos);
|
||||||
|
}
|
19
source/ps/StringConvert.h
Executable file
19
source/ps/StringConvert.h
Executable file
@ -0,0 +1,19 @@
|
|||||||
|
typedef unsigned short jschar;
|
||||||
|
typedef unsigned short ucs2char;
|
||||||
|
|
||||||
|
struct JSString;
|
||||||
|
struct JSContext;
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace StringConvert
|
||||||
|
{
|
||||||
|
// A random collection of conversion functions:
|
||||||
|
|
||||||
|
JSString* wstring_to_jsstring(JSContext* cx, const std::wstring& str);
|
||||||
|
JSString* wchars_to_jsstring(JSContext* cx, const wchar_t* chars);
|
||||||
|
|
||||||
|
void jschars_to_wstring(const jschar* chars, size_t len, std::wstring& result);
|
||||||
|
void ucs2le_to_wstring(const char* start, const char* end, std::wstring& result);
|
||||||
|
|
||||||
|
}
|
52
source/ps/i18n.cpp
Executable file
52
source/ps/i18n.cpp
Executable file
@ -0,0 +1,52 @@
|
|||||||
|
#include "precompiled.h"
|
||||||
|
|
||||||
|
#include "ps/i18n.h"
|
||||||
|
#include "ps/CVFSFile.h"
|
||||||
|
#include "scripting/ScriptingHost.h"
|
||||||
|
|
||||||
|
#include "ps/CLogger.h"
|
||||||
|
#define LOG_CATEGORY "i18n"
|
||||||
|
|
||||||
|
I18n::CLocale_interface* g_CurrentLocale = NULL;
|
||||||
|
|
||||||
|
bool I18n::LoadLanguage(const char* name)
|
||||||
|
{
|
||||||
|
CLocale_interface* locale_ptr = I18n::NewLocale(g_ScriptingHost.getContext());
|
||||||
|
|
||||||
|
if (! locale_ptr)
|
||||||
|
{
|
||||||
|
debug_warn("Failed to create locale");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::auto_ptr<CLocale_interface> locale (locale_ptr);
|
||||||
|
|
||||||
|
{
|
||||||
|
CVFSFile strings;
|
||||||
|
|
||||||
|
CStr filename = CStr("language/")+name+"/phrases.lng";
|
||||||
|
|
||||||
|
if (strings.Load(filename) != PSRETURN_OK)
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "Error opening language string file '%s'", filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! locale->LoadStrings( (const char*) strings.GetBuffer() ))
|
||||||
|
{
|
||||||
|
LOG(ERROR, LOG_CATEGORY, "Error loading language string file '%s'", filename.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Free any previously loaded data
|
||||||
|
delete g_CurrentLocale;
|
||||||
|
|
||||||
|
g_CurrentLocale = locale.release();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void I18n::Shutdown()
|
||||||
|
{
|
||||||
|
delete g_CurrentLocale;
|
||||||
|
}
|
11
source/ps/i18n.h
Executable file
11
source/ps/i18n.h
Executable file
@ -0,0 +1,11 @@
|
|||||||
|
#include "i18n/Interface.h"
|
||||||
|
|
||||||
|
extern I18n::CLocale_interface* g_CurrentLocale;
|
||||||
|
|
||||||
|
#define translate(s) g_CurrentLocale->Translate(s)
|
||||||
|
|
||||||
|
namespace I18n
|
||||||
|
{
|
||||||
|
bool LoadLanguage(const char* name);
|
||||||
|
void Shutdown();
|
||||||
|
}
|
@ -14,6 +14,8 @@
|
|||||||
#include "Network/Server.h"
|
#include "Network/Server.h"
|
||||||
#include "Network/Client.h"
|
#include "Network/Client.h"
|
||||||
|
|
||||||
|
#include "ps/i18n.h"
|
||||||
|
|
||||||
#include "scripting/JSInterface_Entity.h"
|
#include "scripting/JSInterface_Entity.h"
|
||||||
#include "scripting/JSInterface_BaseEntity.h"
|
#include "scripting/JSInterface_BaseEntity.h"
|
||||||
#include "scripting/JSInterface_Vector3D.h"
|
#include "scripting/JSInterface_Vector3D.h"
|
||||||
@ -49,6 +51,7 @@ JSFunctionSpec ScriptFunctionTable[] =
|
|||||||
{"endGame", endGame, 0, 0, 0 },
|
{"endGame", endGame, 0, 0, 0 },
|
||||||
{"joinGame", joinGame, 0, 0, 0 },
|
{"joinGame", joinGame, 0, 0, 0 },
|
||||||
{"startServer", startServer, 0, 0, 0 },
|
{"startServer", startServer, 0, 0, 0 },
|
||||||
|
{"loadLanguage", loadLanguage, 1, 0, 0 },
|
||||||
|
|
||||||
{"exit", exitProgram, 0, 0, 0 },
|
{"exit", exitProgram, 0, 0, 0 },
|
||||||
{"crash", crash, 0, 0, 0 },
|
{"crash", crash, 0, 0, 0 },
|
||||||
@ -357,6 +360,21 @@ JSBool endGame(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObje
|
|||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSBool loadLanguage(JSContext* cx, JSObject* UNUSEDPARAM(globalObject), unsigned int argc, jsval* argv, jsval* rval)
|
||||||
|
{
|
||||||
|
if (argc != 1)
|
||||||
|
{
|
||||||
|
JS_ReportError(cx, "loadLanguage: needs 1 parameter");
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
CStr lang = g_ScriptingHost.ValueToString(argv[0]);
|
||||||
|
I18n::LoadLanguage(lang);
|
||||||
|
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
extern void kill_mainloop(); // from main.cpp
|
extern void kill_mainloop(); // from main.cpp
|
||||||
|
|
||||||
JSBool exitProgram(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
|
JSBool exitProgram(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
|
||||||
@ -368,7 +386,7 @@ JSBool exitProgram(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(global
|
|||||||
JSBool crash(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
|
JSBool crash(JSContext* UNUSEDPARAM(context), JSObject* UNUSEDPARAM(globalObject), unsigned int UNUSEDPARAM(argc), jsval* UNUSEDPARAM(argv), jsval* UNUSEDPARAM(rval))
|
||||||
{
|
{
|
||||||
MICROLOG(L"Crashing at user's request.");
|
MICROLOG(L"Crashing at user's request.");
|
||||||
uintptr_t ptr = 0xDEADC0DE;
|
uintptr_t ptr = 0xDEADC0DE; // oh dear, might this be an invalid pointer?
|
||||||
return *(JSBool*) ptr;
|
return *(JSBool*) ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,13 +27,16 @@ JSBool joinGame(JSContext* context, JSObject* globalObject, unsigned int argc, j
|
|||||||
JSBool startGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
JSBool startGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
JSBool endGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
JSBool endGame(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
|
|
||||||
|
// Replaces the current language (locale) with a new one
|
||||||
|
JSBool loadLanguage(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
|
|
||||||
// Tells the main loop to stop looping
|
// Tells the main loop to stop looping
|
||||||
JSBool exitProgram(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
JSBool exitProgram(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
|
|
||||||
// Crashes.
|
// Crashes.
|
||||||
JSBool crash(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
JSBool crash(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
|
|
||||||
// Tries to print the amount of remaining video memory
|
// Tries to print the amount of remaining video memory. (I don't like starting functions with underscores).
|
||||||
JSBool js_mem(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
JSBool js_mem(JSContext* context, JSObject* globalObject, unsigned int argc, jsval* argv, jsval* rval);
|
||||||
|
|
||||||
extern JSFunctionSpec ScriptFunctionTable[];
|
extern JSFunctionSpec ScriptFunctionTable[];
|
||||||
|
@ -25,39 +25,52 @@ package.files = {
|
|||||||
{ sourcesfromdirs("../../ps") },
|
{ sourcesfromdirs("../../ps") },
|
||||||
{ sourcesfromdirs("../../ps/scripting") },
|
{ sourcesfromdirs("../../ps/scripting") },
|
||||||
{ sourcesfromdirs("../../ps/Network") },
|
{ sourcesfromdirs("../../ps/Network") },
|
||||||
|
|
||||||
-- simulation/
|
-- simulation/
|
||||||
{ sourcesfromdirs("../../simulation") },
|
{ sourcesfromdirs("../../simulation") },
|
||||||
{ sourcesfromdirs("../../simulation/scripting") },
|
{ sourcesfromdirs("../../simulation/scripting") },
|
||||||
|
|
||||||
-- lib/
|
-- lib/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../lib",
|
"../../lib",
|
||||||
"../../lib/sysdep",
|
"../../lib/sysdep",
|
||||||
"../../lib/res") },
|
"../../lib/res") },
|
||||||
|
|
||||||
-- graphics/
|
-- graphics/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../graphics") },
|
"../../graphics") },
|
||||||
{ sourcesfromdirs( "../../graphics/scripting" ) },
|
{ sourcesfromdirs( "../../graphics/scripting" ) },
|
||||||
|
|
||||||
-- maths/
|
-- maths/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../maths") },
|
"../../maths") },
|
||||||
{ sourcesfromdirs( "../../maths/scripting" ) },
|
{ sourcesfromdirs( "../../maths/scripting" ) },
|
||||||
|
|
||||||
-- renderer/
|
-- renderer/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../renderer") },
|
"../../renderer") },
|
||||||
|
|
||||||
-- gui/
|
-- gui/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../gui") },
|
"../../gui") },
|
||||||
{ sourcesfromdirs( "../../gui/scripting" ) },
|
{ sourcesfromdirs( "../../gui/scripting" ) },
|
||||||
|
|
||||||
-- terrain/
|
-- terrain/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../terrain") },
|
"../../terrain") },
|
||||||
|
|
||||||
-- sound/
|
-- sound/
|
||||||
{ sourcesfromdirs(
|
{ sourcesfromdirs(
|
||||||
"../../sound") },
|
"../../sound") },
|
||||||
|
|
||||||
-- main
|
-- main
|
||||||
{ "../../main.cpp" },
|
{ "../../main.cpp" },
|
||||||
|
|
||||||
-- scripting
|
-- scripting
|
||||||
{ sourcesfromdirs("../../scripting") }
|
{ sourcesfromdirs("../../scripting") },
|
||||||
|
|
||||||
|
-- i18n
|
||||||
|
{ sourcesfromdirs("../../i18n") }
|
||||||
}
|
}
|
||||||
|
|
||||||
package.includepaths = {
|
package.includepaths = {
|
||||||
|
Loading…
Reference in New Issue
Block a user