Delete obsolete non-working i18n code

This was SVN commit r8527.
This commit is contained in:
Ykkrosh 2010-11-03 17:52:07 +00:00
parent ec5d4a9f9e
commit 818ea52c3b
72 changed files with 3 additions and 5360 deletions

View file

@ -57,9 +57,6 @@ lodbias = 0
profile = default
; Language selection: (currently "english" or "pseudogreek")
language = english
; Font mappings:
font.console = console

View file

@ -1,394 +0,0 @@
use strict;
use warnings;
use Spreadsheet::ParseExcel;
use Encode;
opendir my $root, '.' or die "Error opening current directory: $!";
for my $d (grep { /^[a-z]+$/ and -d $_ } readdir $root) {
opendir my $dir, $d or die "Error opening directory '$d': $!";
for (grep /\.xls$/, readdir $dir) {
/(.*)\.xls$/;
print "* Converting $d/$1.xls\n";
convert("$d/$1");
}
}
print "* Completed\n\n";
sub convert {
my $filename_in = "$_[0].xls";
my $filename_base = $_[0];
$_[0] =~ /([^\/]+)$/;
my $table_name = $1;
my $workbook = Spreadsheet::ParseExcel::Workbook->Parse($filename_in);
die unless $workbook;
my $worksheet = $workbook->{Worksheet}[0];
my @data;
my %encodings = (
# Excel name => Encode name
ucs2 => 'ucs2-be',
ascii => 'ascii',
);
for my $r ($worksheet->{MinRow} .. $worksheet->{MaxRow}) {
push @data, [];
for my $c ($worksheet->{MinCol} .. $worksheet->{MaxCol}) {
my $cell = $worksheet->{Cells}[$r][$c];
if ($cell) {
my $code = defined $cell->{Code} ? $cell->{Code} : 'ascii';
if (not exists $encodings{$code}) {
die "Unrecognised encoding '$code'";
}
push @{$data[-1]}, decode($encodings{$code}, $cell->{Val});
} else {
push @{$data[-1]}, '';
}
}
}
if (@data < 3) {
die "Too few rows of data";
}
# Remove the top line
shift @data;
if (1 != grep length, @{$data[0]}) {
die "Second row must contain a single cell, either 'phrases' or the name of the word-table (e.g. 'nouns')";
}
my $title = (shift @data)->[0];
# Remove blank lines
while (@data and not grep length, @{$data[0]}) {
shift @data;
}
if ($title eq 'phrases') {
convert_phrases($filename_base, \@data);
} else {
convert_words($filename_base, $title, \@data);
}
}
sub convert_words {
my $filename_out = "$_[0].wrd";
my $title = $_[1];
my @data = @{$_[2]};
my @keys = map lc, @{ shift @data };
# Remove the English name from the list of keys
shift @keys;
=pod
struct {
u8 TitleLength;
u16* Title;
u8 PropCount;
struct {
u8 KeyLength;
u16* Key;
}* PropNames;
u16 ValueCount;
struct {
u8 WordLength;
u16* Word;
struct {
u8 PropLength;
u16* Property;
} Properties[ProprCount];
} Words[ValueCount];
}
=cut
open my $o, '>', $filename_out or die "Error opening $filename_out: $!";
binmode $o;
print $o pack 'C/a*', encode('utf16-le', $title);
print $o pack 'C', scalar @keys;
print $o pack 'C/a*', encode('utf16-le', $_) for @keys;
print $o pack 'S', scalar @data;
for (@data) {
print $o pack 'C/a*', encode('utf16-le', $_) for @$_[0..@keys]; # 1 more than @keys, because the first 'property' is the English name
}
close $o;
}
sub convert_phrases {
my $filename_out = "$_[0].lng";
my @data = @{$_[1]};
# Allow simple error reporting
our $errors = 0;
sub error($) { print STDERR "$_[0]\n"; ++$errors; }
# Split the input file on "--" lines, and store an array ref
# for each section containing the lines
my @phrase_lines;
my $line_no = 3;
for (@data) {
push @phrase_lines, [ [ $line_no, $_->[0] ], [ $line_no, $_->[1] ] ];
++$line_no;
}
# Build phrase_data, being a list of [ raw key, parsed key, parsed translation ]
my @phrase_data;
for (@phrase_lines) {
next unless @$_;
my $key = shift @$_;
if (not @$_) {
error "Error in line $key->[0]: no translation specified for key '$key->[1]'";
next;
}
my $translation = join "\n", map $_->[1], @$_;
push @phrase_data, [ $key->[1], parse_translation($key->[1], $translation) ];
}
if ($errors) {
die "ABORTING: $::errors errors found\n";
}
#use Data::Dumper; print Dumper \@phrase_data; exit;
=pod
Disk format:
file {
u16 phrase_count
phrase* phrases;
}
phrase {
u16 key_length;
u16* key_string; // not null-terminated
u8 variable_count; // just for validating its use
u8 section_count;
translation_section* sections;
}
translation_section {
u8 type;
// Type 0: (string)
u16 length;
u16* text; // not null-terminated
// OR Type 1: (variable)
u8 id; // referring to the position in the key phrase
// OR Type 2: (function)
u8 namelength;
u8* name;
u8 paramcount;
func_param* params;
}
func_param {
u8 type;
// Type 0: (string)
u8 length;
u16* text;
// OR Type 1: (variable)
u8 id; // referring to the position in the key phrase
// OR Type 2: (int)
u32 value;
// (doubles/etc will just be stringified)
}
=cut
# Output the language file to disk
open my $o, '>', $filename_out or die "Error opening $filename_out: $!";
binmode $o;
print $o pack 'S', scalar @phrase_data;
for (@phrase_data) {
# $_ eq [ key, var count, [ sections... ] ]
my $key = encode('utf16-le', $_->[0]);
print $o pack 'S/a*C', $key, $_->[1];
print $o pack 'C', scalar @{ $_->[2] };
for (@{ $_->[2] }) {
# $_ eq [ type, data... ]
if ($_->[0] eq 'str') {
print $o pack 'CS/a*', 0, encode('utf16-le', $_->[1]);
} elsif ($_->[0] eq 'var') {
print $o pack 'CC', 1, $_->[1];
} elsif ($_->[0] eq 'code') {
my ($name, @params) = @{$_->[1]};
print $o pack 'CC/a*C', 2, $name, scalar @params;
for (@params) {
if ($_->[0] eq 'str') {
print $o pack 'CC/a*', 0, encode('utf16-le', $_->[1]);
} elsif ($_->[0] eq 'var') {
print $o pack 'CC', 1, $_->[1];
} elsif ($_->[0] eq 'int') {
print $o pack 'Cl', 2, $_->[1];
} else {
die "Invalid func param type $_->[0]";
}
}
} else {
die "Invalid sec type $_->[0]";
}
}
}
}
sub parse_basic {
# Parse "\$[a-zA-Z0-9_]+" (allowing "$$" to represent "$")
my @parts;
for (split /(\$(?:\$|[a-zA-Z0-9_]+))/, $_[0]) {
if (/\$([a-zA-Z0-9_]+)/) {
push @parts, [ var => $1 ];
} else {
$_ = '$' if $_ eq '$$';
if (@parts and $parts[-1][0] eq 'str') {
$parts[-1][1] .= $_;
} else {
push @parts, [ str => $_ ];
}
}
}
return \@parts;
}
sub parse_complex {
# Parse "\$[a-zA-Z0-9_]+" (allowing "$$" to represent "$")
# as well as [...] code lumps
my @chars = split //, $_[0];
my @parts = [ str => '' ];
while (@chars) {
$_ = shift @chars;
if ($_ eq '$') {
if (@chars==0 or $chars[0] eq '$') {
$parts[-1][1] .= '$';
} else {
my $varname = '';
while (@chars and $chars[0] =~ /[a-zA-Z0-9_]/) {
$varname .= shift @chars;
}
push @parts, [ var => $varname ];
push @parts, [ str => '' ];
}
} elsif ($_ eq '[') {
my $codetext;
while (@chars and $chars[0] ne ']') {
$codetext .= shift @chars;
}
shift @chars;
push @parts, [ code => parse_code($codetext) ];
push @parts, [ str => '' ];
} else {
$parts[-1][1] .= $_;
}
}
@parts = grep $_->[1] ne '', @parts;
return \@parts;
}
sub parse_code {
my @rawparts = split /,\s*/, $_[0];
my $name = shift @rawparts;
my @parts;
for (@rawparts) {
if (/^\d+$/) {
push @parts, [ 'int' => $_ ];
} elsif (/^\$(.*)/) {
push @parts, [ 'var' => $1 ];
} else {
push @parts, [ 'str' => $_ ];
}
}
return [ $name, @parts ];
}
sub parse_translation {
my ($key, $str) = @_;
# Parse key to extract the variable sections
$key = parse_basic($key);
# Fill variables with ( var0 => 0, var1 => 1, var2 => 2 )
my %variables;
for (@$key) {
$variables{$_->[1]} = scalar keys %variables if $_->[0] eq 'var';
}
my %unused_variables = %variables;
# Parse the translated string for
$str = parse_complex($str);
# Replace variable strings with the appropriate numbers
for (@$str) {
if ($_->[0] eq 'var') {
if (not exists $variables{$_->[1]}) {
die "Unrecognised variable '$_->[1]'\n";
}
delete $unused_variables{$_->[1]};
$_->[1] = $variables{$_->[1]};
} elsif ($_->[0] eq 'code') {
for (@{$_->[1]}[1 .. $#{$_->[1]}]) {
if ($_->[0] eq 'var') {
if (not exists $variables{$_->[1]}) {
die "Unrecognised variable '$_->[1]'\n";
}
delete $unused_variables{$_->[1]};
$_->[1] = $variables{$_->[1]};
}
}
}
}
for (keys %unused_variables) {
warn "Warning: variable '$_' unreferenced in translated string\n";
}
return (scalar keys %variables, $str);
}

View file

@ -480,19 +480,6 @@ function setup_all_libs ()
setup_static_lib_package("graphics", source_dirs, extern_libs, {})
-- internationalization = i18n
-- note: this package isn't large, but is separate because it may be
-- useful for other projects.
source_dirs = {
"i18n"
}
extern_libs = {
"spidermonkey",
"boost"
}
setup_static_lib_package("i18n", source_dirs, extern_libs, {})
source_dirs = {
"tools/atlas/GameInterface",
"tools/atlas/GameInterface/Handlers"

View file

@ -23,7 +23,6 @@ GUI utilities
#include "GUI.h"
#include "GUIManager.h"
#include "ps/Parser.h"
#include "ps/i18n.h"
extern int g_yres;

View file

@ -1,160 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#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);
// (TODO: the hashing here is quite rubbish)
using namespace I18n;
namespace I18n {
// These bits don't seem to work without the explicit namespace{}
template<> BufferVariable* NewBufferVariable<int>(int v) { return new BufferVariable_int(v); }
template<> BufferVariable* NewBufferVariable<float>(float v) { return new BufferVariable_double(v); }
template<> BufferVariable* NewBufferVariable<double>(double v) { return new BufferVariable_double(v); }
template<> BufferVariable* NewBufferVariable<Noun>(Noun v) { return new BufferVariable_string(v.value); }
template<> BufferVariable* NewBufferVariable<Name>(Name v) { return new BufferVariable_rawstring(v.value); }
//
// Don't allow plain strings -- people *must* specify whether it's going
// to be automatically translated (I18n::Noun) or not (I18n::Name/Raw)
//
}
StrImW BufferVariable_int::ToString(CLocale*)
{
wchar_t buffer[50];
swprintf_s(buffer, ARRAY_SIZE(buffer), L"%d", value);
return buffer;
}
u32 BufferVariable_int::Hash()
{
return (u32)value;
}
StrImW BufferVariable_double::ToString(CLocale*)
{
wchar_t buffer[50];
swprintf_s(buffer, ARRAY_SIZE(buffer), L"%f", value);
return buffer;
}
u32 BufferVariable_double::Hash()
{
// Add the two four-bytes of the double
union {
u32 i[2];
double d;
} u;
u.d = value;
return u.i[0]+u.i[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(L"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;
}

View file

@ -1,104 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
BufferVariable stores the parameters that have been passed to a StringBuffer,
providing hashes and strings on request.
*/
#ifndef INCLUDED_I18N_BUFFERVARIABLE
#define INCLUDED_I18N_BUFFERVARIABLE
#include "Common.h"
#include "StrImmutable.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 // INCLUDED_I18N_BUFFERVARIABLE

View file

@ -1,464 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "CLocale.h"
#include "TSComponent.h"
#include "ps/StringConvert.h"
#include <algorithm>
#include "ps/CLogger.h"
#define LOG_CATEGORY L"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 ((const char*)data, (const 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(L"Invalid function parameter type");
}
}
String->Parts.push_back(Func);
break;
}
default: // Argh!
debug_warn(L"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(CLogger::Error, LOG_CATEGORY, L"I18n: Functions file '%hs' is too short", filename);
return false;
}
if (*(jschar*)data != 0xFEFF)
{
LOG(CLogger::Error, LOG_CATEGORY, L"I18n: Functions file '%hs' has invalid Unicode format (lacking little-endian BOM)", filename);
return false;
}
if (! Script.ExecuteCode((jschar*)(data+2), len/2, filename))
{
LOG(CLogger::Error, LOG_CATEGORY, L"I18n: JS errors in functions file '%hs'", filename);
return false;
}
return true;
}
bool CLocale::LoadDictionary(const char* data)
{
ReadWString8(DictName);
u8 PropertyCount = *(u8*)data;
data += 1;
DictData& dict = Dictionaries[DictName];
if (dict.DictProperties.size() && PropertyCount != dict.DictProperties.size())
{
LOG(CLogger::Error, LOG_CATEGORY, L"I18n: Multiple dictionary files loaded with the name ('%ls') and different properties", DictName.c_str());
return false;
// TODO: Check headings to make sure they're identical (or handle them more cleverly)
}
// Read the names of the properties
int i;
for (i = 0; i < PropertyCount; ++i)
{
ReadWString8(Property);
dict.DictProperties[Property] = i;
}
u16 ValueCount = *(u16*)data;
data += 2;
// Read each 'value' (word + properties)
for (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;
}
void CLocale::UnloadDictionaries()
{
Dictionaries.clear();
}
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())
{
LOG(CLogger::Warning, LOG_CATEGORY, L"I18n: Non-loaded dictionary '%ls' accessed", dictname.c_str());
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(CLogger::Normal, LOG_CATEGORY, L"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
debug_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();
}

View file

@ -1,114 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
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 INCLUDED_I18N_CLOCALE
#define INCLUDED_I18N_CLOCALE
#include "Common.h"
#include "Interface.h"
#include "StringBuffer.h"
#include "StrImmutable.h"
#include "ScriptInterface.h"
#include "lib/sysdep/stl.h" // STL_HASH_MAP
#include <map>
#include <algorithm>
struct JSContext;
namespace I18n
{
class CLocale : public CLocale_interface
{
friend class 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);
void UnloadDictionaries();
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);
CLocale(JSContext* context, JSObject* scope) : CacheAge(0), Script(this, context, scope) {}
~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 // INCLUDED_I18N_CLOCALE

View file

@ -1,37 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Things that are used by most I18n code:
#ifndef INCLUDED_I18N_COMMON
#define INCLUDED_I18N_COMMON
#include <string>
#include "ps/Errors.h"
namespace I18n
{
// Define an 'internal' string type, for no particular reason
typedef std::wstring Str;
}
ERROR_GROUP(I18n);
// That was exciting.
#endif // INCLUDED_I18N_COMMON

View file

@ -1,48 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_I18N_DATATYPES
#define INCLUDED_I18N_DATATYPES
#include "StrImmutable.h"
namespace I18n
{
// Use for names of objects that should be translated, e.g.
// translate("Construct $obj")<<I18n::Noun(selectedobject.name)
struct Noun
{
template<typename T> Noun(T d) : value(d) {}
StrImW value;
};
// Allow translate("Hello $you")<<I18n::Name(playername), which
// won't attempt to translate the player's name.
// Templated to allow char* and wchar_t*
struct Name
{
template<typename T> Name(T d) : value(d) {}
StrImW value;
};
// Also allow I18n::Raw("english message"), which does the same
// non-translation but makes more sense when writing e.g. error messages
typedef Name Raw;
}
#endif // INCLUDED_I18N_DATATYPES

View file

@ -1,93 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
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 L"i18n"
using namespace I18n;
struct JSContext;
CLocale_interface* I18n::NewLocale(JSContext* cx, JSObject* scope)
{
try
{
return new CLocale(cx, scope);
}
catch (PSERROR_I18n& e)
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error creating locale object ('%hs')", e.what());
return NULL;
}
}

