diff --git a/doc/man5/taskrc.5 b/doc/man5/taskrc.5
index 9fc5a50f1..01aeaa51f 100644
--- a/doc/man5/taskrc.5
+++ b/doc/man5/taskrc.5
@@ -63,6 +63,13 @@ Valid variable names and their default values are:
This is a path to the directory containing all the task files. By default, it is set up to be ~/.task,
for example: /Users/paul/.task
+.TP
+.B locale=en-US
+The locale is a combination of ISO 639-1 language code and ISO 3166 country
+code. If not specified, task will assume en-US. If specified, task will locate
+the correct file of localized strings and proceed. It is an error to specify a
+locale for which there is no strings file.
+
.TP
.B confirmation=yes
May be "yes" or "no", and determines whether task will ask for confirmation before deleting a task.
diff --git a/html/config.html b/html/config.html
index c789f64fc..3cf316f35 100644
--- a/html/config.html
+++ b/html/config.html
@@ -52,6 +52,23 @@
/Users/paul/.task
+
locale
+
+
+ A locale can be specified as a combination of an ISO 639-1
+ language code (such as 'en'), and an ISO 3166 country code
+ (such as 'US'), combined in to specify which strings file
+ to load ('en-US').
+
+
+
+ If a corresponding strings file is found, it will be loaded,
+ and all subsequent error messages and feedback will be
+ localized. It is an error to specify a locale without a
+ corresponding strings file.
+
+
+
confirmation
May be "yes" or "no", and determines whether task will ask for
diff --git a/src/Config.cpp b/src/Config.cpp
index aee4287e9..6dcb4c8ba 100644
--- a/src/Config.cpp
+++ b/src/Config.cpp
@@ -93,7 +93,7 @@ bool Config::load (const std::string& file)
while (getline (in, line))
{
// Remove comments.
- size_type pound = line.find ("#");
+ std::string::size_type pound = line.find ("#");
if (pound != std::string::npos)
line = line.substr (0, pound);
@@ -102,7 +102,7 @@ bool Config::load (const std::string& file)
// Skip empty lines.
if (line.length () > 0)
{
- size_type equal = line.find ("=");
+ std::string::size_type equal = line.find ("=");
if (equal != std::string::npos)
{
std::string key = trim (line.substr (0, equal), " \t");
diff --git a/src/StringTable.cpp b/src/StringTable.cpp
index 9dac4626f..4c11db68b 100644
--- a/src/StringTable.cpp
+++ b/src/StringTable.cpp
@@ -25,7 +25,10 @@
//
////////////////////////////////////////////////////////////////////////////////
+#include
#include
+#include
+#include
#include "StringTable.h"
////////////////////////////////////////////////////////////////////////////////
@@ -58,7 +61,6 @@ StringTable::~StringTable ()
}
////////////////////////////////////////////////////////////////////////////////
-// [data.location] / language . XX
// UTF-8 encoding
//
// 123 This is the string
@@ -66,19 +68,47 @@ StringTable::~StringTable ()
// ...
void StringTable::load (const std::string& file)
{
- // TODO Load the specified file.
+ std::ifstream in;
+ in.open (file.c_str (), std::ifstream::in);
+ if (in.good ())
+ {
+ std::string line;
+ while (getline (in, line))
+ {
+ // Remove comments.
+ std::string::size_type pound = line.find ("#");
+ if (pound != std::string::npos)
+ line = line.substr (0, pound);
+
+ line = trim (line, " \t");
+
+ // Skip empty lines.
+ if (line.length () > 0)
+ {
+ std::string::size_type equal = line.find (" ");
+ if (equal != std::string::npos)
+ {
+ int key = ::atoi (trim (line.substr (0, equal), " \t").c_str ());
+ std::string value = trim (line.substr (equal+1, line.length () - equal), " \t");
+ mMapping[key] = value;
+ }
+ }
+ }
+
+ in.close ();
+ }
+ else
+ throw std::string ("Could not read string file '") + file + "'";
}
////////////////////////////////////////////////////////////////////////////////
-std::string StringTable::get (int id)
+std::string StringTable::get (int id, const std::string& alternate)
{
// Return the right string.
if (mMapping.find (id) != mMapping.end ())
return mMapping[id];
- std::stringstream error;
- error << "MISSING " << id;
- return error.str ();
+ return alternate;
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/src/StringTable.h b/src/StringTable.h
index ef0080d97..f5226c3f2 100644
--- a/src/StringTable.h
+++ b/src/StringTable.h
@@ -39,7 +39,7 @@ public:
~StringTable (); // Destructor
void load (const std::string&);
- std::string get (int);
+ std::string get (int, const std::string&);
private:
std::map mMapping;
diff --git a/src/sandbox/Context.cpp b/src/sandbox/Context.cpp
index a8095d165..eca52e2aa 100644
--- a/src/sandbox/Context.cpp
+++ b/src/sandbox/Context.cpp
@@ -95,16 +95,21 @@ void Context::initialize (int argc, char** argv)
config.set ("color", "off");
}
+ // Load appropriate stringtable as soon after the config file as possible, to
+ // allow all subsequent messages to be localizable.
+ std::string location = expandPath (config.get ("data.location"));
+ std::string locale = config.get ("locale");
+ if (locale != "")
+ stringtable.load (location + "/strings." + locale);
+
// TODO Handle "--version, -v" right here.
// init TDB.
- std::string location = config.get ("data.location");
std::vector all;
split (all, location, ',');
foreach (path, all)
tdb.location (expandPath (*path));
- // TODO Load appropriate stringtable.
// TODO Load pending.data.
// TODO Load completed.data.
// TODO Load deleted.data.
diff --git a/src/tests/.gitignore b/src/tests/.gitignore
index d82085aed..d583ecc30 100644
--- a/src/tests/.gitignore
+++ b/src/tests/.gitignore
@@ -10,3 +10,4 @@ seq.t
att.t
mod.t
record.t
+stringtable.t
diff --git a/src/tests/Makefile b/src/tests/Makefile
index 6e69f9861..6e7f23555 100644
--- a/src/tests/Makefile
+++ b/src/tests/Makefile
@@ -1,9 +1,10 @@
PROJECT = t.t tdb.t date.t duration.t t.benchmark.t text.t autocomplete.t \
- parse.t seq.t att.t mod.t # record.t
+ parse.t seq.t att.t mod.t stringtable.t # record.t
CFLAGS = -I. -I.. -Wall -pedantic -ggdb3 -fno-rtti
LFLAGS = -L/usr/local/lib
OBJECTS = ../TDB.o ../T.o ../parse.o ../text.o ../Date.o ../Duration.o \
- ../util.o ../Config.o ../Sequence.o ../Att.o ../Record.o ../Mod.o
+ ../util.o ../Config.o ../Sequence.o ../Att.o ../Record.o ../Mod.o \
+ ../StringTable.o
all: $(PROJECT)
@@ -55,3 +56,6 @@ mod.t: mod.t.o $(OBJECTS) test.o
att.t: att.t.o $(OBJECTS) test.o
g++ att.t.o $(OBJECTS) test.o $(LFLAGS) -o att.t
+stringtable.t: stringtable.t.o $(OBJECTS) test.o
+ g++ stringtable.t.o $(OBJECTS) test.o $(LFLAGS) -o stringtable.t
+
diff --git a/src/tests/stringtable.t.cpp b/src/tests/stringtable.t.cpp
new file mode 100644
index 000000000..4d282d0b4
--- /dev/null
+++ b/src/tests/stringtable.t.cpp
@@ -0,0 +1,57 @@
+////////////////////////////////////////////////////////////////////////////////
+// task - a command line task list manager.
+//
+// Copyright 2006 - 2009, Paul Beckingham.
+// All rights reserved.
+//
+// This program 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.
+//
+// This program 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
+// this program; if not, write to the
+//
+// Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor,
+// Boston, MA
+// 02110-1301
+// USA
+//
+////////////////////////////////////////////////////////////////////////////////
+#include
+#include
+#include
+#include
+
+////////////////////////////////////////////////////////////////////////////////
+int main (int argc, char** argv)
+{
+ UnitTest t (4);
+
+ // Create a string file.
+ std::string file = "./strings.xx-XX";
+ spit (file, "# comment\n1 found");
+ t.is (access (file.c_str (), F_OK), 0, "strings.xx-XX created.");
+
+ // Load the string file.
+ StringTable st;
+ st.load (file);
+
+ // Test the object.
+ t.is (st.get (1, "nope"), "found", "string 1 'found' found");
+ t.is (st.get (2, "nope"), "nope", "string 2 'nope' defaulted");
+
+ // Clean up.
+ unlink (file.c_str ());
+ t.is (access (file.c_str (), F_OK), -1, "strings.xx-XX removed.");
+
+ return 0;
+}
+
+////////////////////////////////////////////////////////////////////////////////