diff --git a/components/net/sf/briar/i18n/I18nImpl.java b/components/net/sf/briar/i18n/I18nImpl.java
index f4095a35940de9ddffd77de2c0448ffe391f4376..cf258512a0d6bc4edc5126b8d47451a0e4c9308b 100644
--- a/components/net/sf/briar/i18n/I18nImpl.java
+++ b/components/net/sf/briar/i18n/I18nImpl.java
@@ -121,8 +121,11 @@ public class I18nImpl implements I18n {
 	}
 
 	public void loadLocale() throws IOException {
-		File root = FileUtils.getBriarDirectory();
-		Scanner s = new Scanner(new File(root, "Data/locale.cfg"));
+		loadLocale(FileUtils.getBriarDirectory());
+	}
+
+	public void loadLocale(File dir) throws IOException {
+		Scanner s = new Scanner(new File(dir, "Data/locale.cfg"));
 		if(s.hasNextLine()) setLocale(new Locale(s.nextLine()));
 		s.close();
 	}
@@ -132,7 +135,7 @@ public class I18nImpl implements I18n {
 	}
 
 	public void saveLocale(File dir) throws IOException {
-		File localeCfg = new File(dir, "locale.cfg");
+		File localeCfg = new File(dir, "Data/locale.cfg");
 		FileOutputStream out = new FileOutputStream(localeCfg);
 		PrintStream print = new PrintStream(out);
 		print.println(locale);
diff --git a/components/net/sf/briar/setup/SetupWorker.java b/components/net/sf/briar/setup/SetupWorker.java
index c51693cec1d48dde1d7d0f34859c6940b439eb72..415031796a109cb389f8237c9e450b18d91e3f9b 100644
--- a/components/net/sf/briar/setup/SetupWorker.java
+++ b/components/net/sf/briar/setup/SetupWorker.java
@@ -82,7 +82,7 @@ class SetupWorker implements Runnable {
 			createLaunchers(dir);
 			if(callback.isCancelled()) return;
 			// Save the chosen locale for the first launch
-			i18n.saveLocale(data);
+			i18n.saveLocale(dir);
 			if(callback.isCancelled()) return;
 			// Installation succeeded - delete the installer
 			jar.deleteOnExit();
diff --git a/test/build.xml b/test/build.xml
index f5f8413e0f4be0e11ef2e910c1473d709a8049be..12f16f5501fb11b2c0f99c25fe5407ad101ec217 100644
--- a/test/build.xml
+++ b/test/build.xml
@@ -13,7 +13,8 @@
 				<path refid='test-classes'/>
 				<path refid='util-classes'/>
 			</classpath>
-			<test name='net.sf.briar.i18n.FontManagerImplTest'/>
+			<test name='net.sf.briar.i18n.FontManagerTest'/>
+			<test name='net.sf.briar.i18n.I18nTest'/>
 			<test name='net.sf.briar.invitation.InvitationWorkerTest'/>
 			<test name='net.sf.briar.setup.SetupWorkerTest'/>
 			<test name='net.sf.briar.util.FileUtilsTest'/>
diff --git a/test/net/sf/briar/TestUtils.java b/test/net/sf/briar/TestUtils.java
index bd8397c85c1da84b6cd8d0032b5b8b32ef404908..32b8198f59aba43a6351db70b4f11b15fdca06ab 100644
--- a/test/net/sf/briar/TestUtils.java
+++ b/test/net/sf/briar/TestUtils.java
@@ -33,4 +33,12 @@ public class TestUtils {
 	public static void deleteTestDirectories() {
 		delete(new File("test.tmp"));
 	}
+
+	public static File getBuildDirectory() {
+		File build = new File("build"); // Ant
+		if(build.exists() && build.isDirectory()) return build;
+		File bin = new File("bin"); // Eclipse
+		if(bin.exists() && bin.isDirectory()) return bin;
+		throw new RuntimeException("Could not find build directory");
+	}
 }
diff --git a/test/net/sf/briar/i18n/FontManagerImplTest.java b/test/net/sf/briar/i18n/FontManagerTest.java
similarity index 87%
rename from test/net/sf/briar/i18n/FontManagerImplTest.java
rename to test/net/sf/briar/i18n/FontManagerTest.java
index 3c5dbd67593ac812d97cdf3ac83a06e08730a7ab..0b5ebc415d50067976cdf4c5e3c06212e86a13e8 100644
--- a/test/net/sf/briar/i18n/FontManagerImplTest.java
+++ b/test/net/sf/briar/i18n/FontManagerTest.java
@@ -3,15 +3,15 @@ import java.awt.Font;
 import java.util.Locale;
 
 import junit.framework.TestCase;
-import net.sf.briar.i18n.FontManagerImpl;
+import net.sf.briar.api.i18n.FontManager;
 
 import org.junit.Test;
 
-public class FontManagerImplTest extends TestCase {
+public class FontManagerTest extends TestCase {
 
 	@Test
 	public void testBundledFontsAreLoaded() {
-		FontManagerImpl fontManager = new FontManagerImpl();
+		FontManager fontManager = new FontManagerImpl();
 		fontManager.initialize(Locale.UK);
 
 		Font font = fontManager.getFontForLanguage("en"); // English
@@ -28,7 +28,7 @@ public class FontManagerImplTest extends TestCase {
 
 	@Test
 	public void testInternationalCharactersCanBeDisplayed() {
-		FontManagerImpl fontManager = new FontManagerImpl();
+		FontManager fontManager = new FontManagerImpl();
 		fontManager.initialize(Locale.UK);
 
 		Font font = fontManager.getFontForLanguage("en"); // English
@@ -52,7 +52,7 @@ public class FontManagerImplTest extends TestCase {
 
 	@Test
 	public void testSetAndGetUiFont() {
-		FontManagerImpl fontManager = new FontManagerImpl();
+		FontManager fontManager = new FontManagerImpl();
 		fontManager.initialize(Locale.UK);
 		Font font = fontManager.getUiFont();
 		assertEquals(12, font.getSize());
diff --git a/test/net/sf/briar/i18n/I18nTest.java b/test/net/sf/briar/i18n/I18nTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..23e9c8fa562010319970f54ace154672f1c0e78e
--- /dev/null
+++ b/test/net/sf/briar/i18n/I18nTest.java
@@ -0,0 +1,102 @@
+package net.sf.briar.i18n;
+
+import java.awt.Font;
+import java.io.File;
+import java.io.IOException;
+import java.util.Locale;
+
+import junit.framework.TestCase;
+import net.sf.briar.TestUtils;
+import net.sf.briar.api.i18n.FontManager;
+import net.sf.briar.api.i18n.I18n;
+
+import org.jmock.Expectations;
+import org.jmock.Mockery;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class I18nTest extends TestCase {
+
+	final File base =
+		new File(TestUtils.getBuildDirectory(), "i18n.properties");
+	final File french =
+		new File(TestUtils.getBuildDirectory(), "i18n_fr.properties");
+	final File testDir = TestUtils.getTestDirectory();
+
+	FontManager fontManager = null;
+	I18n i18n = null;
+
+	@Before
+	public void setUp() throws IOException {
+		TestUtils.createFile(base,
+				"FOO=foo\r\n" +
+				"BAR=bar\r\n");
+		TestUtils.createFile(french,
+				"FOO=le foo\r\n" +
+				"BAR=la bar\r\n");
+		fontManager = new FontManagerImpl();
+		fontManager.initialize(Locale.UK);
+		i18n = new I18nImpl(fontManager);
+	}
+
+	@Test
+	public void testTr() {
+		i18n.setLocale(Locale.UK);
+		assertEquals("foo", i18n.tr("FOO"));
+		i18n.setLocale(Locale.FRANCE);
+		assertEquals("le foo", i18n.tr("FOO"));
+		i18n.setLocale(Locale.CHINA); // No translation - use defaul
+		assertEquals("foo", i18n.tr("FOO"));
+	}
+
+	@Test
+	public void testSettingLocaleAffectsComponentOrientation() {
+		i18n.setLocale(new Locale("en")); // English
+		assertTrue(i18n.getComponentOrientation().isLeftToRight());
+		i18n.setLocale(new Locale("ar")); // Arabic
+		assertFalse(i18n.getComponentOrientation().isLeftToRight());
+	}
+
+	@Test
+	public void testListenersAreInformedOfLocaleChanges() {
+		final Font englishFont = fontManager.getFontForLanguage("en");
+		final Font tibetanFont = fontManager.getFontForLanguage("bo");
+
+		Mockery context = new Mockery();
+		final I18n.Listener listener = context.mock(I18n.Listener.class);
+		context.checking(new Expectations() {{
+			// Listener should be called once when registered
+			oneOf(listener).localeChanged(englishFont);
+			// Listener should be called again when locale changes
+			oneOf(listener).localeChanged(tibetanFont);
+		}});
+
+		i18n.setLocale(new Locale("en"));
+		i18n.addListener(listener);
+		i18n.setLocale(new Locale("bo"));
+		i18n.removeListener(listener);
+
+		context.assertIsSatisfied();
+	}
+
+	@Test
+	public void testSaveAndLoadLocale() throws IOException {
+		testDir.mkdirs();
+		new File(testDir, "Data").mkdir();
+		i18n.setLocale(new Locale("fr"));
+		assertEquals("le foo", i18n.tr("FOO"));
+		i18n.saveLocale();
+		i18n.setLocale(new Locale("zh")); // No translation - use default
+		assertEquals("foo", i18n.tr("FOO"));
+		i18n.loadLocale();
+		assertEquals("le foo", i18n.tr("FOO"));
+	}
+
+	@After
+	public void tearDown() {
+		TestUtils.delete(base);
+		TestUtils.delete(french);
+		TestUtils.deleteTestDirectories();
+	}
+}
diff --git a/test/net/sf/briar/setup/SetupWorkerTest.java b/test/net/sf/briar/setup/SetupWorkerTest.java
index f311b8386928b8c5edc5855d0789099dfaa413b5..c597d9cf94784e1d701ddd4c475cc15cebe2081d 100644
--- a/test/net/sf/briar/setup/SetupWorkerTest.java
+++ b/test/net/sf/briar/setup/SetupWorkerTest.java
@@ -117,7 +117,7 @@ public class SetupWorkerTest extends TestCase {
 			oneOf(callback).extractingFile(jreFoo);
 			oneOf(callback).extractingFile(fooJar);
 			oneOf(callback).extractingFile(fooTtf);
-			oneOf(i18n).saveLocale(new File(testDir, "Briar/Data"));
+			oneOf(i18n).saveLocale(new File(testDir, "Briar"));
 			oneOf(callback).installed(new File(testDir, "Briar"));
 		}});