diff mde/lookup/Translation.d @ 107:20f7d813bb0f

Translation: now has a file for each locale, instead of a file for each section. Items updated; all strings translated. New unittest for Translation; I realised the old one wasn't run anymore. Changed unittest data and conf dirs.
author Diggory Hardy <diggory.hardy@gmail.com>
date Sun, 30 Nov 2008 17:17:56 +0000
parents 42e241e7be3e
children 6acd96f8685f
line wrap: on
line diff
--- a/mde/lookup/Translation.d	Sat Nov 29 16:43:44 2008 +0000
+++ b/mde/lookup/Translation.d	Sun Nov 30 17:17:56 2008 +0000
@@ -56,8 +56,67 @@
 */
 class Translation : IDataSection
 {
-    final char[] name;      /// The module/package/... which the instance is for
-    final char[] L10n;      /// The localization loaded (e.g. en-GB)
+    /** Load the translation for the requested module/package/...
+    *
+    * Options (mde.options) must have been loaded before this is run.
+    *
+    * Params:
+    *   name The module/package/... to load strings for.
+    *
+    * Throws:
+    * If no localization exists for this name and the current locale (or any locale to be chain-
+    * loaded), or an error occurs while loading the database, a L10nLoadException will be thrown.
+    */
+    static {
+	/** Get Translation instance for _section section.
+	 *
+	 * On the first call, loads all Translation sections.
+	 * 
+	 * These are loaded from data/L10n/locale.mtt where locale is the current locale, as well
+	 * as any files named for locales specified in the header tag <char[][]|depends=[...]>.
+	 *
+	 * On errors, a message is logged and the function continues, likely resulting in some
+	 * symbol names being untranslated. */
+	Translation get (char[] section) {
+	    if (sections is null) {
+		char[][] files = [miscOpts.L10n()];	// initial locale plus dependencies
+		size_t read = 0;	// index in files of next file to read
+		while (read < files.length) {
+		    try {
+			IReader reader = dataDir.makeMTReader ("L10n/"~files[read++], PRIORITY.HIGH_LOW, null, true);
+			assert (reader.dataset.header);
+			auto dp = "depends" in reader.dataset.header._charAA;
+			if (dp) {	// append, making sure no duplicates are inserted
+			    f1: foreach (dep; *dp) {
+				foreach (f; files)
+				    if (f == dep)
+					continue f1;
+				files ~= dep;
+			    }
+			}
+			reader.dataSecCreator = delegate IDataSection(ID sec) {
+			    auto p = sec in sections;
+			    if (p) return *p;
+			    return new Translation (sec);
+			};
+			reader.read;
+		    } catch (MTException e) {
+			logger.error ("Mergetag exception occurred:");
+			logger.error (e.msg);
+		    }
+		}
+	    }
+	    auto p = section in sections;
+	    if (p is null) {
+		logger.warn ("Section "~section ~ " not found in files; strings will not be translated.");
+		return new Translation (section);
+	    }
+	    return *p;
+	}
+	Translation[char[]] sections;
+    }
+    
+    final char[] section;	// ONLY used to log an error message
     
     alias entry opCall;	    /// Convenience alias
     
@@ -96,60 +155,6 @@
         }
     }
     
-    /** Load the translation for the requested module/package/...
-    *
-    * Options (mde.options) must have been loaded before this is run.
-    *
-    * Params:
-    *   name The module/package/... to load strings for.
-    *
-    * Throws:
-    * If no localization exists for this name and the current locale (or any locale to be chain-
-    * loaded), or an error occurs while loading the database, a L10nLoadException will be thrown.
-    */
-    static Translation load (char[] name)
-    {
-        bool[ID] loadedSecs;        // set of all locales/sections loaded; used to prevent circular loading
-        ID[] secsToLoad             // locales/sections to load (dependancies may be added)
-        = [cast(ID) miscOpts.L10n];  // start by loading the current locale
-        
-        Translation transl = new Translation (name, miscOpts.L10n());
-        
-        IReader reader;
-        try {
-            reader = dataDir.makeMTReader (name, PRIORITY.HIGH_LOW);
-            /* Note: we don't want to load every translation section depended on to its own class
-            * instance, since we want to merge them. So make every mergetag section use the same
-            * instance. */
-            reader.dataSecCreator = delegate IDataSection(ID) {
-                return transl;
-            };
-        
-            while (secsToLoad.length) {                 // while we have sections left to load
-                ID sec = secsToLoad[0];                 // take current section
-                secsToLoad = secsToLoad[1..$];          // and remove from list
-                
-                if (sec in loadedSecs) continue;        // skip if it's already been loaded
-                loadedSecs[sec] = true;
-                
-                reader.read ([sec]);                    // Do the actual loading
-                
-                // Add dependancies to front of list:
-                secsToLoad = transl.depends ~ secsToLoad;
-            }
-            // When loop finishes, we're done loading.
-        } catch (MTException e) {
-            // If an error occurs, log a message but continue (strings will just be untranslated)
-            logger.error ("Mergetag exception occurred:");
-            logger.error (e.msg);
-        }
-        
-        delete transl.depends;      // Free memory
-        transl.depends = [];
-        
-        return transl;
-    }
-    
     static this() {
         logger = Log.getLogger ("mde.lookup.Translation");
     }