View file

@ -1,63 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
The only file that external code should need to include.
*/
#ifndef INCLUDED_I18N_INTERFACE
#define INCLUDED_I18N_INTERFACE
#include "StringBuffer.h"
#include "DataTypes.h"
struct JSContext;
struct JSObject;
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) = 0;
virtual void UnloadDictionaries() = 0;
virtual ~CLocale_interface() {}
};
// Build a CLocale. Returns NULL on failure.
CLocale_interface* NewLocale(JSContext* cx, JSObject* scope);
}
#endif // INCLUDED_I18N_INTERFACE

View file

@ -1,485 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ScriptInterface.h"
#include "CLocale.h"
#include "ps/StringConvert.h"
#include "scripting/SpiderMonkey.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY L"i18n"
using namespace I18n;
// LookedupWord JS class:
namespace JSI_LookedupWord {
static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
CLocale::LookupType* lookedup = (CLocale::LookupType*)JS_GetPrivate(cx, obj);
debug_assert(lookedup);
JSObject* parent = JS_GetParent(cx, obj);
debug_assert(parent);
CLocale* locale = (CLocale*)JS_GetPrivate(cx, parent);
debug_assert(locale);
JSString* prop = JS_ValueToString(cx, id);
JSU_ASSERT(prop, "lookup() property failed to convert to string");
jschar* prop_chars = JS_GetStringChars(prop);
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);
JSU_ASSERT(result_str, "lookup() property failed to create string");
*vp = STRING_TO_JSVAL(result_str);
return JS_TRUE;
}
static 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);
debug_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
};
}
// 'i18n' JS class:
namespace JSI_i18n {
#define TYPE(x) \
static JSBool Create_##x(JSContext* cx, JSObject* obj, uintN argc, jsval* argv, jsval* rval) \
{ \
/* Set *rval = { type => "Name", value => argv[0] } */ \
\
JSObject* object = JS_NewObject(cx, NULL, NULL, obj); \
JSU_ASSERT(object, "Failed to create i18n value object"); \
\
/* TODO: More error checking */ \
JSU_REQUIRE_PARAMS(1); \
jsval type = STRING_TO_JSVAL(JS_NewStringCopyZ(cx, #x)); \
jsval value = STRING_TO_JSVAL(JS_ValueToString(cx, argv[0])); \
JS_SetProperty(cx, object, "type", &type); \
JS_SetProperty(cx, object, "value", &value); \
\
*rval = OBJECT_TO_JSVAL(object); \
\
return JS_TRUE; \
}
TYPE(Name)
TYPE(Raw)
TYPE(Noun)
#undef TYPE
static JSClass JSI_class = {
"JSI_i18n", JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub,
NULL, NULL, NULL, NULL
};
#define TYPE(x) {#x, Create_##x, 1, 0, 0}
static JSFunctionSpec JSI_funcs[] = {
TYPE(Name),
TYPE(Raw),
TYPE(Noun),
{0,0,0,0,0},
};
#undef TYPE
}
static JSBool JSFunc_LookupWord(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSU_REQUIRE_PARAMS(2);
// Get the strings
JSString* dictname = JS_ValueToString(cx, argv[0]);
JSString* word = JS_ValueToString(cx, argv[1]);
JSU_ASSERT(dictname && word, "lookup() failed to convert parameters to strings");
// and the characters from the strings
jschar* dictname_chars = JS_GetStringChars(dictname);
jschar* word_chars = JS_GetStringChars(word);
// (can't fail)
// 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, &JSI_LookedupWord::JSI_class, NULL, obj);
JSU_ASSERT(wordobj, "lookup() failed to create object");
// 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 JSBool JSFunc_Translate(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
{
JSU_ASSERT(argc >= 1, "Too few parameters to translate() function");
JSString* phrase_str = JS_ValueToString(cx, argv[0]);
JSU_ASSERT(phrase_str, "translate() failed to convert first parameter to string");
CStrW phrase (JS_GetStringChars(phrase_str));
jsval locale_objval;
JSU_ASSERT(JS_GetProperty(cx, obj, "i18n", &locale_objval), "translate() failed to find i18n object in current scope");
CLocale* locale = (CLocale*)JS_GetPrivate(cx, JSVAL_TO_OBJECT(locale_objval));
StringBuffer sb = locale->Translate(phrase.c_str());
for (uintN i=1; i<argc; ++i)
{
if (JSVAL_IS_INT(argv[i]))
{
sb << (int) JSVAL_TO_INT(argv[i]);
continue;
}
if (JSVAL_IS_DOUBLE(argv[i]))
{
sb << *JSVAL_TO_DOUBLE(argv[i]);
continue;
}
if (JSVAL_IS_OBJECT(argv[i]))
{
// Check for 'type' and 'value' properties
jsval type, value;
if (JS_GetProperty(cx, JSVAL_TO_OBJECT(argv[i]), "type", &type)
&& type != JSVAL_VOID
&& JS_GetProperty(cx, JSVAL_TO_OBJECT(argv[i]), "value", &value)
&& value != JSVAL_VOID)
{
// TODO: More error handling
std::string typestr = JS_GetStringBytes(JS_ValueToString(cx, type));
std::wstring val;
if (typestr == "Name")
{
JSString* s = JS_ValueToString(cx, value);
sb << I18n::Name(JS_GetStringChars(s));
continue;
}
else if (typestr == "Raw")
{
JSString* s = JS_ValueToString(cx, value);
sb << I18n::Raw(JS_GetStringChars(s));
continue;
}
else if (typestr == "Noun")
{
JSString* s = JS_ValueToString(cx, value);
sb << I18n::Noun(JS_GetStringChars(s));
continue;
}
}
}
JS_ReportError(cx, "Invalid parameter passed to translate() (must be a number or a i18n.something() object)");
return JS_FALSE;
}
JSString* result_str = StringConvert::wstring_to_jsstring(cx, sb);
*rval = STRING_TO_JSVAL(result_str);
return JS_TRUE;
}
// Visible to all JS code:
static JSFunctionSpec JSI_i18nInterfaceFunctions[] = {
{"translate", JSFunc_Translate, 0, 0, 0},
{0,0,0,0,0},
};
// Visible to functions called by the i18n system:
static JSFunctionSpec JSI_i18nFunctions[] = {
{"lookup", JSFunc_LookupWord, 2, 0, 0},
{0,0,0,0,0},
};
// Object under which to run functions called by the i18n system:
static JSClass JSI_i18nFunctionScope = {
"", 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, JSObject* scope)
: Context(cx)
{
// Don't do much if there's currently no scripting support
if (cx == NULL)
return;
// This constructor should, in theory, never fail
// Create the object [/scope] under which the locale-dependent translation
// functions are executed
Object = JS_NewObject(Context, &JSI_i18nFunctionScope, NULL, NULL);
if (! Object)
throw PSERROR_I18n_Script_SetupFailed();
JS_AddRoot(Context, &Object);
// (This will get leaked if the constructor throws, but there will be more
// important things to worry about than memory leaks in those situations)
// Register the functions for use by i18n translations
if (! JS_DefineFunctions(Context, Object, JSI_i18nFunctions))
throw PSERROR_I18n_Script_SetupFailed();
// Store the CLocale* in the script-object's private area
JS_SetPrivate(Context, Object, locale);
// Interface setup:
// Register the interface functions
if (! JS_DefineFunctions(Context, scope, JSI_i18nInterfaceFunctions))
throw PSERROR_I18n_Script_SetupFailed();
// Create the 'i18n' interface object
JSObject* i18nObject = JS_DefineObject(Context, scope, "i18n", &JSI_i18n::JSI_class, NULL, JSPROP_READONLY | JSPROP_PERMANENT);
if (! i18nObject)
throw PSERROR_I18n_Script_SetupFailed();
// Define its functions (i18n.Name() etc)
if (! JS_DefineFunctions(Context, i18nObject, JSI_i18n::JSI_funcs))
throw PSERROR_I18n_Script_SetupFailed();
// Store the CLocale* in the i18n object's private area
JS_SetPrivate(Context, i18nObject, locale);
}
ScriptObject::~ScriptObject()
{
if (Context)
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(CLogger::Error, LOG_CATEGORY, L"I18n: Error executing JS function '%hs'", name);
return L"(JS error)";
}
// Convert rval to a string and return
JSString* str = JS_ValueToString(Context, rval);
if (! str)
{
debug_warn(L"Conversion to string failed");
return L"(JS error)";
}
jschar* chars = JS_GetStringChars(str);
if (! chars)
{
debug_warn(L"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(L"Error creating JS string");
Value = JSVAL_NULL;
}
else
{
Value = STRING_TO_JSVAL(str);
JS_AddRoot(Context, &Value);
}
}
jsval ScriptValueString::GetJsval(const std::vector<BufferVariable*>& UNUSED(vars))
{
return Value;
}
ScriptValueString::~ScriptValueString()
{
if (! JSVAL_IS_NULL(Value))
{
JS_RemoveRoot(Context, &Value);
}
}
/************/
ScriptValueInteger::ScriptValueInteger(ScriptObject& script, const int v)
{
Context = script.Context;
Value = INT_TO_JSVAL(v);
}
jsval ScriptValueInteger::GetJsval(const std::vector<BufferVariable*>& UNUSED(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(L"Error creating JS double");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, &GCVal);
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(L"Error creating JS string");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, &GCVal);
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(L"Error creating JS string");
return JSVAL_NULL;
}
GCVal = (void*)val;
JS_AddRoot(Context, &GCVal);
return STRING_TO_JSVAL(val);
}
default:
debug_warn(L"Invalid type");
return JSVAL_NULL;
}
}
ScriptValueVariable::~ScriptValueVariable()
{
if (GCVal)
JS_RemoveRoot(Context, &GCVal);
}

