Changeset 107:20f7d813bb0f

Show
Ignore:
Timestamp:
11/30/08 12:17:56 (1 month ago)
Author:
Diggory Hardy <diggory.hardy@gmail.com>
branch:
default
Message:

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.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • data/conf/gui.mtt

    r105 r107  
    66<WidgetData|square={0:[0x1,6,6]}> 
    77<WidgetData|blank={0:[0x2]}> 
    8 <WidgetData|opts={0:[0x2031,0x6030,0],1:["Options","optCls"]}> 
    9 <WidgetData|optCls={0:[0xC100,0,2,1],1:["optName","optVars"]}> 
     8<WidgetData|opts={0:[0x2031,0xC100,0,2,1],1:["Options","optName","optSecs"]}> 
     9<WidgetData|optSecs={0:[0x6030,0],1:["optSec"]}> 
     10<WidgetData|optSec={0:[0xC100,0,2,1],1:["optName","optVars"]}> 
    1011<WidgetData|optVars={0:[0x6030,0],1:["optDBox"]}> 
    1112<WidgetData|optDBox={0:[0xC100,1,2,1],1:["optBox","optDesc"]}> 
     
    1516<WidgetData|optVal={0:[0x6030]}> 
    1617<WidgetData|optSep={0:[0x21, 0xff],1:["="]}> 
    17 <WidgetData|floating={0:[0x8200,6,14,6],1:["L10n","blank","opts"]}> 
    18 <WidgetData|L10n={0:[0x2031,0xC100,0,2,1],1:["Options.MiscOptions.L10n","optBox2","apply"]}> 
    19 <WidgetData|optBox2={0:[0xC100,0,1,3],1:["optName","optSep","optVal"]}>!{cloned to avoid bug #4} 
    20 <WidgetData|apply={0:[0x2031,0x4043],1:["quit"]}> 
     18<WidgetData|floating={0:[0x8200,6,14,6],1:["quit","blank","opts"]}> 
     19<WidgetData|quit={0:[0x2031,0x4043],1:["imde.quit"]}> 
    2120{Basic} 
    2221<WidgetData|root={0:[0x21,0x90D970],1:["A string!"]}> 
  • mde/content/Items.d

    r105 r107  
    5252            } 
    5353            Options.allContentList = new ContentList (h, list); 
    54             Options.allContentList.name ("Options");    //FIXME 
     54            Translation trl = Translation.get (h); 
     55            Translation.Entry trle = trl.getStruct (h); 
     56            Options.allContentList.name (trle.name, trle.desc); 
    5557        } 
    5658        return Options.allContentList; 
     
    6971            return *q; 
    7072        } 
    71     } else if (h == "quit" && item is null) { 
    72         quit.name ("Quit"); //FIXME 
    73         return quit; 
     73    } else if (h == "imde") { 
     74        if (!imdeTransl) { 
     75        Translation trl = Translation.get (h); 
     76        Translation.Entry trle = trl.getStruct ("quit"); 
     77        quit.name (trle.name, trle.desc); 
     78        imdeTransl = true; 
     79        } 
     80        h = head (item); 
     81        if (h == "quit" && item is null) { 
     82        quit.name ("Quit"); //FIXME 
     83        return quit; 
     84        } 
    7485    } 
    7586    throw new ContentItemException (h); 
     
    93104    void loadTransl (Options p, char[] n) { 
    94105    debug logger.trace ("Loading translation strings for Options."~n); 
    95     Translation trans = Translation.load ("L10n/"~n); 
     106    Translation trans = Translation.get (n); 
    96107    Translation.Entry transled = trans.getStruct (n); 
    97108    p.contentList = new ContentList (n, p.content); 
     
    102113    } 
    103114    } 
     115     
     116    bool imdeTransl = false;    // Has section imde been translated? 
  • mde/input/Input.d

    r99 r107  
    587587    debug (mdeUnitTest) unittest { 
    588588        Input ut = new Input(); 
    589         ut.loadConfig ("unittest/InputConfig"); 
    590              
     589        ut.loadConfig ("InputConfig"); 
     590     
    591591        int[6] counters;    // counters for callbacks 
    592592        // Should become: [2,2,0,2,1,1] 
    593              
     593     
    594594        //BEGIN Set up some callbacks 
    595595        ut.addButtonCallback (0x03F0, delegate void(inputID id, bool status) { 
  • mde/lookup/Translation.d

    r103 r107  
    5757class Translation : IDataSection 
    5858{ 
    59     final char[] name;      /// The module/package/... which the instance is for 
    60     final char[] L10n;      /// The localization loaded (e.g. en-GB) 
     59    /** Load the translation for the requested module/package/... 
     60    * 
     61    * Options (mde.options) must have been loaded before this is run. 
     62    * 
     63    * Params: 
     64    *   name The module/package/... to load strings for. 
     65    * 
     66    * Throws: 
     67    * If no localization exists for this name and the current locale (or any locale to be chain- 
     68    * loaded), or an error occurs while loading the database, a L10nLoadException will be thrown. 
     69    */ 
     70    static { 
     71    /** Get Translation instance for _section section. 
     72     * 
     73     * On the first call, loads all Translation sections. 
     74     *  
     75     * These are loaded from data/L10n/locale.mtt where locale is the current locale, as well 
     76     * as any files named for locales specified in the header tag <char[][]|depends=[...]>. 
     77     * 
     78     * On errors, a message is logged and the function continues, likely resulting in some 
     79     * symbol names being untranslated. */ 
     80    Translation get (char[] section) { 
     81        if (sections is null) { 
     82        char[][] files = [miscOpts.L10n()]; // initial locale plus dependencies 
     83        size_t read = 0;    // index in files of next file to read 
     84        while (read < files.length) { 
     85            try { 
     86            IReader reader = dataDir.makeMTReader ("L10n/"~files[read++], PRIORITY.HIGH_LOW, null, true); 
     87            assert (reader.dataset.header); 
     88            auto dp = "depends" in reader.dataset.header._charAA; 
     89            if (dp) {   // append, making sure no duplicates are inserted 
     90                f1: foreach (dep; *dp) { 
     91                foreach (f; files) 
     92                    if (f == dep) 
     93                    continue f1; 
     94                files ~= dep; 
     95                } 
     96            } 
     97            reader.dataSecCreator = delegate IDataSection(ID sec) { 
     98                auto p = sec in sections; 
     99                if (p) return *p; 
     100                return new Translation (sec); 
     101            }; 
     102            reader.read; 
     103            } catch (MTException e) { 
     104            logger.error ("Mergetag exception occurred:"); 
     105            logger.error (e.msg); 
     106            } 
     107        } 
     108        } 
     109        auto p = section in sections; 
     110        if (p is null) { 
     111        logger.warn ("Section "~section ~ " not found in files; strings will not be translated."); 
     112        return new Translation (section); 
     113        } 
     114        return *p; 
     115    } 
     116    Translation[char[]] sections; 
     117    } 
     118     
     119    final char[] section;   // ONLY used to log an error message 
    61120     
    62121    alias entry opCall;     /// Convenience alias 
     
    95154            return ret; 
    96155        } 
    97     } 
    98      
    99     /** Load the translation for the requested module/package/... 
    100     * 
    101     * Options (mde.options) must have been loaded before this is run. 
    102     * 
    103     * Params: 
    104     *   name The module/package/... to load strings for. 
    105     * 
    106     * Throws: 
    107     * If no localization exists for this name and the current locale (or any locale to be chain- 
    108     * loaded), or an error occurs while loading the database, a L10nLoadException will be thrown. 
    109     */ 
    110     static Translation load (char[] name) 
    111     { 
    112         bool[ID] loadedSecs;        // set of all locales/sections loaded; used to prevent circular loading 
    113         ID[] secsToLoad             // locales/sections to load (dependancies may be added) 
    114         = [cast(ID) miscOpts.L10n];  // start by loading the current locale 
    115          
    116         Translation transl = new Translation (name, miscOpts.L10n()); 
    117          
    118         IReader reader; 
    119         try { 
    120             reader = dataDir.makeMTReader (name, PRIORITY.HIGH_LOW); 
    121             /* Note: we don't want to load every translation section depended on to its own class 
    122             * instance, since we want to merge them. So make every mergetag section use the same 
    123             * instance. */ 
    124             reader.dataSecCreator = delegate IDataSection(ID) { 
    125                 return transl; 
    126             }; 
    127          
    128             while (secsToLoad.length) {                 // while we have sections left to load 
    129                 ID sec = secsToLoad[0];                 // take current section 
    130                 secsToLoad = secsToLoad[1..$];          // and remove from list 
    131                  
    132                 if (sec in loadedSecs) continue;        // skip if it's already been loaded 
    133                 loadedSecs[sec] = true; 
    134                  
    135                 reader.read ([sec]);                    // Do the actual loading 
    136                  
    137                 // Add dependancies to front of list: 
    138                 secsToLoad = transl.depends ~ secsToLoad; 
    139             } 
    140             // When loop finishes, we're done loading. 
    141         } catch (MTException e) { 
    142             // If an error occurs, log a message but continue (strings will just be untranslated) 
    143             logger.error ("Mergetag exception occurred:"); 
    144             logger.error (e.msg); 
    145         } 
    146          
    147         delete transl.depends;      // Free memory 
    148         transl.depends = []; 
    149          
    150         return transl; 
    151156    } 
    152157     
     
    173178            Entry entry = deserialize!(Entry) (dt); 
    174179            if (entry.name is null) {   // This tag is invalid; ignore it 
    175                 logger.error ("For name "~name~", L10n "~L10n~": tag with ID "~cast(char[])id~" has no data"); 
     180                logger.error ("In L10n/*.mtt section "~section~": tag with ID "~cast(char[])id~" has no data"); 
    176181                return; 
    177182            } 
     
    195200     
    196201private: 
    197     /* Sets name and L10n. 
    198     * 
    199     * Also ensures only load() can create instances. */ 
    200     this (char[] n, char[] l) { 
    201         name = n; 
    202         L10n = l; 
     202    /* Sets name and ensures only Translation's static functions can create instances. */ 
     203    this (char[] n) { 
     204        section = n; 
     205    sections[n] = this; 
    203206    } 
    204207     
     
    212215     
    213216    debug (mdeUnitTest) unittest { 
    214         /* Relies on file: conf/L10n/i18nUnitTest.mtt 
    215         * Contents: 
    216         ********* 
    217         {MT01} 
    218         {test-1} 
    219         <entry|Str1=["Test 1"]> 
    220         <char[][]|depends=["test-2"]> 
    221         {test-2} 
    222         <entry|Str1=["Test 2"]> 
    223         <entry|Str2=["Test 3","Description",bogus,"entries",56]> 
    224         *********/ 
     217        // Relies on files in unittest/data/L10n: locale-x.mtt for x in 1..4 
    225218         
    226         // Hack a specific locale... 
    227         // Also to allow unittest to run without init. 
    228         TextContent realL10n = miscOpts.L10n; 
    229         miscOpts.L10n = new TextContent ("L10n", "test-1"); 
     219        // Hack a specific locale. Also to allow unittest to run without init. 
     220        StringContent realL10n = miscOpts.L10n; 
     221        miscOpts.L10n = new StringContent ("L10n", "locale-1"); 
    230222         
    231         Translation transl = load ("unittest/Translation"); 
    232          
    233         // Simple get-string, check dependancy's entry doesn't override 
    234         assert (transl.entry ("Str1") == "Test 1"); 
    235          
    236         // Entry included from dependancy with description 
    237         char[] desc; 
    238         assert (transl.entry ("Str2", desc) == "Test 3"); 
    239         assert (desc == "Description"); 
    240          
    241         // No entry: fallback to identifier string 
    242         assert (transl.entry ("Str3") == "Str3"); 
    243          
    244         // No checks for version info since it's not functionality of this module. 
    245         // Only check extra entries are allowed but ignored. 
    246          
     223    // Struct tests 
     224    Translation t = get ("section-2"); 
     225    Entry e = t.getStruct ("entry-1"); 
     226    assert (e.name == "Test 1"); 
     227    assert (e.desc == "Description"); 
     228    e = t.getStruct ("entry-2"); 
     229        assert (e.name == "Test 2"); 
     230    assert (e.desc is null); 
     231     
     232    // Dependency tests. Priority should be 1,2,3,4 (entries should come from first file in list that they appear in). 
     233    t = get ("section-1"); 
     234    char[] d = "1"; 
     235    assert (t.entry ("file-1", d) == "locale-1"); 
     236    assert (d is null); 
     237    assert (t.entry ("file-2", d) == "locale-2"); 
     238    assert (d == "desc2"); 
     239    assert (t.entry ("file-3") == "locale-3"); 
     240    assert (t.entry ("file-4") == "locale-4"); 
     241     
    247242        // Restore 
    248243    delete miscOpts.L10n; 
    249244        miscOpts.L10n = realL10n; 
     245    sections = null; 
    250246         
    251247        logger.info ("Unittest complete."); 
  • mde/mde.d

    r103 r107  
    3434    import mde.file.ssi; 
    3535    import mde.file.mergetag.mdeUT; 
     36    import mde.lookup.Translation; 
    3637} 
    3738 
  • mde/setup/paths.d

    r106 r107  
    140140        FilePath[] ret; 
    141141         
    142         debug (mdeUnitTest) { 
    143         /* Alternate approach - just try from current directory. 
    144         * Really just for unittests, but with the if condition it shouldn't break anything else. */ 
    145             if (pathsLen == 0) { 
    146                 FilePath file = findFile (filename); 
    147                 if (file !is null) 
    148                     ret ~= file; 
    149                 return ret; 
    150             } 
    151         } 
    152          
    153142        if (readOrder == PRIORITY.LOW_HIGH) { 
    154143            for (size_t i = 0; i < pathsLen; ++i) { 
     
    173162    // Unconditionally add a path 
    174163    void addPath (char[] path) { 
     164    assert (pathsLen < MAX_PATHS); 
    175165        paths[pathsLen++] = path~'/'; 
    176166    } 
     
    178168    // Test a path and add if is a folder. 
    179169    bool tryPath (char[] path, bool create = false) { 
    180         FilePath fp = FilePath (path); 
     170    assert (pathsLen < MAX_PATHS); 
     171    FilePath fp = FilePath (path); 
    181172        if (fp.exists && fp.isFolder) { 
    182173            paths[pathsLen++] = path~'/'; 
     
    214205 
    215206//BEGIN Path resolution 
    216 // These are used several times: 
    217 const DATA = "/data"; 
    218 const CONF = "/conf"; 
    219  
    220 /** Find at least one path for each required directory. 
    221 
    222 * Note: the logger cannot be used yet, so only output is exception messages. */ 
     207/** Find at least one path for each required directory. */ 
     208debug (mdeUnitTest) { 
     209    /** In unittest mode, add paths unittest/data and unittest/conf before init runs. */ 
     210    static this () { 
     211    dataDir.tryPath ("unittest/data"); 
     212    confDir.tryPath ("unittest/conf"); 
     213    if (!(dataDir.pathsLen && confDir.pathsLen)) 
     214        throw new mdeException ("Fatal: unittest/data and unittest/conf don't both exist"); 
     215    // Don't bother with real paths or logDir or font dir(s) for unittest. 
     216    } 
     217
    223218version (linux) { 
    224219    SortedMap!(char[],char[]) fontFiles;    // key is file name, value is CString path 
     
    247242        // Static data paths: 
    248243        dataDir.addPath (staticPath.toString);      // we know this is valid anyway 
    249         dataDir.tryPath (userPath.toString ~ DATA); 
     244        dataDir.tryPath (userPath.toString ~ "/data"); 
    250245        if (extraDataPath) dataDir.tryPath (extraDataPath); 
    251246        if (!dataDir.pathsLen) throw new mdeException ("Fatal: no data path found!"); 
    252247         
    253248        // Configuration paths: 
    254         confDir.tryPath (staticPath.toString ~ CONF); 
     249        confDir.tryPath (staticPath.toString ~ "/conf"); 
    255250        confDir.tryPath ("/etc/mde"); 
    256         confDir.tryPath (userPath.toString ~ CONF, true); 
     251        confDir.tryPath (userPath.toString ~ "/conf", true); 
    257252        if (extraConfPath) confDir.tryPath (extraConfPath); 
    258253        if (!confDir.pathsLen) throw new mdeException ("Fatal: no conf path found!"); 
     
    294289        // Static data paths: 
    295290        dataDir.addPath (staticPath.toString);   // we know this is valid anyway 
    296         dataDir.tryPath (userPath.toString ~ DATA); 
     291   dataDir.tryPath (userPath.toString ~ "/data"); 
    297292        if (extraDataPath) dataDir.tryPath (extraDataPath); 
    298293        if (!dataDir.pathsLen) throw new mdeException ("Fatal: no data path found!"); 
    299294         
    300295        // Configuration paths: 
    301         confDir.tryPath (staticPath.toString ~ CONF); 
     296        confDir.tryPath (staticPath.toString ~ "/conf"); 
    302297        confDir.tryPath (installPath.append("user").toString); 
    303         confDir.tryPath (userPath.toString ~ CONF, true); 
     298        confDir.tryPath (userPath.toString ~ "/conf", true); 
    304299        if (extraConfPath) confDir.tryPath (extraConfPath); 
    305300        if (!confDir.pathsLen) throw new mdeException ("Fatal: no conf path found!"); 
     
    325320    } 
    326321     
    327 // The maximum number of paths for any one "directory". 
    328 // There are NO CHECKS that this is not exceeded. 
     322    // The maximum number of paths for any one "directory". 
    329323    const MAX_PATHS = 4; 
    330324