gettext.pp 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 1999-2001 by the Free Pascal development team
  4. Gettext interface to resourcestrings.
  5. See the file COPYING.FPC, included in this distribution,
  6. for details about the copyright.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  10. **********************************************************************}
  11. {$MODE objfpc}
  12. {$H+}
  13. unit gettext;
  14. interface
  15. uses SysUtils, Classes;
  16. const
  17. MOFileHeaderMagic = $950412de;
  18. type
  19. TMOFileHeader = packed record
  20. magic: LongWord; // MOFileHeaderMagic
  21. revision: LongWord; // 0
  22. nstrings: LongWord; // Number of string pairs
  23. OrigTabOffset: LongWord; // Offset of original string offset table
  24. TransTabOffset: LongWord; // Offset of translated string offset table
  25. HashTabSize: LongWord; // Size of hashing table
  26. HashTabOffset: LongWord; // Offset of first hashing table entry
  27. end;
  28. TMOStringInfo = packed record
  29. length: LongWord;
  30. offset: LongWord;
  31. end;
  32. TMOStringTable = array[0..(1 shl 30) div SizeOf(TMOStringInfo)] of TMOStringInfo;
  33. PMOStringTable = ^TMOStringTable;
  34. TLongWordArray = array[0..(1 shl 30) div SizeOf(LongWord)] of LongWord;
  35. PLongWordArray = ^TLongWordArray;
  36. TPCharArray = array[0..(1 shl 30) div SizeOf(PChar)] of PChar;
  37. PPCharArray = ^TPCharArray;
  38. TMOFile = class
  39. protected
  40. StringCount, HashTableSize: LongWord;
  41. HashTable: PLongWordArray;
  42. OrigTable, TranslTable: PMOStringTable;
  43. OrigStrings, TranslStrings: PPCharArray;
  44. public
  45. constructor Create(const AFilename: String);
  46. constructor Create(AStream: TStream);
  47. destructor Destroy; override;
  48. function Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  49. function Translate(AOrig: String; AHash: LongWord): String;
  50. function Translate(AOrig: String): String;
  51. end;
  52. EMOFileError = class(Exception);
  53. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  54. procedure TranslateResourceStrings(AFile: TMOFile);
  55. procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
  56. procedure TranslateResourceStrings(const AFilename: String);
  57. procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
  58. implementation
  59. uses {$ifdef win32} windows, {$endif}dos;
  60. constructor TMOFile.Create(AStream: TStream);
  61. var
  62. header: TMOFileHeader;
  63. i: Integer;
  64. begin
  65. inherited Create;
  66. AStream.Read(header, Sizeof(header));
  67. if header.magic <> MOFileHeaderMagic then
  68. raise EMOFileError.Create('Invalid magic - not a MO file?');
  69. GetMem(OrigTable, header.nstrings * SizeOf(TMOStringInfo));
  70. GetMem(TranslTable, header.nstrings * SizeOf(TMOStringInfo));
  71. GetMem(OrigStrings, header.nstrings * SizeOf(PChar));
  72. GetMem(TranslStrings, header.nstrings * SizeOf(PChar));
  73. AStream.Position := header.OrigTabOffset;
  74. AStream.Read(OrigTable^, header.nstrings * SizeOf(TMOStringInfo));
  75. AStream.Position := header.TransTabOffset;
  76. AStream.Read(TranslTable^, header.nstrings * SizeOf(TMOStringInfo));
  77. StringCount := header.nstrings;
  78. // Read strings
  79. for i := 0 to StringCount - 1 do
  80. begin
  81. AStream.Position := OrigTable^[i].offset;
  82. { SetLength(s, OrigTable^[i].length);
  83. AStream.Read(s[1], OrigTable^[i].length);
  84. OrigStrings^[i] := StrNew(PChar(s));}
  85. GetMem(OrigStrings^[i], OrigTable^[i].length + 1);
  86. AStream.Read(OrigStrings^[i]^, OrigTable^[i].length);
  87. OrigStrings^[i][OrigTable^[i].length] := #0;
  88. end;
  89. for i := 0 to StringCount - 1 do
  90. begin
  91. AStream.Position := TranslTable^[i].offset;
  92. { SetLength(s, TranslTable^[i].length);
  93. AStream.Read(s[1], TranslTable^[i].length);
  94. TranslStrings^[i] := StrNew(PChar(s));}
  95. GetMem(TranslStrings^[i], TranslTable^[i].length+1);
  96. AStream.Read(TranslStrings^[i]^, TranslTable^[i].length);
  97. TranslStrings^[i][TranslTable^[i].length] := #0;
  98. end;
  99. // Read hashing table
  100. HashTableSize := header.HashTabSize;
  101. GetMem(HashTable, 4 * HashTableSize);
  102. AStream.Position := header.HashTabOffset;
  103. AStream.Read(HashTable^, 4 * HashTableSize);
  104. end;
  105. constructor TMOFile.Create(const AFilename: String);
  106. var
  107. f: TStream;
  108. begin
  109. f := TFileStream.Create(AFilename, fmOpenRead);
  110. try
  111. Self.Create(f);
  112. finally
  113. f.Free;
  114. end;
  115. end;
  116. destructor TMOFile.Destroy;
  117. var
  118. i: Integer;
  119. begin
  120. for i := 0 to StringCount - 1 do
  121. begin
  122. Dispose(OrigStrings^[i]);
  123. Dispose(TranslStrings^[i]);
  124. end;
  125. Dispose(OrigTable);
  126. Dispose(TranslTable);
  127. Dispose(OrigStrings);
  128. Dispose(TranslStrings);
  129. Dispose(HashTable);
  130. inherited Destroy;
  131. end;
  132. function TMOFile.Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  133. var
  134. idx, incr, nstr: LongWord;
  135. begin
  136. idx := AHash mod HashTableSize;
  137. incr := 1 + (AHash mod (HashTableSize - 2));
  138. while True do
  139. begin
  140. nstr := HashTable^[idx];
  141. if nstr = 0 then
  142. begin
  143. Result := '';
  144. exit;
  145. end;
  146. if (OrigTable^[nstr - 1].length = LongWord(ALen)) and
  147. (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then
  148. begin
  149. Result := TranslStrings^[nstr - 1];
  150. exit;
  151. end;
  152. if idx >= HashTableSize - incr then
  153. Dec(idx, HashTableSize - incr)
  154. else
  155. Inc(idx, incr);
  156. end;
  157. end;
  158. function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
  159. begin
  160. Result := Translate(PChar(AOrig), Length(AOrig), AHash);
  161. end;
  162. function TMOFile.Translate(AOrig: String): String;
  163. begin
  164. Result := Translate(AOrig, Hash(AOrig));
  165. end;
  166. // -------------------------------------------------------
  167. // Resourcestring translation procedures
  168. // -------------------------------------------------------
  169. function Translate (Name,Value : AnsiString; Hash : Longint; arg:pointer) : AnsiString;
  170. begin
  171. Result:=TMOFile(arg).Translate(Value,Hash);
  172. end;
  173. procedure TranslateResourceStrings(AFile: TMOFile);
  174. begin
  175. SetResourceStrings(@Translate,AFile);
  176. end;
  177. procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
  178. begin
  179. SetUnitResourceStrings(AUnitName,@Translate,AFile);
  180. end;
  181. {$ifdef win32}
  182. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  183. var
  184. Buffer: array[1..4] of char;
  185. Country: string;
  186. UserLCID: LCID;
  187. begin
  188. //defaults
  189. Lang := '';
  190. FallbackLang:='';
  191. UserLCID := GetUserDefaultLCID;
  192. if GetLocaleInfo(UserLCID, LOCALE_SABBREVLANGNAME, @Buffer[1], 4)<>0 then
  193. FallbackLang := lowercase(copy(Buffer,1,2));
  194. if GetLocaleInfo(UserLCID, LOCALE_SABBREVCTRYNAME, @Buffer[1], 4)<>0 then begin
  195. Country := copy(Buffer,1,2);
  196. // some 2 letter codes are not the first two letters of the 3 letter code
  197. // there are probably more, but first let us see if there are translations
  198. if (Buffer='PRT') then Country:='PT';
  199. Lang := FallbackLang+'_'+Country;
  200. end;
  201. end;
  202. {$else}
  203. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  204. begin
  205. lang := GetEnv('LC_ALL');
  206. if Length(lang) = 0 then
  207. begin
  208. lang := GetEnv('LC_MESSAGES');
  209. if Length(lang) = 0 then
  210. begin
  211. lang := GetEnv('LANG');
  212. if Length(lang) = 0 then
  213. exit; // no language defined via environment variables
  214. end;
  215. end;
  216. FallbackLang := Copy(lang, 1, 2);
  217. end;
  218. {$endif}
  219. procedure TranslateResourceStrings(const AFilename: String);
  220. var
  221. mo: TMOFile;
  222. lang, FallbackLang: String;
  223. begin
  224. GetLanguageIDs(Lang, FallbackLang);
  225. try
  226. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  227. try
  228. TranslateResourceStrings(mo);
  229. finally
  230. mo.Free;
  231. end;
  232. except
  233. on e: Exception do;
  234. end;
  235. lang := Copy(lang, 1, 5);
  236. try
  237. mo := TMOFile.Create(Format(AFilename, [lang]));
  238. try
  239. TranslateResourceStrings(mo);
  240. finally
  241. mo.Free;
  242. end;
  243. except
  244. on e: Exception do;
  245. end;
  246. end;
  247. procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
  248. var
  249. mo: TMOFile;
  250. lang, FallbackLang: String;
  251. begin
  252. GetLanguageIDs(Lang, FallbackLang);
  253. try
  254. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  255. try
  256. TranslateUnitResourceStrings(AUnitName,mo);
  257. finally
  258. mo.Free;
  259. end;
  260. except
  261. on e: Exception do;
  262. end;
  263. lang := Copy(lang, 1, 5);
  264. try
  265. mo := TMOFile.Create(Format(AFilename, [lang]));
  266. try
  267. TranslateUnitResourceStrings(AUnitName,mo);
  268. finally
  269. mo.Free;
  270. end;
  271. except
  272. on e: Exception do;
  273. end;
  274. end;
  275. end.