diff --git a/HACKING b/HACKING
new file mode 100644
index 0000000..8d99f49
--- /dev/null
+++ b/HACKING
@@ -0,0 +1,26 @@
+Localization
+============
+
+Unless you a native English speaker, you may like to run Micropolis
+in your own language. Micropolis is designed to be run in any language,
+but we need translators to provide the translated text to display.
+
+ 1. Open a command prompt window and `cd' to the directory containing
+    the micropolisj.jar file.
+
+ 2. Run the following command to launch the translation tool:
+
+    java -cp micropolisj.jar micropolisj.util.TranslationTool
+
+ 3. Click Add Locale and enter the appropriate 2-character language
+    code, and (optionally) a 2-character country code.
+
+ 4. Next to each string, double-click the blank field and type in
+    a translation.
+
+ 5. Click Save. (This will cause the translated strings to be written
+    in the proper format into a subdirectory named micropolisj.)
+
+ 6. Close the translation tool. Send the generated files (from the
+    micropolisj subdirectory) to jason@long.name for inclusion in the
+    next release of Micropolis.
diff --git a/src/micropolisj/util/TranslationTool.java b/src/micropolisj/util/TranslationTool.java
index 43b9e15..8933f39 100644
--- a/src/micropolisj/util/TranslationTool.java
+++ b/src/micropolisj/util/TranslationTool.java
@@ -11,6 +11,9 @@ public class TranslationTool extends JFrame
 {
 	JTable stringsTable;
 	StringsModel stringsModel;
+	String lastLanguage;
+	String lastCountry;
+	String lastVariant;
 
 	public TranslationTool()
 		throws IOException
@@ -44,11 +47,45 @@ public class TranslationTool extends JFrame
 			}});
 		buttonPane.add(btn);
 
+		btn = new JButton("Test");
+		btn.addActionListener(new ActionListener() {
+			public void actionPerformed(ActionEvent evt) {
+				onTestClicked();
+			}});
+		buttonPane.add(btn);
+
 		pack();
 		setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE);
 		setLocationRelativeTo(null);
 	}
 
+	private void onTestClicked()
+	{
+		try
+		{
+			String javaPath = "java";
+			String classPath = "." + System.getProperty("path.separator") + "micropolisj.jar";
+
+			ProcessBuilder processBuilder =
+			new ProcessBuilder(javaPath,
+				"-Duser.language="+lastLanguage,
+				"-Duser.country="+lastCountry,
+				"-Duser.variant="+lastVariant,
+				"-cp",
+				classPath,
+				"micropolisj.Main"
+				);
+			processBuilder.start();
+		}
+		catch (Exception e)
+		{
+			JOptionPane.showMessageDialog(this,
+				e.getMessage(),
+				"Error",
+				JOptionPane.ERROR_MESSAGE);
+		}
+	}
+
 	private void onSaveClicked()
 	{
 		try
@@ -91,22 +128,22 @@ public class TranslationTool extends JFrame
 
 		try
 		{
-			String language = langEntry.getText();
-			String country = countryEntry.getText();
-			String variant = variantEntry.getText();
+			lastLanguage = langEntry.getText();
+			lastCountry = countryEntry.getText();
+			lastVariant = variantEntry.getText();
 
-			if (language.length() == 0) {
+			if (lastLanguage.length() == 0) {
 				throw new Exception("Language is required");
 			}
 
-			String code = language;
-			if (country.length() != 0) {
-				code += "_" + country;
-				if (variant.length() != 0) {
-					code += "_" + variant;
+			String code = lastLanguage;
+			if (lastCountry.length() != 0) {
+				code += "_" + lastCountry;
+				if (lastVariant.length() != 0) {
+					code += "_" + lastVariant;
 				}
 			}
-			else if (variant.length() != 0) {
+			else if (lastVariant.length() != 0) {
 				throw new Exception("Cannot specify variant without a country code.");
 			}
 
@@ -231,7 +268,13 @@ public class TranslationTool extends JFrame
 		@Override
 		public boolean isCellEditable(int row, int col)
 		{
-			return col != 0;
+			if (col == 0) {
+				return false;
+			}
+			else {
+				MyLocaleInfo l = locales.get(col-1);
+				return l.code != null;
+			}
 		}
 
 		@Override
@@ -250,7 +293,7 @@ public class TranslationTool extends JFrame
 
 		File getPFile(String file, String localeCode)
 		{
-			return new File("strings/"
+			return new File("micropolisj/"
 				+file
 				+(localeCode != null ? "_"+localeCode : "")
 				+".properties");
@@ -263,6 +306,9 @@ public class TranslationTool extends JFrame
 			for (String file : FILES)
 			{
 				Properties p = new Properties();
+				if (localeCode == null) {
+					p.load(getClass().getResourceAsStream("/micropolisj/"+file+".properties"));
+				}
 				File f = getPFile(file, localeCode);
 				if (f.exists()) {
 					p.load(new FileInputStream(f));
@@ -274,6 +320,15 @@ public class TranslationTool extends JFrame
 			fireTableStructureChanged();
 		}
 
+		void makeDirectories(File f)
+			throws IOException
+		{
+			File d = f.getParentFile();
+			if (d != null) {
+				d.mkdirs();
+			}
+		}
+
 		void save()
 			throws IOException
 		{
@@ -285,8 +340,10 @@ public class TranslationTool extends JFrame
 				{
 					Properties p = l.propsMap.get(file);
 					File f = getPFile(file, l.code);
+					makeDirectories(f);
 					p.store(new FileOutputStream(f), l.code);
 				}
+				l.dirty = false;
 			}
 		}
 	}