View file

@ -1,113 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
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 INCLUDED_I18N_SCRIPTINTERFACE
#define INCLUDED_I18N_SCRIPTINTERFACE
#include "Common.h"
#include "BufferVariable.h"
#include <vector>
ERROR_SUBGROUP(I18n, Script);
ERROR_TYPE(I18n_Script, SetupFailed);
typedef intptr_t JSWord;
typedef JSWord jsword;
typedef jsword jsval;
typedef unsigned short jschar;
struct JSContext;
struct JSObject;
namespace I18n
{
class ScriptValue;
class CLocale;
class ScriptObject
{
public:
ScriptObject(CLocale* locale, JSContext* cx, JSObject* scope);
~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 // INCLUDED_I18N_SCRIPTINTERFACE

View file

@ -1,123 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
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 INCLUDED_I18N_STRIMMUTABLE
#define INCLUDED_I18N_STRIMMUTABLE
#include <wchar.h>
#include <string.h>
#include "lib/sysdep/cpu.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:
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));
}
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 !MSC_VERSION || defined(_NATIVE_WCHAR_T_DEFINED)
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
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
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 // INCLUDED_I18N_STRIMMUTABLE

View file

@ -1,99 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#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 <iostream>
#include "ps/CLogger.h"
#define LOG_CATEGORY L"i18n"
using namespace I18n;
#ifdef I18NDEBUG
namespace I18n { bool g_UsedCache; }
#endif
I18n::StringBuffer::operator Str()
{
#ifdef I18NDEBUG
g_UsedCache = false;
#endif
if (Variables.size() != String->VarCount)
{
LOG(CLogger::Error, LOG_CATEGORY, L"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)
{
if (String->Parts.size())
return String->Parts[0]->ToString(Locale, Variables).str();
else
return Str();
}
Str ret;
if (Locale->ReadCached(this, ret))
{
// Found in cache
#ifdef I18NDEBUG
g_UsedCache = true;
#endif
// 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;
}
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;
}

View file

@ -1,76 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Returned by Translate(), and used to collect variables from the user
before converting into a string.
*/
#ifndef INCLUDED_I18N_STRINGBUFFER
#define INCLUDED_I18N_STRINGBUFFER
#include "Common.h"
#include "TranslatedString.h"
#include "ps/CStr.h"
namespace I18n
{
class CLocale;
class BufferVariable;
template<typename T> BufferVariable* NewBufferVariable(T v);
class StringBuffer
{
friend class CLocale;
public:
// Builds and returns the finished string
operator Str();
operator CStrW() { return CStrW( (Str)*this ); }
// 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:
TranslatedString* String;
// pointer instead of reference allows assignment.
// this class is returned by value, so it's nicer for the
// compiler-generated copy ctor to be used.
std::vector<BufferVariable*> Variables;
CLocale* Locale;
StringBuffer(TranslatedString* str, CLocale* loc)
: String(str), Locale(loc)
{
}
};
}
#endif // INCLUDED_I18N_STRINGBUFFER

View file

@ -1,58 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#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* UNUSED(locale), std::vector<BufferVariable*>& UNUSED(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>);
}

View file

@ -1,94 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Stores sections of a translated string: constant strings, simple variables,
and JS function calls, allowing conversion to strings.
*/
#ifndef INCLUDED_I18N_TSCOMPONENT
#define INCLUDED_I18N_TSCOMPONENT
#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
{
NONCOPYABLE(TSComponentString);
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
{
NONCOPYABLE(TSComponentVariable);
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
{
NONCOPYABLE(TSComponentFunction);
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 // INCLUDED_I18N_TSCOMPONENT

View file

@ -1,29 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#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;
}

View file

@ -1,45 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
Simple storage for translated phrases, made up of lots of TSComponents
(strings / variables / functions)
*/
#ifndef INCLUDED_I18N_TRANSLATEDSTRING
#define INCLUDED_I18N_TRANSLATEDSTRING
#include <vector>
namespace I18n
{
class TSComponent;
class TranslatedString
{
public:
std::vector<const TSComponent*> Parts;
unsigned char VarCount;
~TranslatedString();
};
}
#endif // INCLUDED_I18N_TRANSLATEDSTRING

View file

@ -1,23 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
class CStr : public std::string {};
class CStrW : public std::wstring
{
public:
CStrW(const std::wstring &s) : std::wstring(s) {}
};

View file

@ -1,70 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "StringConvert.h"
#include <assert.h>
#include "jsapi.h"
#if SDL_BYTEORDER == 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);
}

View file

@ -1,36 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
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);
}

View file

@ -1,60 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Make sure we have the argument (UNIDOUBLER_HEADER), and that we're not
// called from within another unidoubler execution (now that's just asking for
// trouble)
#if defined(UNIDOUBLER_HEADER) && !defined(IN_UNIDOUBLER)
#define IN_UNIDOUBLER
#define _UNICODE
#undef CStr
#undef CStr_hash_compare
#define CStr CStrW
// Compat for old code. *Deprecated* CStrW isn't 16-bit - it is "wide"
#define CStr16 CStrW
#define CStr_hash_compare CStrW_hash_compare
#include UNIDOUBLER_HEADER
// Undef all the Conversion Macros
#undef tstring
#undef tstringstream
#undef _tcout
#undef _tstod
#undef _ttoi
#undef _ttol
#undef TCHAR
#undef _T
#undef _istspace
#undef _tsnprintf
#undef _totlower
#undef _totupper
// Now include the 8-bit version under the name CStr8
#undef _UNICODE
#undef CStr
#undef CStr_hash_compare
#define CStr CStr8
#define CStr_hash_compare CStr8_hash_compare
#include UNIDOUBLER_HEADER
#undef IN_UNIDOUBLER
#undef UNIDOUBLER_HEADER
#endif

View file

@ -1,16 +0,0 @@
BufferVariable.cpp
BufferVariable.h
CLocale.cpp
CLocale.h
Common.h
Interface.cpp
Interface.h
ScriptInterface.cpp
ScriptInterface.h
StrImmutable.h
StringBuffer.cpp
#StringBuffer.h
TranslatedString.cpp
TranslatedString.h
#TSComponent.cpp
TSComponent.h

View file

@ -1,7 +0,0 @@
del Coverage\*.dyn
cd ..\..\..\binaries\system
..\..\source\i18n\tests\Coverage\tests
cd ..\..\source\i18n\tests\Coverage
profmerge
codecov -comp ../comp.txt -ccolor #f6f6ff -mname "Philip Taylor" -maddr excors@gmail.com
start CODE_COVERAGE.HTML

View file

@ -1,41 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SYSDEP_H__
#define SYSDEP_H__
// STL_HASH_MAP, STL_HASH_MULTIMAP
#ifdef __GNUC__
// GCC
# include <ext/hash_map>
# define STL_HASH_MAP __gnu_cxx::hash_map
# define STL_HASH_MULTIMAP __gnu_cxx::hash_multimap
#else // !__GNUC__
# include <hash_map>
# if defined(_MSC_VER) && (_MSC_VER >= 1300)
// VC7 or above
# define STL_HASH_MAP stdext::hash_map
# define STL_HASH_MULTIMAP stdext::hash_multimap
# else
// VC6 and anything else (most likely name)
# define STL_HASH_MAP std::hash_map
# define STL_HASH_MULTIMAP std::hash_multimap
# endif // defined(_MSC_VER) && (_MSC_VER >= 1300)
#endif // !__GNUC__
#endif // #ifndef SYSDEP_H__

View file

@ -1,60 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Hacked version to minimise dependencies
// convenience type (shorter defs than stdint uintN_t)
#ifndef __TYPES_H__
#define __TYPES_H__
//#include "posix.h"
typedef unsigned int uint32_t;
typedef unsigned short uint16_t;
typedef unsigned char uint8_t;
typedef unsigned short wchar_t;
// defines instead of typedefs so we can #undef conflicting decls
// Workaround: GCC won't parse constructor-casts with multi-word types, while
// visual studio will. Define uint/long to a namespaced one-word typedef.
typedef unsigned long PS_ulong;
typedef unsigned int PS_uint;
#define ulong PS_ulong
#define uint PS_uint
#define i8 int8_t
#define i16 int16_t
#define i32 int32_t
#define i64 int64_t
#define u8 uint8_t
#define u16 uint16_t
#define u32 uint32_t
#define u64 uint64_t
// the standard only guarantees 16 bits.
// we use this for memory offsets and ranges, so it better be big enough.
#ifdef SIZE_MAX // nested #if to avoid ICC warning if not defined
# if SIZE_MAX < 32
# error "check size_t and SIZE_MAX - too small?"
# endif
#endif
#endif // #ifndef __TYPES_H__

View file

@ -1,18 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"

View file

@ -1,56 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef NDEBUG
#include <vector>
#include <hash_map>
#include <string>
#include <sstream>
#include <map>
/*
#include <crtdbg.h>
#define new new(_NORMAL_BLOCK ,__FILE__, __LINE__)
#define malloc(s) _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define calloc(c, s) _calloc_dbg(c, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define realloc(p, s) _realloc_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define _expand(p, s) _expand_dbg(p, s, _NORMAL_BLOCK, __FILE__, __LINE__)
#define free(p) _free_dbg(p, _NORMAL_BLOCK)
*/
#endif
// Random stuff:
#define SDL_LIL_ENDIAN 1234
#define SDL_BIG_ENDIAN 4321
#define SDL_BYTEORDER SDL_LIL_ENDIAN
#include <cassert>
#define cassert(x) extern char cassert__##__LINE__ [x]
#define debug_warn(x) assert(0&&x)
#define debug_assert assert
#define XP_WIN
#define MSC_VERSION _MSC_VER
#define UNUSED(x)
#include "ps/Errors.h"
#include <cstdio>
#define swprintf _snwprintf