@@ -172,7 +177,7 @@
             
             Entry entry = deserialize!(Entry) (dt);
             if (entry.name is null) {   // This tag is invalid; ignore it
-                logger.error ("For name "~name~", L10n "~L10n~": tag with ID "~cast(char[])id~" has no data");
+                logger.error ("In L10n/*.mtt section "~section~": tag with ID "~cast(char[])id~" has no data");
                 return;
             }
             entries[cast(char[]) id] = entry;
@@ -194,12 +199,10 @@
     }
     
 private:
-    /* Sets name and L10n.
-    *
-    * Also ensures only load() can create instances. */
-    this (char[] n, char[] l) {
-        name = n;
-        L10n = l;
+    /* Sets name and ensures only Translation's static functions can create instances. */
+    this (char[] n) {
+        section = n;
+	sections[n] = this;
     }
     
     //BEGIN Data
@@ -211,42 +214,35 @@
     //END Data
     
     debug (mdeUnitTest) unittest {
-        /* Relies on file: conf/L10n/i18nUnitTest.mtt
-        * Contents:
-        *********
-        {MT01}
-        {test-1}
-        <entry|Str1=["Test 1"]>
-        <char[][]|depends=["test-2"]>
-        {test-2}
-        <entry|Str1=["Test 2"]>
-        <entry|Str2=["Test 3","Description",bogus,"entries",56]>
-        *********/
+        // Relies on files in unittest/data/L10n: locale-x.mtt for x in 1..4
         
-        // Hack a specific locale...
-        // Also to allow unittest to run without init.
-        TextContent realL10n = miscOpts.L10n;
-        miscOpts.L10n = new TextContent ("L10n", "test-1");
+        // Hack a specific locale. Also to allow unittest to run without init.
+        StringContent realL10n = miscOpts.L10n;
+        miscOpts.L10n = new StringContent ("L10n", "locale-1");
         
-        Translation transl = load ("unittest/Translation");
-        
-        // Simple get-string, check dependancy's entry doesn't override
-        assert (transl.entry ("Str1") == "Test 1");
-        
-        // Entry included from dependancy with description
-        char[] desc;
-        assert (transl.entry ("Str2", desc) == "Test 3");
-        assert (desc == "Description");
-        
-        // No entry: fallback to identifier string
-        assert (transl.entry ("Str3") == "Str3");
-        
-        // No checks for version info since it's not functionality of this module.
-        // Only check extra entries are allowed but ignored.
-        
+	// Struct tests
+	Translation t = get ("section-2");
+	Entry e = t.getStruct ("entry-1");
+	assert (e.name == "Test 1");
+	assert (e.desc == "Description");
+	e = t.getStruct ("entry-2");
+        assert (e.name == "Test 2");
+	assert (e.desc is null);
+	
+	// Dependency tests. Priority should be 1,2,3,4 (entries should come from first file in list that they appear in).
+	t = get ("section-1");
+	char[] d = "1";
+	assert (t.entry ("file-1", d) == "locale-1");
+	assert (d is null);
+	assert (t.entry ("file-2", d) == "locale-2");
+	assert (d == "desc2");
+	assert (t.entry ("file-3") == "locale-3");
+	assert (t.entry ("file-4") == "locale-4");
+	
         // Restore
 	delete miscOpts.L10n;
         miscOpts.L10n = realL10n;
+	sections = null;
         
         logger.info ("Unittest complete.");
     }