compiler/ecere/i18n: Added i18n context support through $"context"."string" syntax...
[sdk] / ecere / src / sys / i18n.ec
1 import "File"
2 import "Map"
3
4 #if defined(ECERE_BOOTSTRAP) || defined(ECERE_STATIC)
5 #define dllexport
6 #if !defined(ECERE_BOOTSTRAP)
7 #define stdcall
8 #endif
9 #endif
10
11 #ifndef ECERE_BOOTSTRAP
12 #define FileOpen FileOpenBuffered
13 #endif
14
15 static Map<String, Map<String, String>> moduleMaps { };
16
17 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
18                          | (((unsigned int)(dword) & 0x0000ff00) <<  8) \
19                          | (((unsigned int)(dword) & 0x00ff0000) >>  8) \
20                          | (((unsigned int)(dword) & 0xff000000) >> 24))
21
22 public dllexport void LoadTranslatedStrings(Module module, char * name)
23 {
24    File f;
25    char fileName[MAX_LOCATION];
26
27    char lcAll[256];
28    char language[256];
29    char lang[256];
30    char lcMessages[256];
31    char * locale = null;
32
33    if(GetEnvironment("LANGUAGE", language, sizeof(language)))
34       locale = language;
35    else if(GetEnvironment("LC_ALL", lcAll, sizeof(lcAll)))
36       locale = lcAll;
37    else if(GetEnvironment("LC_MESSAGES", lcMessages, sizeof(lcMessages)))
38       locale = lcMessages;
39    else if(GetEnvironment("LANG", lang, sizeof(lang)))
40       locale = lang;
41
42    if(locale)
43    {
44       char * dot;
45       strcpy(language, locale);
46       dot = strstr(language, ".");
47       if(dot) *dot = 0;
48       locale = language;
49    }
50
51    if(module.name)
52       sprintf(fileName, "<:%s>locale/%s/LC_MESSAGES/%s.mo", module.name, locale, name);
53    else
54       sprintf(fileName, ":locale/%s/LC_MESSAGES/%s.mo", locale, name);
55    f = FileOpen(fileName, read);
56    if(!f)
57    {
58       sprintf(fileName, "locale/%s/LC_MESSAGES/%s.mo", locale, name);
59       f = FileOpen(fileName, read);
60    }
61    if(!f)
62    {
63       sprintf(fileName, "/usr/share/locale/%s/LC_MESSAGES/%s.mo", locale, name);
64       f = FileOpen(fileName, read);
65    }
66    if(f)
67    {
68       uint magic = 0;
69       f.Read(&magic, sizeof(uint), 1);
70       if(magic == 0x950412de || magic == 0xde120495)
71       {
72          Map<String, String> textMap;
73          bool swap = magic != 0x950412de;
74          uint revision = 0;
75          uint numStrings = 0;
76          uint origStrings = 0, transStrings = 0;
77          uint hashingSize = 0, hashingOffset = 0;
78          int c;
79          f.Read(&revision, sizeof(uint), 1);       if(swap) SWAP_DWORD(revision);
80          f.Read(&numStrings, sizeof(uint), 1);     if(swap) SWAP_DWORD(numStrings);
81          f.Read(&origStrings, sizeof(uint), 1);    if(swap) SWAP_DWORD(origStrings);
82          f.Read(&transStrings, sizeof(uint), 1);   if(swap) SWAP_DWORD(transStrings);
83          f.Read(&hashingSize, sizeof(uint), 1);    if(swap) SWAP_DWORD(hashingSize);
84          f.Read(&hashingOffset, sizeof(uint), 1);  if(swap) SWAP_DWORD(hashingOffset);
85          
86          if(!moduleMaps)
87             moduleMaps = { };
88          {
89             MapIterator<String, Map<String, String>> it { map = moduleMaps };
90             if(it.Index(module.name, false))
91                delete it.data;
92             // TOFIX: delete moduleMaps[module];
93          }
94          moduleMaps[module.name] = textMap = { };
95          for(c = 0; c < numStrings; c++)
96          {
97             uint len = 0, offset = 0;
98             char * original = null, * translated = null;
99
100             f.Seek(origStrings + c*2*sizeof(uint), start);
101             f.Read(&len, sizeof(uint), 1);      if(swap)SWAP_DWORD(len);
102             f.Read(&offset, sizeof(uint), 1);   if(swap)SWAP_DWORD(offset);
103             f.Seek(offset, start);
104
105             original = new byte[len + 1];
106             f.Read(original, 1, len + 1);
107
108             f.Seek(transStrings + c*2*sizeof(uint), start);
109             f.Read(&len, sizeof(uint), 1);      if(swap)SWAP_DWORD(len);
110             f.Read(&offset, sizeof(uint), 1);   if(swap)SWAP_DWORD(offset);
111             f.Seek(offset, start);
112
113             translated = new byte[len + 1];
114             f.Read(translated, 1, len + 1);
115
116             if(len)
117             {
118                MapIterator<String, String> it { map = textMap };
119                // TOFIX: Memory leak if the add fails
120                if(it.Index(original, false))
121                   delete translated;
122                else
123                   textMap[original] = translated;
124             }
125             else
126                delete translated;
127             delete original;
128          }
129       }
130       else
131       {
132          // Failure
133          printf("Invalid format while loading %s\n", fileName);
134       }
135       delete f;
136    }
137 }
138
139 public dllexport void UnloadTranslatedStrings(Module module)
140 {
141    MapIterator<String, Map<String, String>> it { map = moduleMaps };
142    if(it.Index(module.name, false))
143    {
144       it.data.Free();
145       moduleMaps.Delete(it.pointer);
146    }
147 }
148
149 public dllexport char * GetTranslatedString(Module module, char * string, char * stringAndContext)
150 {
151    Map<String, String> textMap = moduleMaps ? moduleMaps[module.name] : null;
152    char * result = textMap ? textMap[stringAndContext ? stringAndContext : string] : string;
153    return (result && result[0]) ? result : string;
154 }