i18n: (#858) Fixed directory layout to really match what Launchpad expects
[sdk] / ecere / src / sys / i18n.ec
1 #ifndef ECERE_NOFILE
2 import "File"
3 #endif
4 import "Map"
5
6 #if defined(ECERE_BOOTSTRAP) || defined(ECERE_STATIC)
7 #define dllexport
8 #if !defined(ECERE_BOOTSTRAP)
9 #define stdcall
10 #endif
11 #endif
12
13 #ifndef ECERE_BOOTSTRAP
14 #define FileOpen FileOpenBuffered
15 #endif
16
17 static Map<String, Map<String, String>> moduleMaps { };
18
19 #define SWAP_DWORD(dword) ((((unsigned int)(dword) & 0x000000ff) << 24) \
20                          | (((unsigned int)(dword) & 0x0000ff00) <<  8) \
21                          | (((unsigned int)(dword) & 0x00ff0000) >>  8) \
22                          | (((unsigned int)(dword) & 0xff000000) >> 24))
23
24 public dllexport void LoadTranslatedStrings(Module module, char * name)
25 {
26 #ifndef ECERE_NOFILE
27    File f;
28    char fileName[MAX_LOCATION];
29
30    char lcAll[256];
31    char language[256];
32    char lang[256];
33    char lcMessages[256];
34    char * locale = null;
35    char genericLocale[256];
36
37    genericLocale[0] = 0;
38
39    if(GetEnvironment("LANGUAGE", language, sizeof(language)))
40       locale = language;
41    else if(GetEnvironment("LC_ALL", lcAll, sizeof(lcAll)))
42       locale = lcAll;
43    else if(GetEnvironment("LC_MESSAGES", lcMessages, sizeof(lcMessages)))
44       locale = lcMessages;
45    else if(GetEnvironment("LANG", lang, sizeof(lang)))
46       locale = lang;
47
48    if(locale)
49    {
50       char * dot;
51       if(language != locale)
52          strcpy(language, locale);
53       dot = strstr(language, ".");
54       if(dot) *dot = 0;
55       locale = language;
56    }
57    if(locale)
58    {
59       char * under;
60       strcpy(genericLocale, locale);
61       under = strchr(genericLocale, '_');
62       if(under)
63          *under = 0;
64    }
65
66    if(module.name)
67       sprintf(fileName, "<:%s>locale/%s/%s.mo", module.name, name, locale);
68    else
69       sprintf(fileName, ":locale/%s/%s.mo", name, locale);
70    f = FileOpen(fileName, read);
71    if(!f)
72    {
73       if(module.name)
74          sprintf(fileName, "<:%s>locale/%s/LC_MESSAGES/%s.mo", module.name, locale, name);
75       else
76          sprintf(fileName, ":locale/%s/LC_MESSAGES/%s.mo", locale, name);
77       f = FileOpen(fileName, read);
78    }
79    if(!f)
80    {
81       sprintf(fileName, "locale/%s/LC_MESSAGES/%s.mo", locale, name);
82       f = FileOpen(fileName, read);
83    }
84    if(!f)
85    {
86       sprintf(fileName, "/usr/share/locale/%s/LC_MESSAGES/%s.mo", locale, name);
87       f = FileOpen(fileName, read);
88    }
89
90    if(!f && locale && strcmpi(locale, genericLocale))
91    {
92       // Attempt with generic language
93       if(module.name)
94          sprintf(fileName, "<:%s>locale/%s/%s.mo", module.name, name, genericLocale);
95       else
96          sprintf(fileName, ":locale/%s/%s.mo", name, genericLocale);
97       f = FileOpen(fileName, read);
98       if(!f)
99       {
100          if(module.name)
101             sprintf(fileName, "<:%s>locale/%s/LC_MESSAGES/%s.mo", module.name, genericLocale, name);
102          else
103             sprintf(fileName, ":locale/%s/LC_MESSAGES/%s.mo", genericLocale, name);
104          f = FileOpen(fileName, read);
105       }
106       if(!f)
107       {
108          sprintf(fileName, "locale/%s/LC_MESSAGES/%s.mo", genericLocale, name);
109          f = FileOpen(fileName, read);
110       }
111       if(!f)
112       {
113          sprintf(fileName, "/usr/share/locale/%s/LC_MESSAGES/%s.mo", genericLocale, name);
114          f = FileOpen(fileName, read);
115       }
116    }
117
118    if(f)
119    {
120       uint magic = 0;
121       f.Read(&magic, sizeof(uint), 1);
122       if(magic == 0x950412de || magic == 0xde120495)
123       {
124          Map<String, String> textMap;
125          bool swap = magic != 0x950412de;
126          uint revision = 0;
127          uint numStrings = 0;
128          uint origStrings = 0, transStrings = 0;
129          uint hashingSize = 0, hashingOffset = 0;
130          int c;
131          f.Read(&revision, sizeof(uint), 1);       if(swap) SWAP_DWORD(revision);
132          f.Read(&numStrings, sizeof(uint), 1);     if(swap) SWAP_DWORD(numStrings);
133          f.Read(&origStrings, sizeof(uint), 1);    if(swap) SWAP_DWORD(origStrings);
134          f.Read(&transStrings, sizeof(uint), 1);   if(swap) SWAP_DWORD(transStrings);
135          f.Read(&hashingSize, sizeof(uint), 1);    if(swap) SWAP_DWORD(hashingSize);
136          f.Read(&hashingOffset, sizeof(uint), 1);  if(swap) SWAP_DWORD(hashingOffset);
137
138          if(!moduleMaps)
139             moduleMaps = { };
140          {
141             MapIterator<String, Map<String, String>> it { map = moduleMaps };
142             if(it.Index(module.name, false))
143                delete it.data;
144             // TOFIX: delete moduleMaps[module];
145          }
146          moduleMaps[module.name] = textMap = { };
147          for(c = 0; c < numStrings; c++)
148          {
149             uint len = 0, offset = 0;
150             char * original = null, * translated = null;
151
152             f.Seek(origStrings + c*2*sizeof(uint), start);
153             f.Read(&len, sizeof(uint), 1);      if(swap)SWAP_DWORD(len);
154             f.Read(&offset, sizeof(uint), 1);   if(swap)SWAP_DWORD(offset);
155             f.Seek(offset, start);
156
157             original = new byte[len + 1];
158             f.Read(original, 1, len + 1);
159
160             f.Seek(transStrings + c*2*sizeof(uint), start);
161             f.Read(&len, sizeof(uint), 1);      if(swap)SWAP_DWORD(len);
162             f.Read(&offset, sizeof(uint), 1);   if(swap)SWAP_DWORD(offset);
163             f.Seek(offset, start);
164
165             translated = new byte[len + 1];
166             f.Read(translated, 1, len + 1);
167
168             if(len)
169             {
170                MapIterator<String, String> it { map = textMap };
171                // TOFIX: Memory leak if the add fails
172                if(it.Index(original, false))
173                   delete translated;
174                else
175                   textMap[original] = translated;
176             }
177             else
178                delete translated;
179             delete original;
180          }
181       }
182       else
183       {
184          // Failure
185          printf("Invalid format while loading %s\n", fileName);
186       }
187       delete f;
188    }
189 #endif
190 }
191
192 public dllexport void UnloadTranslatedStrings(Module module)
193 {
194    MapIterator<String, Map<String, String>> it { map = moduleMaps };
195    if(it.Index(module.name, false))
196    {
197       it.data.Free();
198       moduleMaps.Delete(it.pointer);
199    }
200 }
201
202 public dllexport char * GetTranslatedString(Module module, char * string, char * stringAndContext)
203 {
204    Map<String, String> textMap = moduleMaps ? moduleMaps[module.name] : null;
205    char * result = textMap ? textMap[stringAndContext ? stringAndContext : string] : string;
206    return (result && result[0]) ? result : string;
207 }