View file

@ -1,31 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
enum {
ERROR,
WARNING,
};
#include <stdarg.h>
static void LOG(int level, const char* cat, const char* fmt, ...)
{
va_list va;
va_start(va, fmt);
vprintf(fmt, va);
printf("\n");
}

View file

@ -1,80 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
// Auto-generated by errorlist.pl - do not edit.
#include "precompiled.h"
#include "Errors.h"
class PSERROR_Error : public PSERROR {};
class PSERROR_I18n : public PSERROR {};
class PSERROR_I18n_Script : public PSERROR_I18n {};
class PSERROR_Error_InvalidError : public PSERROR_Error { public: PSRETURN getCode() const; };
class PSERROR_I18n_Script_SetupFailed : public PSERROR_I18n_Script { public: PSRETURN getCode() const; };
extern const PSRETURN PSRETURN_Error_InvalidError = 0x01000001;
extern const PSRETURN PSRETURN_I18n_Script_SetupFailed = 0x02010001;
extern const PSRETURN MASK__PSRETURN_Error = 0xff000000;
extern const PSRETURN CODE__PSRETURN_Error = 0x01000000;
extern const PSRETURN MASK__PSRETURN_I18n = 0xff000000;
extern const PSRETURN CODE__PSRETURN_I18n = 0x02000000;
extern const PSRETURN MASK__PSRETURN_I18n_Script = 0xffff0000;
extern const PSRETURN CODE__PSRETURN_I18n_Script = 0x02010000;
extern const PSRETURN MASK__PSRETURN_Error_InvalidError = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_Error_InvalidError = 0x01000001;
extern const PSRETURN MASK__PSRETURN_I18n_Script_SetupFailed = 0xffffffff;
extern const PSRETURN CODE__PSRETURN_I18n_Script_SetupFailed = 0x02010001;
PSRETURN PSERROR_Error_InvalidError::getCode() const { return 0x01000001; }
PSRETURN PSERROR_I18n_Script_SetupFailed::getCode() const { return 0x02010001; }
const char* PSERROR::what() const throw ()
{
return GetErrorString(getCode());
}
const char* GetErrorString(const PSERROR& err)
{
return GetErrorString(err.getCode());
}
const char* GetErrorString(PSRETURN code)
{
switch (code)
{
case 0x01000001: return "Error_InvalidError";
case 0x02010001: return "I18n_Script_SetupFailed";
default: return "Unrecognised error";
}
}
void ThrowError(PSRETURN code)
{
switch (code) // Use 'break' in case someone tries to continue from the exception
{
case 0x01000001: throw PSERROR_Error_InvalidError(); break;
case 0x02010001: throw PSERROR_I18n_Script_SetupFailed(); break;
default: throw PSERROR_Error_InvalidError(); // Hmm...
}
}

View file

@ -1,58 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef _ERRORS_H_
#define _ERRORS_H_
#include <exception>
#include "lib/types.h"
typedef u32 PSRETURN;
class PSERROR : public std::exception
{
public:
virtual const char* what() const throw ();
virtual PSRETURN getCode() const = 0; // for functions that catch exceptions then return error codes
};
#define ERROR_GROUP(a) class PSERROR_##a : public PSERROR {}; \
extern const PSRETURN MASK__PSRETURN_##a; \
extern const PSRETURN CODE__PSRETURN_##a
#define ERROR_SUBGROUP(a,b) class PSERROR_##a##_##b : public PSERROR_##a {}; \
extern const PSRETURN MASK__PSRETURN_##a##_##b; \
extern const PSRETURN CODE__PSRETURN_##a##_##b
#define ERROR_TYPE(a,b) class PSERROR_##a##_##b : public PSERROR_##a { public: PSRETURN getCode() const; }; \
extern const PSRETURN MASK__PSRETURN_##a##_##b; \
extern const PSRETURN CODE__PSRETURN_##a##_##b; \
extern const PSRETURN PSRETURN_##a##_##b
#define ERROR_IS(a, b) ( ((a) & MASK__PSRETURN_##b) == CODE__PSRETURN_##b )
const PSRETURN PSRETURN_OK = 0;
const PSRETURN MASK__PSRETURN_OK = 0xffffffff;
const PSRETURN CODE__PSRETURN_OK = 0;
const char* GetErrorString(PSRETURN code);
const char* GetErrorString(const PSERROR& err);
void ThrowError(PSRETURN code);
#endif

View file

@ -1,30 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#define XP_WIN
#include <jsapi.h>
#include <jsatom.h>
#ifndef NDEBUG
# include <jsdbgapi.h>
#endif
// Make JS debugging a little easier by automatically naming GC roots
// Don't simply #define NAME_ALL_GC_ROOTS, because jsapi.h is horridly broken
#ifndef NDEBUG
# define JS_AddRoot(cx, rp) JS_AddNamedRoot((cx), (rp), __FILE__ )
#endif

View file

@ -1,182 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include <stdio.h>
#include <assert.h>
#include <time.h>
#include "Interface.h"
#include "scripting/SpiderMonkey.h"
I18n::CLocale_interface* g_CurrentLocale;
#define translate(x) g_CurrentLocale->Translate(x)
std::string readfile(const char* fn)
{
std::string t;
FILE* f = fopen(fn, "rb");
assert(f);
size_t c;
char buf[1024];
while (0 != (c = fread(buf, 1, sizeof(buf), f)))
t += std::string(buf, buf+c);
fclose(f);
return t;
}
void errrep(JSContext* cx, const char* msg, JSErrorReport* err)
{
printf("Error: %s\n", msg);
}
int main()
{
JSRuntime* rt = JS_NewRuntime(1024*1024);
assert(rt);
JSContext* cx = JS_NewContext(rt, 8192);
assert(cx);
JSClass clas =
{
"global", 0,
JS_PropertyStub, JS_PropertyStub,
JS_PropertyStub, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub,
JS_ConvertStub, JS_FinalizeStub
};
JSObject* glob = JS_NewObject(cx, &clas, NULL, NULL);
JSBool builtins = JS_InitStandardClasses(cx, glob);
assert(builtins);
JS_SetErrorReporter(cx, errrep);
#ifndef NDEBUG
// _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_LEAK_CHECK_DF);
//_CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_CHECK_ALWAYS_DF | _CRTDBG_DELAY_FREE_MEM_DF);
//CrtSetBreakAlloc(1450);
#endif
g_CurrentLocale = I18n::NewLocale(cx, glob);
if (!g_CurrentLocale)
return 1;
extern void test(JSContext*, JSObject*);
test(cx, glob);
#if 0
std::string lang = readfile("e:\\0ad\\cvs\\binaries\\data\\mods\\official\\language\\test\\phrases.lng");
std::string funcs = readfile("e:\\0ad\\cvs\\binaries\\data\\mods\\official\\language\\test\\functions.js");
std::string words = readfile("e:\\0ad\\cvs\\binaries\\data\\mods\\official\\language\\test\\nouns.wrd");
std::string words2 = readfile("e:\\0ad\\cvs\\binaries\\data\\mods\\official\\language\\test\\nouns2.wrd");
g_CurrentLocale->LoadFunctions(funcs.c_str(), funcs.size(), "functions.txt");
g_CurrentLocale->LoadStrings(lang.c_str());
g_CurrentLocale->LoadDictionary(words.c_str());
g_CurrentLocale->LoadDictionary(words2.c_str());
I18n::Str s;
// const char* script = " translate('Hello $name!', i18n.Noun('apple')) ";
const char* script = " translate('Testing things $num of $object', 3/1.5, i18n.Noun('banana')) ";
jsval rval;
if (JS_EvaluateScript(cx, glob, script, (int)strlen(script), "test", 1, &rval) != JS_TRUE)
assert(! "Eval failed");
//assert(JSVAL_IS_STRING(rval));
//s = JS_GetStringChars(JSVAL_TO_STRING(rval));
s = JS_GetStringChars(JS_ValueToString(cx, rval));
printf("%ls\n", s.c_str());
#if 0
s = translate(L"Hello $name!") << I18n::Name("banana");
printf("%ls\n", s.c_str());
s = translate(L"Hello $name!") << I18n::Name("banana");
printf("%ls\n", s.c_str());
s = translate(L"Testing things $num of $object") << 1 << I18n::Name("banana");
printf("%ls\n", s.c_str());
s = translate(L"Testing things $num of $object") << 1234 << I18n::Name("banana");
printf("%ls\n", s.c_str());
s = translate(L"Testing things $num of $object") << 12345 << I18n::Name("apple");
printf("%ls\n", s.c_str());
s = translate(L"Testing things $num of $object") << 123456 << I18n::Name("orange");
printf("%ls\n", s.c_str());
s = translate(L"Testing things $num of $object") << 1234567 << I18n::Name("cheese");
printf("%ls\n", s.c_str());
s = translate(L"Hello $name!") << I18n::Name("Philip");
printf("%ls\n", s.c_str());
s = translate(L"Hello $name!") << I18n::Name("Philip2");
printf("%ls\n", s.c_str());
// s = translate(L"Also hi to $you$me etc") << I18n::DateShort(time(NULL)) << -1;
// printf("%ls\n", s.c_str());
// s = translate(L"Hello $name!") << I18n::DateShort(time(NULL));
//char* y = "you";
//s = translate(L"Hello $name!") << y;
// printf("%ls\n", s.c_str());
s = translate(L"Your $colour cheese-monster eats $num biscuits") << I18n::Name("blue") << 15;
printf("%ls\n", s.c_str());
#endif
// Performance test: (it should go at a few hundred thousand per second)
#if 0
clock_t t = clock();
int limit = 1000000;
for (int i=0; i<limit; ++i)
{
/*
I18n::Str s1 = translate(L"Hello world 1");
I18n::Str s2 = translate(L"Hello world 2");
I18n::Str s3 = translate(L"Hello world 3");
I18n::Str s4 = translate(L"Hello world 4");
// I18n::Str s1 = translate(L"Hello $name!") << L"Philip";
// I18n::Str s2 = translate(L"Your $colour cheese-monster eats $num biscuits") << "blue" << 15;
//I18n::Str s3 = translate(L"Hello2 $name!") << L"Philip";
//I18n::Str s4 = translate(L"Your2 $colour cheese-monster eats $num biscuits") << "blue" << 15;
// I18n::Str s3 = translate(L"Hello $name!") << 1234;
// I18n::Str s4 = translate(L"Your $colour cheese-monster eats $num biscuits") << 10000 << 15;
/*/
I18n::Str s1 = translate(L"Testing things $num $obj") << 1234.0 << I18n::Name("cheese bananas");
//I18n::Str s2 = translate(L"Testing things $num") << 124;
//I18n::Str s3 = translate(L"Testing things $num") << 1231234;
//I18n::Str s4 = translate(L"Testing things $num") << 1243456;
//*/
}
t = clock() - t;
printf("\nTook %f secs (%f/sec)\n", (float)t/(float)CLOCKS_PER_SEC, (float)limit/((float)t/(float)CLOCKS_PER_SEC));
#endif
#endif
delete g_CurrentLocale;
JS_DestroyContext(cx);
JS_DestroyRuntime(rt);
JS_ShutDown();
getchar();
}

View file

