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