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