@ -1,298 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "Interface.h"
#include "scripting/SpiderMonkey.h"
#define FILE_PATH "..\\data\\mods\\official\\language\\test\\"
extern I18n::CLocale_interface* g_CurrentLocale;
#define translate(x) g_CurrentLocale->Translate(x)
extern std::string readfile(const char* fn);
#include <stdarg.h>
typedef char CHAR;
typedef const CHAR *LPCSTR;
extern "C" {
__declspec(dllimport) void __stdcall
OutputDebugStringA(LPCSTR lpOutputString);
}
/*
WINBASEAPI
VOID
WINAPI
OutputDebugStringA(
IN LPCSTR lpOutputString
);
*/
void test_assert(bool test, const char* file, int line, const char* fmt, ...)
{
if (! test)
{
char msg[512];
va_list v;
va_start(v, fmt);
_vsnprintf(msg, sizeof(msg), fmt, v);
char err[512];
_snprintf(err, sizeof(err), "%s(%d) : TEST FAILED : %s\n", file, line, msg);
err[sizeof(err)-1] = 0;
OutputDebugStringA(err);
printf("%s", err);
}
}
#ifndef I18NDEBUG
#error Please compile with I18NDEBUG debug
#endif
namespace I18n { extern bool g_UsedCache; }
#define TEST_EQ_NOCACHE(answer, str, param) \
{ \
I18n::Str s = translate(L##str) param; \
test_assert(s == L##answer, __FILE__, __LINE__, "Unexpected output: %ls", s.c_str()); \
test_assert(!I18n::g_UsedCache, __FILE__, __LINE__, "Unexpectedly used cache"); \
}
#define TEST_EQ_CACHED(answer, str, param) \
{ \
I18n::Str s = translate(L##str) param; \
test_assert(s == L##answer, __FILE__, __LINE__, "Unexpected output: %ls", s.c_str()); \
test_assert(I18n::g_UsedCache, __FILE__, __LINE__, "Failed to use cache"); \
}
#define TEST_EQ_SCRIPT(answer, str) \
{ \
jsval rval; \
if (JS_EvaluateScript(cx, glob, str, (int)strlen(str), "test", 1, &rval) != JS_TRUE) \
test_assert(false, __FILE__, __LINE__, "Script eval failed"); \
else { \
JSString* r = JS_ValueToString(cx, rval); \
I18n::Str s (JS_GetStringChars(r), JS_GetStringLength(r)); \
test_assert(s == L##answer, __FILE__, __LINE__, "Unexpected output:\n '%ls'\nExpected:\n '%ls'", s.c_str(), L##answer); \
} \
}
void test(JSContext* cx, JSObject* glob)
{
// Untranslated strings. Simple strings.
TEST_EQ_NOCACHE(
"Hello world",
"Hello world",
);
// Reporting of an invalid number of parameters.
TEST_EQ_NOCACHE(
"(translation error)",
"Hello world", << 1
);
// Untranslated strings with variables. Int variables.
TEST_EQ_NOCACHE(
"Hello world 1",
"Hello world $n", << 1
);
// Parsing of untranslated strings. Int variables.
TEST_EQ_NOCACHE(
"Hello world 1 23$4!",
"Hello world $a $bee$cee$$$bee!", << 1 << 2 << 3 << 4
);
// PROBLEM: Duplicated variables aren't handled correctly (or at all).
// Make sure they're never used in the identifier strings.
// Caching with ints.
TEST_EQ_CACHED(
"Hello world 1",
"Hello world $n", << 1
);
// Double variables.
TEST_EQ_NOCACHE(
"Hello world 1.500000",
"Hello world $n", << 1.5
);
// Caching with doubles.
TEST_EQ_CACHED(
"Hello world 1.500000",
"Hello world $n", << 1.5
);
/*
// More caching with doubles.
int num[2] = { -1, 0x3ff00000 }; // slightly endian-dependent
TEST_EQ_NOCACHE(
"Hello world 1.000001",
"Hello world $n", << *(double*)&num;
);
num[1] = 0xbff00000;
TEST_EQ_NOCACHE(
"Hello world -1.000001",
"Hello world $n", << *(double*)&num;
);
num[0] = -2;
TEST_EQ_NOCACHE(
"Hello world -1.000001",
"Hello world $n", << *(double*)&num;
);
*/
// Float variables.
TEST_EQ_NOCACHE(
"Hello world 2.500000",
"Hello world $n", << 2.5f
);
// Caching with floats.
TEST_EQ_CACHED(
"Hello world 2.500000",
"Hello world $n", << 2.5f
);
// Handling nouns (no noun table loaded).
TEST_EQ_NOCACHE(
"Hello world cheese",
"Hello world $n", << I18n::Noun("cheese")
);
g_CurrentLocale->LoadDictionary(readfile(FILE_PATH "nouns2.wrd").c_str());
// Handling nouns (not in noun table).
TEST_EQ_NOCACHE(
"Hello world pumpkin",
"Hello world $n", << I18n::Noun("pumpkin")
);
// Handling nouns (from noun table but missing singular).
TEST_EQ_NOCACHE(
"Hello world apple",
"Hello world $n", << I18n::Noun("apple")
);
g_CurrentLocale->UnloadDictionaries();
g_CurrentLocale->LoadDictionary(readfile(FILE_PATH "nouns.wrd").c_str());
// Name variables.
TEST_EQ_NOCACHE(
"Hello world banana",
"Hello world $n", << I18n::Name("banana")
);
// Raw string variables. Caching names / raw strings.
TEST_EQ_CACHED(
"Hello world banana",
"Hello world $n", << I18n::Raw("banana")
);
// Handling nouns (from noun table).
TEST_EQ_NOCACHE(
"Hello world BANANA",
"Hello world $n", << I18n::Noun("banana")
);
// Caching nouns.
TEST_EQ_CACHED(
"Hello world BANANA",
"Hello world $n", << I18n::Noun("banana")
);
std::string funcs = readfile(FILE_PATH "functions.js");
g_CurrentLocale->LoadFunctions(funcs.c_str(), funcs.size(), "functions.txt");
g_CurrentLocale->LoadStrings(readfile(FILE_PATH "phrases.lng").c_str());
// Functions with strings, doubles, ints, variable ints, and variable strings.
TEST_EQ_NOCACHE(
"1.2345+67890=67891.2345. An armadillo buys a platypus for 14.99 (platypus! 14.990000!)",
"Test $obj ($$$amnt)", << I18n::Noun("platypus") << 14.99
);
// Noun lookups in JS functions.
TEST_EQ_NOCACHE(
"There are (1 BANANA) here",
"Testing things $num of $object", << 1 << I18n::Noun("banana")
);
// More noun lookups in JS functions.
TEST_EQ_NOCACHE(
"There are (2 bananas) here",
"Testing things $num of $object", << 2 << I18n::Noun("banana")
);
// Noun lookups in JS functions, despite being Raw. (JS only sees strings).
TEST_EQ_NOCACHE(
"There are (2.5 bananas) here",
"Testing things $num of $object", << 2.5 << I18n::Raw("banana")
);
// Noun lookup failures in JS functions.
TEST_EQ_NOCACHE(
"There are (-3.5 orangutan) here",
"Testing things $num of $object", << -3.5 << I18n::Raw("orangutan")
);
// Repeated tests, using JS interface.
TEST_EQ_SCRIPT(
"Hello world banana",
"translate('Hello world $n', i18n.Name('banana'))"
);
TEST_EQ_SCRIPT(
"Hello world banana",
"translate('Hello world $n', i18n.Raw('banana'))"
);
TEST_EQ_SCRIPT(
"Hello world BANANA",
"translate('Hello world $n', i18n.Noun('banana'))"
);
TEST_EQ_SCRIPT(
"1.2345+67890=67891.2345. An armadillo buys a platypus for 14.99 (platypus! 14.990000!)",
"translate('Test $obj ($$$amnt)', i18n.Noun('platypus'), 14.99)"
);
TEST_EQ_SCRIPT(
"There are (1 BANANA) here",
"translate('Testing things $num of $object', 1, i18n.Noun('banana'))"
);
TEST_EQ_SCRIPT(
"There are (2 bananas) here",
"translate('Testing things $num of $object', 2, i18n.Noun('banana'))"
);
TEST_EQ_SCRIPT(
"There are (2.5 bananas) here",
"translate('Testing things $num of $object', 2.5, i18n.Raw('banana'))"
);
TEST_EQ_SCRIPT(
"There are (-3.5 orangutan) here",
"translate('Testing things $num of $object', -3.5, i18n.Raw('orangutan'))"
);
}

View file

@ -1,262 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Intel C++ Project"
Version="9.0"
Name="tests"
ProjectGUID="{B48BE4CA-40AF-4D48-86CC-7FCA6BDD02BF}"
VCNestedProjectGUID="{1D6A376C-DA9E-4E03-83A7-16784F356633}"
VCNestedProjectFileName="tests.vcproj"
Keyword="Win32Proj">
<Configurations>
<Configuration
Name="Debug|Win32">
<Tool
Name="GeneralTool"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2"
CompilerName="1"/>
<Tool
Name="DebugTool"
WorkingDirectory="e:\0ad\svnc\trunk\binaries\system"/>
<Tool
Name="CppCmplrTool"
Optimization="0"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;I18NDEBUG"
MinimalRebuild="1"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="1"
DebugInformationFormat="4"/>
<Tool
Name="LinkerTool"
DelayImpLib=""
AdditionalDependencies="kernel32.lib js32d.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="1"
ProgramDatabaseFile="$(OutDir)/tests.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="LibrarianTool"/>
<Tool
Name="ResCmplrTool"/>
<Tool
Name="MidlCmplrTool"/>
<Tool
Name="BscMakeTool"/>
<Tool
Name="PreBuildEventTool"/>
<Tool
Name="PreLinkEventTool"/>
<Tool
Name="PostBuildEventTool"/>
<Tool
Name="CustomTool"/>
<Tool
Name="CustomNodeTool"/>
</Configuration>
<Configuration
Name="Release|Win32">
<Tool
Name="GeneralTool"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2"
CompilerName="1"/>
<Tool
Name="DebugTool"
WorkingDirectory="e:\0ad\svnc\trunk\binaries\system"/>
<Tool
Name="CppCmplrTool"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="4"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="1"
DebugInformationFormat="3"/>
<Tool
Name="LinkerTool"
DelayImpLib=""
AdditionalDependencies="kernel32.lib js32.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="1"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="LibrarianTool"/>
<Tool
Name="ResCmplrTool"/>
<Tool
Name="MidlCmplrTool"/>
<Tool
Name="BscMakeTool"/>
<Tool
Name="PreBuildEventTool"/>
<Tool
Name="PreLinkEventTool"/>
<Tool
Name="PostBuildEventTool"/>
<Tool
Name="CustomTool"/>
<Tool
Name="CustomNodeTool"/>
</Configuration>
<Configuration
Name="Coverage|Win32">
<Tool
Name="GeneralTool"
OutputDirectory="Coverage"
IntermediateDirectory="Coverage"
ConfigurationType="1"
CharacterSet="2"
PGOPhase="2"/>
<Tool
Name="DebugTool"
WorkingDirectory="e:\0ad\svnc\trunk\binaries\system"/>
<Tool
Name="CppCmplrTool"
Optimization="0"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;I18NDEBUG"
MinimalRebuild="1"
BasicRuntimeChecks="0"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="1"
DebugInformationFormat="3"/>
<Tool
Name="LinkerTool"
DelayImpLib=""
AdditionalDependencies="kernel32.lib js32d.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="2"
SuppressStartupBanner="1"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="1"
ProgramDatabaseFile="$(OutDir)/tests.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="LibrarianTool"/>
<Tool
Name="ResCmplrTool"/>
<Tool
Name="MidlCmplrTool"/>
<Tool
Name="BscMakeTool"/>
<Tool
Name="PreBuildEventTool"/>
<Tool
Name="PreLinkEventTool"/>
<Tool
Name="PostBuildEventTool"/>
<Tool
Name="CustomTool"/>
<Tool
Name="CustomNodeTool"/>
</Configuration>
</Configurations>
<Files>
<File
RelativePath=".\CStr.h"/>
<File
RelativePath=".\precompiled.cpp">
<FileConfiguration
Name="Coverage|Win32">
<Tool
Name="CppCmplrTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="CppCmplrTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="CppCmplrTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\precompiled.h"/>
<File
RelativePath=".\StringConvert.cpp"/>
<File
RelativePath=".\StringConvert.h"/>
<File
RelativePath=".\test.cpp"/>
<File
RelativePath=".\tests.cpp"/>
<File
RelativePath=".\UniDoubler.h"/>
<File
RelativePath=".\utf16string.h"/>
<File
RelativePath="..\BufferVariable.cpp"/>
<File
RelativePath="..\BufferVariable.h"/>
<File
RelativePath="..\CLocale.cpp"/>
<File
RelativePath="..\CLocale.h"/>
<File
RelativePath="..\Common.h"/>
<File
RelativePath="..\DataTypes.h"/>
<File
RelativePath="..\Interface.cpp"/>
<File
RelativePath="..\Interface.h"/>
<File
RelativePath="..\ScriptInterface.cpp"/>
<File
RelativePath="..\ScriptInterface.h"/>
<File
RelativePath="..\StrImmutable.h"/>
<File
RelativePath="..\StringBuffer.cpp"/>
<File
RelativePath="..\StringBuffer.h"/>
<File
RelativePath="..\TranslatedString.cpp"/>
<File
RelativePath="..\TranslatedString.h"/>
<File
RelativePath="..\TSComponent.cpp"/>
<File
RelativePath="..\TSComponent.h"/>
<File
RelativePath=".\lib\types.h"/>
<File
RelativePath=".\lib\sysdep\sysdep.h"/>
<File
RelativePath=".\ps\CLogger.h"/>
<File
RelativePath=".\ps\Errors.cpp"/>
<File
RelativePath=".\ps\Errors.h"/>
<File
RelativePath=".\scripting\SpiderMonkey.h"/>
</Files>
</VisualStudioProject>

View file

@ -1,29 +0,0 @@
Microsoft Visual Studio Solution File, Format Version 8.00
Project("{EAF909A5-FA59-4C3D-9431-0FCC20D5BCF9}") = "tests", "tests.icproj", "{9B3CF673-0EF8-4385-8AFE-456CD309D91A}"
ProjectSection(ProjectDependencies) = postProject
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfiguration) = preSolution
Coverage = Coverage
Debug = Debug
Release = Release
EndGlobalSection
GlobalSection(ProjectConfiguration) = postSolution
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Coverage.ActiveCfg = Coverage|Win32
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Coverage.Build.0 = Coverage|Win32
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Debug.ActiveCfg = Debug|Win32
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Debug.Build.0 = Debug|Win32
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Release.ActiveCfg = Release|Win32
{9B3CF673-0EF8-4385-8AFE-456CD309D91A}.Release.Build.0 = Release|Win32
{1D6A376C-DA9E-4E03-83A7-16784F356633}.Coverage.ActiveCfg = Coverage|Win32
{1D6A376C-DA9E-4E03-83A7-16784F356633}.Debug.ActiveCfg = Debug|Win32
{1D6A376C-DA9E-4E03-83A7-16784F356633}.Debug.Build.0 = Debug|Win32
{1D6A376C-DA9E-4E03-83A7-16784F356633}.Release.ActiveCfg = Release|Win32
{1D6A376C-DA9E-4E03-83A7-16784F356633}.Release.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
EndGlobalSection
GlobalSection(ExtensibilityAddIns) = postSolution
EndGlobalSection
EndGlobal

View file

@ -1,343 +0,0 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="7.10"
Name="tests"
ProjectGUID="{1D6A376C-DA9E-4E03-83A7-16784F356633}"
RootNamespace="tests"
Keyword="Win32Proj">
<Platforms>
<Platform
Name="Win32"/>
</Platforms>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="Debug"
IntermediateDirectory="Debug"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;I18NDEBUG"
MinimalRebuild="TRUE"
BasicRuntimeChecks="3"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="4"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="kernel32.lib js32d.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="2"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/tests.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="Release"
IntermediateDirectory="Release"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
RuntimeLibrary="4"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="kernel32.lib js32.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="1"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="TRUE"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
<Configuration
Name="Coverage|Win32"
OutputDirectory="Coverage"
IntermediateDirectory="Coverage"
ConfigurationType="1"
CharacterSet="2">
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories=".;..;..\..\..\libraries\spidermonkey\include"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;I18NDEBUG"
MinimalRebuild="TRUE"
BasicRuntimeChecks="0"
RuntimeLibrary="5"
UsePrecompiledHeader="3"
PrecompiledHeaderThrough="precompiled.h"
WarningLevel="3"
Detect64BitPortabilityProblems="TRUE"
DebugInformationFormat="3"/>
<Tool
Name="VCCustomBuildTool"/>
<Tool
Name="VCLinkerTool"
AdditionalDependencies="kernel32.lib js32d.lib"
OutputFile="$(OutDir)/tests.exe"
LinkIncremental="2"
SuppressStartupBanner="TRUE"
AdditionalLibraryDirectories="E:\0ad\libs\lib"
GenerateDebugInformation="TRUE"
ProgramDatabaseFile="$(OutDir)/tests.pdb"
SubSystem="1"
TargetMachine="1"/>
<Tool
Name="VCMIDLTool"/>
<Tool
Name="VCPostBuildEventTool"/>
<Tool
Name="VCPreBuildEventTool"/>
<Tool
Name="VCPreLinkEventTool"/>
<Tool
Name="VCResourceCompilerTool"/>
<Tool
Name="VCWebServiceProxyGeneratorTool"/>
<Tool
Name="VCXMLDataGeneratorTool"/>
<Tool
Name="VCWebDeploymentTool"/>
<Tool
Name="VCManagedWrapperGeneratorTool"/>
<Tool
Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<Filter
Name="Source Files"
Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"
UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}">
<File
RelativePath=".\CStr.h">
</File>
<File
RelativePath=".\precompiled.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
<FileConfiguration
Name="Coverage|Win32">
<Tool
Name="VCCLCompilerTool"
UsePrecompiledHeader="1"/>
</FileConfiguration>
</File>
<File
RelativePath=".\precompiled.h">
</File>
<File
RelativePath=".\StringConvert.cpp">
</File>
<File
RelativePath=".\StringConvert.h">
</File>
<File
RelativePath=".\test.cpp">
</File>
<File
RelativePath=".\tests.cpp">
</File>
<File
RelativePath=".\UniDoubler.h">
</File>
<File
RelativePath=".\utf16string.h">
</File>
<Filter
Name="i18n"
Filter="">
<File
RelativePath="..\BufferVariable.cpp">
</File>
<File
RelativePath="..\BufferVariable.h">
</File>
<File
RelativePath="..\CLocale.cpp">
</File>
<File
RelativePath="..\CLocale.h">
</File>
<File
RelativePath="..\Common.h">
</File>
<File
RelativePath="..\DataTypes.h">
</File>
<File
RelativePath="..\Interface.cpp">
</File>
<File
RelativePath="..\Interface.h">
</File>
<File
RelativePath="..\ScriptInterface.cpp">
</File>
<File
RelativePath="..\ScriptInterface.h">
</File>
<File
RelativePath="..\StrImmutable.h">
</File>
<File
RelativePath="..\StringBuffer.cpp">
</File>
<File
RelativePath="..\StringBuffer.h">
</File>
<File
RelativePath="..\TranslatedString.cpp">
</File>
<File
RelativePath="..\TranslatedString.h">
</File>
<File
RelativePath="..\TSComponent.cpp">
</File>
<File
RelativePath="..\TSComponent.h">
</File>
</Filter>
<Filter
Name="lib"
Filter="">
<File
RelativePath=".\lib\types.h">
</File>
<Filter
Name="sysdep"
Filter="">
<File
RelativePath=".\lib\sysdep\sysdep.h">
</File>
</Filter>
</Filter>
<Filter
Name="ps"
Filter="">
<File
RelativePath=".\ps\CLogger.h">
</File>
<File
RelativePath=".\ps\Errors.cpp">
<FileConfiguration
Name="Debug|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Release|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
<FileConfiguration
Name="Coverage|Win32">
<Tool
Name="VCCLCompilerTool"
ObjectFile="$(IntDir)/$(InputName)1.obj"/>
</FileConfiguration>
</File>
<File
RelativePath=".\ps\Errors.h">
</File>
</Filter>
<Filter
Name="scripting"
Filter="">
<File
RelativePath=".\scripting\SpiderMonkey.h">
</File>
</Filter>
</Filter>
<Filter
Name="Header Files"
Filter="h;hpp;hxx;hm;inl;inc;xsd"
UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}">
</Filter>
<Filter
Name="Resource Files"
Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"
UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}">
</Filter>
</Files>
<Globals>
</Globals>
</VisualStudioProject>

View file

@ -1,122 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
/*
A basic_string derivative that works with uint16_t as its underlying char
type.
*/
#ifndef INCLUDED_UTF16STRING
#define INCLUDED_UTF16STRING
// On Windows, wchar_t is typedef'ed to unsigned short, which conflicts
// with uint16_t (which is also an unsigned short), so just use std::wstring
#ifdef _MSC_VER
typedef wchar_t utf16_t;
typedef std::wstring utf16string;
typedef std::wstringstream utf16stringstream;
// On Linux, wchar_t is 32-bit, so define a new version of it
#else
#include <string>
#include "types.h"
typedef uint16_t utf16_t;
typedef std::basic_string<utf16_t> utf16string;
typedef std::basic_stringstream<utf16_t> utf16stringstream;
namespace std {
template<>
struct char_traits<utf16_t>
{
typedef utf16_t char_type;
typedef int int_type;
typedef streampos pos_type;
typedef streamoff off_type;
typedef mbstate_t state_type;
static void assign(char_type& c1, const char_type& c2)
{ c1 = c2; }
static bool eq(const char_type& c1, const char_type& c2)
{ return c1 == c2; }
static bool lt(const char_type& c1, const char_type& c2)
{ return c1 < c2; }
static int compare(const char_type* s1, const char_type* s2, size_t n)
{
return memcmp(s1, s2, n*sizeof(char_type));
}
static size_t length(const char_type* s)
{
const char_type* end=s;
while (*end) end++;
return end-s;
}
static const char_type* find(const char_type* s, size_t n, const char_type& a)
{
size_t i;
for (i=0;i<n;i++)
{
if (s[i] == a) return s+i;
}
return NULL;
}
static char_type* move(char_type* s1, const char_type* s2, size_t n)
{
return (char_type *)memmove(s1, s2, n*sizeof(char_type));
}
static char_type* copy(char_type* s1, const char_type* s2, size_t n)
{
return (char_type *)memcpy(s1, s2, n*sizeof(char_type));
}
static char_type* assign(char_type* s, size_t n, char_type a)
{
while (n--)
{
s[n]=a;
}
return s;
}
static char_type to_char_type(const int_type& c)
{ return (char_type)c; }
static int_type to_int_type(const char_type& c)
{ return (int_type)c; }
static bool eq_int_type(const int_type& c1, const int_type& c2)
{ return c1 == c2; }
static int_type eof()
{ return -1; }
static int_type not_eof(const int_type& c)
{ return (c == -1) ? 0 : c; }
};
}
#endif // #ifdef _MSC_VER / #else
#endif

View file

@ -1,18 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"

View file

@ -1,23 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#define MINIMAL_PCH 1
#include "lib/precompiled.h" // common precompiled header
// Minimal is a bit *too* minimal to let things compile, so include a few more headers
#include "lib/debug.h"
#include "lib/secure_crt.h"

View file

@ -52,7 +52,6 @@
#include "ps/Util.h"
#include "ps/VideoMode.h"
#include "ps/World.h"
#include "ps/i18n.h"
#include "graphics/CinemaTrack.h"
#include "graphics/GameView.h"
@ -185,8 +184,7 @@ retry:
// display progress / description in loading screen
void GUI_DisplayLoadProgress(int percent, const wchar_t* pending_task)
{
CStrW i18n_description = I18n::translate(pending_task);
JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), i18n_description);
JSString* js_desc = StringConvert::wstring_to_jsstring(g_ScriptingHost.getContext(), pending_task);
g_ScriptingHost.SetGlobal("g_Progress", INT_TO_JSVAL(percent));
g_ScriptingHost.SetGlobal("g_LoadDescription", STRING_TO_JSVAL(js_desc));
g_GUI->SendEventToAll("progress");
@ -475,14 +473,9 @@ static void InitPs(bool setup_gui, const CStrW& gui_page, CScriptVal initData)
g_Console->m_iFontOffset = 7;
}
// language and hotkeys
// hotkeys
{
TIMER(L"ps_lang_hotkeys");
std::string lang = "english";
CFG_GET_SYS_VAL("language", String, lang);
I18n::LoadLanguage(lang.c_str());
LoadHotkeys();
}
@ -547,10 +540,6 @@ static void ShutdownPs()
// disable the special Windows cursor, or free textures for OGL cursors
cursor_draw(g_VFS, 0, g_mouse_x, g_yres-g_mouse_y);
// Unload the real language (since it depends on the scripting engine,
// which is going to be killed later) and use the English fallback messages
I18n::LoadLanguage(NULL);
}
@ -669,12 +658,6 @@ void Shutdown(int UNUSED(flags))
delete &g_ConfigDB;
TIMER_END(L"shutdown ConfigDB");
// Really shut down the i18n system. Any future calls
// to translate() will crash.
TIMER_BEGIN(L"shutdown I18N");
I18n::Shutdown();
TIMER_END(L"shutdown I18N");
// resource
// first shut down all resource owners, and then the handle manager.
TIMER_BEGIN(L"resource modules");
@ -768,17 +751,6 @@ void Init(const CmdLineArgs& args, int flags)
exit(0);
}
// Call LoadLanguage(NULL) to initialize the I18n system, but
// without loading an actual language file - translate() will
// just show the English key text, which is better than crashing
// from a null pointer when attempting to translate e.g. error messages.
// Real languages can only be loaded when the scripting system has
// been initialised.
//
// this uses LOG and must therefore come after CLogger init.
MICROLOG(L"init i18n");
I18n::LoadLanguage(NULL);
// override ah_translate with our i18n code.
AppHooks hooks = {0};
hooks.translate = psTranslate;

View file

@ -20,7 +20,6 @@
#include <cstdio>
#include "Pyrogenesis.h"
#include "ps/i18n.h"
#include "lib/sysdep/sysdep.h"
#include "lib/path_util.h"
@ -31,21 +30,7 @@ static const wchar_t* translate_no_mem = L"(no mem)";
// overrides ah_translate. registered in GameSetup.cpp
const wchar_t* psTranslate(const wchar_t* text)
{
// make sure i18n system is (already|still) initialized.
if(g_CurrentLocale)
{
// be prepared for this to fail, because translation potentially
// involves script code and the JS context might be corrupted.
try
{
CStrW ret = I18n::translate(text);
const wchar_t* ret_dup = wcsdup(ret.c_str());
return ret_dup? ret_dup : translate_no_mem;
}
catch(...)
{
}
}
// TODO: implement this somehow
// i18n not available: at least try and return the text (unchanged)
const wchar_t* ret_dup = wcsdup(text);

View file

@ -1,133 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#include "precompiled.h"
#include "ps/i18n.h"
#include "lib/utf8.h"
#include "ps/Filesystem.h"
#include "scripting/ScriptingHost.h"
#include "ps/CLogger.h"
#define LOG_CATEGORY L"i18n"
// Yay, global variables. (The user only ever wants to be using one language
// at a time, so this is sufficient)
I18n::CLocale_interface* g_CurrentLocale = NULL;
std::string g_CurrentLocaleName;
bool I18n::LoadLanguage(const char* name)
{
// Special case: If name==NULL, use an 'empty' locale which should have
// no external dependencies other than CLogger, so it can be called
// before SpiderMonkey/etc has been initialised (useful for localised
// error messages that should fall back to English if the language hasn't
// been loaded yet)
if (name == NULL)
{
CLocale_interface* locale_ptr = I18n::NewLocale(NULL, NULL);
debug_assert(locale_ptr);
delete g_CurrentLocale;
g_CurrentLocale = locale_ptr;
g_CurrentLocaleName = "";
return true;
}
CLocale_interface* locale_ptr = I18n::NewLocale(g_ScriptingHost.getContext(), JS_GetGlobalObject(g_ScriptingHost.getContext()));
if (! locale_ptr)
{
debug_warn(L"Failed to create locale");
return false;
}
// Automatically delete the pointer when returning early
std::auto_ptr<CLocale_interface> locale (locale_ptr);
VfsPath dirname = AddSlash(VfsPath(L"language")/wstring_from_utf8(name));
// Open *.lng with LoadStrings
VfsPaths pathnames;
if(fs_util::GetPathnames(g_VFS, dirname, L"*.lng", pathnames) < 0)
return false;
for (size_t i = 0; i < pathnames.size(); i++)
{
CVFSFile strings;
if (! (strings.Load(g_VFS, pathnames[i]) == PSRETURN_OK && locale->LoadStrings((const char*)strings.GetBuffer())))
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error opening language string file '%ls'", pathnames[i].string().c_str());
return false;
}
}
// Open *.wrd with LoadDictionary
if(fs_util::GetPathnames(g_VFS, dirname, L"*.wrd", pathnames) < 0)
return false;
for (size_t i = 0; i < pathnames.size(); i++)
{
CVFSFile strings;
if (! (strings.Load(g_VFS, pathnames[i]) == PSRETURN_OK && locale->LoadDictionary((const char*)strings.GetBuffer())))
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error opening language string file '%ls'", pathnames[i].string().c_str());
return false;
}
}
// Open *.js with LoadFunctions
if(fs_util::GetPathnames(g_VFS, dirname, L"*.js", pathnames) < 0)
return false;
for (size_t i = 0; i < pathnames.size(); i++)
{
const wchar_t* pathname = pathnames[i].string().c_str();
CStr8 pathname8(pathname);
CVFSFile strings;
if (! (strings.Load(g_VFS, pathname) == PSRETURN_OK
&&
locale->LoadFunctions(
(const char*)strings.GetBuffer(),
strings.GetBufferSize(),
pathname8.c_str()
)))
{
LOG(CLogger::Error, LOG_CATEGORY, L"Error opening language function file '%ls'", pathname);
return false;
}
}
// Free any previously loaded data
delete g_CurrentLocale;
// Store the new CLocale*, and stop the auto_ptr from deleting it
g_CurrentLocale = locale.release();
// Remember the name
g_CurrentLocaleName = name;
return true;
}
const char* I18n::CurrentLanguageName()
{
return g_CurrentLocaleName.c_str();
}
void I18n::Shutdown()
{
delete g_CurrentLocale;
g_CurrentLocale = NULL;
}

View file

@ -1,34 +0,0 @@
/* Copyright (C) 2009 Wildfire Games.
* This file is part of 0 A.D.
*
* 0 A.D. is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* 0 A.D. is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef INCLUDED_I18N
#define INCLUDED_I18N
#include "i18n/Interface.h"
extern I18n::CLocale_interface* g_CurrentLocale;
namespace I18n
{
inline StringBuffer translate(const wchar_t* s) { return g_CurrentLocale->Translate(s); }
bool LoadLanguage(const char* name);
const char* CurrentLanguageName();
void Shutdown();
}
#endif // INCLUDED_I18N

View file

@ -50,7 +50,6 @@
#include "ps/Hotkey.h"
#include "ps/ProfileViewer.h"
#include "ps/World.h"
#include "ps/i18n.h"
#include "ps/scripting/JSInterface_Console.h"
#include "ps/scripting/JSInterface_VFS.h"
#include "renderer/Renderer.h"

View file

@ -1,72 +0,0 @@
use strict;
use warnings;
package DataFiles;
# Field numbers, so the arrays which (read|write)_file use can be accessed more pleasantly
use constant STR_ID => 0;
use constant STR_CONTEXT => 1;
use constant STR_DESCRIPTION => 2;
use constant STR_FLAGS => 3;
# Export the field numbers
require Exporter;
our @ISA = qw(Exporter);
our @EXPORT = qw(STR_ID STR_CONTEXT STR_DESCRIPTION STR_FLAGS);
# Read a file containing \n-separated fields and \n\n-separated records
sub read_file {
my ($filename, %params) = @_;
# If 'ignoremissing' has been passed, handle non-existent files as if they were empty
if ($params{ignoremissing} and not -e $filename) {
return [];
}
open my $f, '<', $filename or die "Error opening $filename for input: $!";
my (@records, @fields);
while (<$f>) {
chomp;
if (length) {
# Each line stores a separate field
push @fields, unescape($_);
} else {
# Split records by empty lines
push @records, [@fields];
@fields = ();
}
}
push @records, [@fields] if @fields;
return \@records;
}
# Inverse of read_file
sub write_file {
my ($filename, $data) = @_;
open my $f, '>', $filename or die "Error opening $filename for output: $!";
for (@$data) {
print $f escape($_), "\n" for @$_;
print $f "\n";
}
}
# Convert \ to \\, newlines to \n, blank lines to \
sub unescape {
my $t = $_[0];
return "" if $t eq "\\";
$t =~ s/\\n/\n/g;
$t =~ s/\\\\/\\/g;
$t;
}
sub escape {
my $t = $_[0];
return "\\" unless defined $t and length $t;
$t =~ s/\\/\\\\/g;
$t =~ s/\n/\\n/g;
$t;
}
1;

View file

@ -1,53 +0,0 @@
use strict;
use warnings;
package StringExtract::CCode;
use StringExtract::Utils;
use Regexp::Common qw(delimited);
our $data = {
file_roots => [
'source',
],
file_roots_ignore => [
'source/i18n/tests',
'source/tools',
],
file_types => qr/\.(?:cpp|h)$/i,
readfile_func => sub { extract(StringExtract::Utils::read_text(@_)); },
};
sub extract {
my ($data, $text) = @_;
$text = StringExtract::Utils::strip_comments($text, 'C++');
my @strings;
while ($text =~ /translate\s*\(\s*L\s*($RE{delimited}{-delim=>'"'})\s*\)/g) {
my $str = $1;
# Remove surrounding quotes
$str =~ s/^"(.*)"$/$1/;
# Translate \" sequences
$str =~ s/\\"/"/g;
# and \n
$str =~ s/\\n/\n/g;
# and \\
$str =~ s/\\\\/\\/g;
(my $filename = $data->{filename}) =~ s~.*?source/~~; # make the filenames a bit neater
push @strings, [ "phrase:".$str, "C++ $filename:".(1+StringExtract::Utils::count_newlines(substr $text, 0, pos $text)) ];
}
return @strings;
}
1;

View file

@ -1,47 +0,0 @@
use strict;
use warnings;
package StringExtract::JSCode;
use StringExtract::Utils;
use Regexp::Common qw(delimited);
our $data = {
file_roots => [
'binaries/data/mods/official/gui',
],
file_types => qr/\.js$/i,
readfile_func => sub { extract(StringExtract::Utils::read_text(@_)); },
};
sub extract {
my ($data, $text) = @_;
return unless defined $text;
$text = StringExtract::Utils::strip_comments($text, 'Java');
my @strings;
while ($text =~ /translate\s*\(\s*($RE{delimited}{-delim=>'"'})/g) {
my $str = $1;
# Remove surrounding quotes
$str =~ s/^"(.*)"$/$1/;
# Translate \" sequences
$str =~ s/\\"/"/g;
# and \n
$str =~ s/\\n/\n/g;
# and \\
$str =~ s/\\\\/\\/g;
push @strings, [ "phrase:".$1, "GUI script" ];
}
return @strings;
}
1;

View file

@ -1,89 +0,0 @@
use strict;
use warnings;
package StringExtract::Utils;
use XML::Simple();
use Carp;
use Regexp::Common qw(comment);
sub read_xml {
my ($filename) = $_;
my $xml = new XML::Simple(
ForceArray => 1,
KeepRoot => 1,
KeyAttr => [],
ForceContent => 1,
);
my $filedata = do { open my $f, '<', $filename or die $!; local $/; <$f> };
# Fix DTD paths
my ($dir) = ($filename =~ m~(.*)[\\/]~);
$filedata =~ s {SYSTEM "(?!/)} {SYSTEM "$dir/};
$filedata =~ s {SYSTEM "/} {SYSTEM "../../../binaries/data/mods/official/};
my $data = eval { $xml->XMLin($filedata) };
if ($@) {
warn "Error reading $filename: $@";
return;
}
recursive_process($data);
my ($filename_short) = ($filename =~ m~([^\\/]+)$~);
my $root = (keys %$data)[0];
return [ $root, $filename_short, @{$data->{$root}} ];
}
sub recursive_process {
if (ref($_[0]) eq 'HASH') {
# Force keys to lowercase
my %temp;
@temp{map lc, keys %{$_[0]}} = values %{$_[0]};
%{$_[0]} = %temp;
# Trim whitespace in content
$_[0]{content} =~ s/^\s*(.*?)\s*$/$1/s if exists $_[0]{content};
# Recurse through sub-elements
recursive_process($_) for values %{$_[0]};
} elsif (ref($_[0]) eq 'ARRAY') {
# Recurse through sub-elements
recursive_process($_) for @{$_[0]};
}
}
sub read_text {
my ($filename) = $_;
open my $file, '<', $filename or carp "Error opening $filename: $!";
my $data = do { local $/; <$file> };
return ({ filename => $filename }, $data);
}
sub strip_comments {
my ($data, $lang) = @_;
#$data =~ s/($RE{comment}{$lang})/ "\n" x count_newlines($1) /eg;
# TODO: Make it work. (The above code fails on lines like 'translate(L"Go to http://oops, this shouldn't be a comment)"')
$data;
}
sub count_newlines {
return $_[0] =~ tr/\n//;
}
1;

View file

@ -1,84 +0,0 @@
use strict;
use warnings;
package StringExtract::XML;
use StringExtract::Utils;
use StringExtract::JSCode;
our $data = {
file_roots => [
'binaries/data/mods/official/art',
'binaries/data/mods/official/entities',
'binaries/data/mods/official/gui',
],
file_types => qr/\.xml$/i,
readfile_func => sub { extract(StringExtract::Utils::read_xml(@_)); },
};
sub extract {
my ($xmldata) = @_;
return unless $xmldata;
my ($root, $filename, %elements) = (@{$xmldata}[0, 1], %{$xmldata->[2]});
my @strings;
# Entities
if ($root eq 'entity') {
push @strings, map [ "noun:".$_->{content}, "Entity name ($filename)" ], @{$elements{name}};
# Actors
} elsif ($root eq 'object') {
push @strings, map [ "noun:".$_->{content}, "Actor name ($filename)" ], @{$elements{name}};
# Materials
} elsif ($root eq 'material') {
# GUI objects
} elsif ($root eq 'objects') {
recursive_extract_guiobject($filename, \@strings, [\%elements]);
# GUI setup
} elsif ($root eq 'setup') {
# Do nothing
# GUI sprites
} elsif ($root eq 'sprites') {
# Do nothing
# GUI styles
} elsif ($root eq 'styles') {
# Do nothing
} else {
warn "Unrecognised XML file type '$root' - ignoring";
}
return @strings;
}
sub recursive_extract_guiobject {
my ($filename, $strings, $elements) = @_;
for my $element (@$elements) {
push @$strings, [ "phrase:".$element->{tooltip}, "GUI tooltip ($filename)" ] if defined $element->{tooltip};
push @$strings, [ "phrase:".$element->{content}, "GUI text ($filename)" ] if defined $element->{content};
if ($element->{script}) {
push @$strings, StringExtract::JSCode::extract($_->{content}) for @{$element->{script}};
}
if ($element->{action}) {
push @$strings, StringExtract::JSCode::extract($_->{content}) for @{$element->{action}};
}
recursive_extract_guiobject($filename, $strings, $element->{object}) if $element->{object};
}
}
1;

View file

@ -1,61 +0,0 @@
use strict;
use warnings;
package Strings;
use DataFiles;
sub merge {
my ($filename, %new_strings) = @_;
# Read the earlier string data
my $strings = DataFiles::read_file($filename, ignoremissing=>1);
for my $old (@$strings) {
my $stringid = $old->[STR_ID];
if (exists $new_strings{$stringid}) {
# String already exists; just update the content
if ($new_strings{$stringid}{context}) {
$old->[STR_CONTEXT] = $new_strings{$stringid}{context};
}
if ($new_strings{$stringid}{description}) {
$old->[STR_DESCRIPTION] = $new_strings{$stringid}{description};
}
# Make sure it's not obsolete now
flag_set(\$old->[STR_FLAGS], 'obsolete', 0);
# Remove it from this list, so the unprocessed ones can be found later
delete $new_strings{$stringid};
} else {
# String has been removed; set obsolete flag
flag_set(\$old->[STR_FLAGS], 'obsolete', 1);
}
}
for (keys %new_strings) {
# Newly added strings
push @$strings, [ $_, len_or($new_strings{$_}{context}, "?"), len_or($new_strings{$_}{description}, "?"), "" ];
}
DataFiles::write_file($filename, $strings);
}
sub len_or { (defined $_[0] and length $_[0]) ? $_[0] : $_[1] }
sub flag_set {
my ($str, $flagname, $value) = @_;
my @flags = split / /, $$str;
if ($value) {
push @flags, $flagname unless grep $_ eq $flagname, @flags;
} else {
@flags = grep $_ ne $flagname, @flags;
}
$$str = join ' ', @flags;
}
1;

View file

@ -1,40 +0,0 @@
use strict;
use warnings;
package Translations;
use DataFiles;
sub merge {
my ($filename, %new_translations) = @_;
# Read the earlier translation data
my $translations = DataFiles::read_file($filename, ignoremissing=>1);
for my $old (@$translations) {
my $stringid = $old->[0];
if (exists $new_translations{$stringid}) {
# Translation already exists; just update the content
$old->[1] = $new_translations{$stringid};
# Remove it from this list, so the unprocessed ones can be found later
delete $new_translations{$stringid};
} else {
# String has been removed; leave it for now
}
}
for (keys %new_translations) {
# Newly added translations
push @$translations, [ $_, len_or($new_translations{$_}, "") ];
}
DataFiles::write_file($filename, $translations);
}
sub len_or { (defined $_[0] and length $_[0]) ? $_[0] : $_[1] }
1;

View file

@ -1,8 +0,0 @@
use strict;
use warnings;
our %config;
$config{data_path} = "../../../binaries/data/tools/i18n";
$config{strings_filename} = "$config{data_path}/strings.txt";

View file

@ -1,57 +0,0 @@
use strict;
use warnings;
my $language = "english";
our %config;
do "config.pl";
use DataFiles;
use Strings;
use Translations;
use Spreadsheet::ParseExcel;
fromspreadsheet($config{strings_filename}, $config{data_path}."/$language/translations.txt", $config{data_path}."/$language/work.xls");
sub fromspreadsheet {
my ($strings_filename, $translations_filename, $spreadsheet_filename) = @_;
# Read data from spreadsheet:
my $strings = DataFiles::read_file($strings_filename);
my $translations = DataFiles::read_file($translations_filename, ignoremissing=>1);
my $workbook = Spreadsheet::ParseExcel::Workbook->Parse($spreadsheet_filename);
my %strings; # Data associated with each string. (type:id => { description => '...' }, ...)
my %translations; # Data associated with each translation. (type:id => translation, ...)
for my $worksheet (@{$workbook->{Worksheet}}) {
my $type = $worksheet->{Name};
my @rows;
for my $row ($worksheet->{MinRow} .. $worksheet->{MaxRow}) {
next if $row == 0;
push @rows, [];
for my $col ($worksheet->{MinCol} .. $worksheet->{MaxCol}) {
push @{$rows[-1]}, $worksheet->{Cells}[$row][$col]->{Val};
}
}
for (@rows) {
my $id = $type.':'.$_->[0];
if ($strings{$id}) {
warn "Duplicated string $id!";
}
$strings{$id} = { description => $_->[2] };
$translations{$id} = $_->[1];
}
}
Strings::merge($strings_filename, %strings);
Translations::merge($translations_filename, %translations);
}

View file

@ -1,84 +0,0 @@
=pod
String extraction utility.
=cut
use strict;
use warnings;
our %config;
do "config.pl";
use File::Find;
# Include appropriate pieces of code
use StringExtract::XML;
use StringExtract::JSCode;
use StringExtract::CCode;
use DataFiles;
use Strings;
my %all_strings;
# Repeat for each data type that needs to be parsed
for my $type (
$StringExtract::XML::data,
# $StringExtract::JSCode::data,
# $StringExtract::CCode::data,
) {
# Get the list of files that the module wants to handle
my @files;
# Search each file_roots, relative to ../../../
my $prefix = "../../../";
my @dirs = map $prefix.$_, @{$type->{file_roots}};
find({
preprocess => sub {
# Trim the ../../../ prefix
my $path = substr($File::Find::dir, length $prefix);
# Ignore all directories that are called '.svn', or whose paths are any of file_roots_ignore
grep not ($_ eq '.svn' or contains($type->{file_roots_ignore}, $path.'/'.$_)), @_;
},
wanted => sub {
# Include files that match the file_types regexp
push @files, $File::Find::name if /$type->{file_types}/;
},
no_chdir => 1,
}, @dirs);
# Call the appropriate read function on every matching file
my @strings = map $type->{readfile_func}->($_), @files;
# Now @strings = ( [stringid, context], ... )
# where context eq 'Entity (whatever.xml:123)'
# Build %all_strings = (stringid => [context, context, ...], ...)
for (@strings) {
# Make sure the value is an array ref
$all_strings{$_->[0]} ||= [];
# Push the string's context data onto the array ref
push @{$all_strings{$_->[0]}}, $_->[1];
}
}
# Transform into %all_strings = (stringid => { context => '...' })
for (keys %all_strings) {
$all_strings{$_} = { context => join "\n", uniq(sort @{$all_strings{$_}}) };
}
# Merge the string data with any existing information
Strings::merge($config{strings_filename}, %all_strings);
sub contains { $_[1] eq $_ && return 1 for @{$_[0]}; 0 }
sub uniq { # Uniquify sorted lists
my @r;
for (@_) { push @r, $_ unless @r and $r[-1] eq $_ };
@r;
}

View file

@ -1,72 +0,0 @@
use strict;
use warnings;
my $language = "english";
our %config;
do "config.pl";
use DataFiles;
use Spreadsheet::WriteExcel;
tospreadsheet($config{strings_filename}, $config{data_path}."/$language/translations.txt", $config{data_path}."/$language/work.xls");
sub tospreadsheet {
my ($strings_filename, $translations_filename, $spreadsheet_filename) = @_;
my $strings = DataFiles::read_file($strings_filename);
my %translations = map @$_, DataFiles::read_file($translations_filename, ignoremissing=>1);
my %data; # = ("phrase" => [ [id, translation, description, context, flags ], ... ], "noun" => ...)
for my $string (@$strings) {
my ($type, $id) = split /:/, $string->[STR_ID], 2;
my $translation = $translations{$string->[STR_ID]};
push @{$data{$type}}, [ $id, $translation, @$string[STR_DESCRIPTION, STR_CONTEXT, STR_FLAGS] ];
}
my $workbook = new Spreadsheet::WriteExcel ($spreadsheet_filename);
for my $type (reverse sort keys %data) {
my $worksheet = $workbook->add_worksheet($type);
my $headerformat = $workbook->add_format();
$headerformat->set_bold(1);
$worksheet->write(0,0, "String", $headerformat);
$worksheet->write(0,1, "Translation", $headerformat);
$worksheet->write(0,2, "Description", $headerformat);
$worksheet->write(0,3, "Used by", $headerformat);
$worksheet->freeze_panes(1, 0);
my $mainformat = $workbook->add_format();
$mainformat->set_align('top');
$mainformat->set_text_wrap();
$worksheet->set_column(0,0, 27, $mainformat);
$worksheet->set_column(1,1, 45, $mainformat);
$worksheet->set_column(2,2, 33, $mainformat);
$worksheet->set_column(3,3, 33, $mainformat);
my $row = 1;
for my $string (@{ $data{$type} }) {
my $col = 0;
$worksheet->write($row, $col++, $_) for @$string;
++$row;
}
}
}
sub find_translation {
$_->[0] eq $_[1] && return $_->[1] for @{$_[0]};
"";
}