gettext.pp 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2000 by the Free Pascal development team
  5. Gettext interface to resourcestrings.
  6. See the file COPYING.FPC, included in this distribution,
  7. for details about the copyright.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
  11. **********************************************************************}
  12. {$MODE objfpc}
  13. {$H+}
  14. unit gettext;
  15. interface
  16. uses SysUtils, Classes;
  17. const
  18. MOFileHeaderMagic = $950412de;
  19. type
  20. TMOFileHeader = packed record
  21. magic: LongWord; // MOFileHeaderMagic
  22. revision: LongWord; // 0
  23. nstrings: LongWord; // Number of string pairs
  24. OrigTabOffset: LongWord; // Offset of original string offset table
  25. TransTabOffset: LongWord; // Offset of translated string offset table
  26. HashTabSize: LongWord; // Size of hashing table
  27. HashTabOffset: LongWord; // Offset of first hashing table entry
  28. end;
  29. TMOStringInfo = packed record
  30. length: LongWord;
  31. offset: LongWord;
  32. end;
  33. TMOStringTable = array[0..(1 shl 30) div SizeOf(TMOStringInfo)] of TMOStringInfo;
  34. PMOStringTable = ^TMOStringTable;
  35. TLongWordArray = array[0..(1 shl 30) div SizeOf(LongWord)] of LongWord;
  36. PLongWordArray = ^TLongWordArray;
  37. TPCharArray = array[0..(1 shl 30) div SizeOf(PChar)] of PChar;
  38. PPCharArray = ^TPCharArray;
  39. TMOFile = class
  40. protected
  41. StringCount, HashTableSize: LongWord;
  42. HashTable: PLongWordArray;
  43. OrigTable, TranslTable: PMOStringTable;
  44. OrigStrings, TranslStrings: PPCharArray;
  45. public
  46. constructor Create(const AFilename: String);
  47. constructor Create(AStream: TStream);
  48. destructor Destroy; override;
  49. function Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  50. function Translate(AOrig: String; AHash: LongWord): String;
  51. function Translate(AOrig: String): String;
  52. end;
  53. EMOFileError = type Exception;
  54. procedure TranslateResourceStrings(AFile: TMOFile);
  55. procedure TranslateResourceStrings(const AFilename: String);
  56. implementation
  57. uses dos;
  58. var
  59. GettextUsed: Boolean;
  60. constructor TMOFile.Create(AStream: TStream);
  61. var
  62. header: TMOFileHeader;
  63. i: Integer;
  64. s: String;
  65. begin
  66. inherited Create;
  67. AStream.Read(header, Sizeof(header));
  68. if header.magic <> MOFileHeaderMagic then
  69. raise EMOFileError.Create('Invalid magic - not a MO file?');
  70. GetMem(OrigTable, header.nstrings * SizeOf(TMOStringInfo));
  71. GetMem(TranslTable, header.nstrings * SizeOf(TMOStringInfo));
  72. GetMem(OrigStrings, header.nstrings * SizeOf(PChar));
  73. GetMem(TranslStrings, header.nstrings * SizeOf(PChar));
  74. AStream.Position := header.OrigTabOffset;
  75. AStream.Read(OrigTable^, header.nstrings * SizeOf(TMOStringInfo));
  76. AStream.Position := header.TransTabOffset;
  77. AStream.Read(TranslTable^, header.nstrings * SizeOf(TMOStringInfo));
  78. StringCount := header.nstrings;
  79. // Read strings
  80. for i := 0 to StringCount - 1 do 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. end;
  86. for i := 0 to StringCount - 1 do begin
  87. AStream.Position := TranslTable^[i].offset;
  88. SetLength(s, TranslTable^[i].length);
  89. AStream.Read(s[1], TranslTable^[i].length);
  90. TranslStrings^[i] := StrNew(PChar(s));
  91. end;
  92. // Read hashing table
  93. HashTableSize := header.HashTabSize;
  94. GetMem(HashTable, 4 * HashTableSize);
  95. AStream.Position := header.HashTabOffset;
  96. AStream.Read(HashTable^, 4 * HashTableSize);
  97. end;
  98. constructor TMOFile.Create(const AFilename: String);
  99. var
  100. f: TStream;
  101. begin
  102. f := TFileStream.Create(AFilename, fmOpenRead);
  103. try
  104. Self.Create(f);
  105. finally
  106. f.Free;
  107. end;
  108. end;
  109. destructor TMOFile.Destroy;
  110. var
  111. i: Integer;
  112. begin
  113. for i := 0 to StringCount - 1 do begin
  114. StrDispose(OrigStrings^[i]);
  115. StrDispose(TranslStrings^[i]);
  116. end;
  117. FreeMem(OrigTable);
  118. FreeMem(TranslTable);
  119. FreeMem(OrigStrings);
  120. FreeMem(TranslStrings);
  121. FreeMem(HashTable);
  122. inherited Destroy;
  123. end;
  124. function TMOFile.Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  125. var
  126. idx, incr, nstr: LongWord;
  127. begin
  128. idx := AHash mod HashTableSize;
  129. incr := 1 + (AHash mod (HashTableSize - 2));
  130. while True do begin
  131. nstr := HashTable^[idx];
  132. if nstr = 0 then begin
  133. Result := '';
  134. exit;
  135. end;
  136. if (OrigTable^[nstr - 1].length = ALen) and
  137. (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then begin
  138. Result := TranslStrings^[nstr - 1];
  139. exit;
  140. end;
  141. if idx >= HashTableSize - incr then
  142. Dec(idx, HashTableSize - incr)
  143. else
  144. Inc(idx, incr);
  145. end;
  146. end;
  147. function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
  148. begin
  149. Result := Translate(PChar(AOrig), Length(AOrig), AHash);
  150. end;
  151. function TMOFile.Translate(AOrig: String): String;
  152. begin
  153. Result := Translate(AOrig, Hash(AOrig));
  154. end;
  155. // -------------------------------------------------------
  156. // Resourcestring translation procedures
  157. // -------------------------------------------------------
  158. {
  159. Define USEITERATOR if you want to translate the strings using
  160. the SetResourceStrings call. This is not recommended for this
  161. particular iplementation, since we must pass through a global
  162. variable TheFile : TMOFile. However that works too.
  163. }
  164. {$ifdef USEITERATOR}
  165. var
  166. Thefile : TMOFile;
  167. function Translate (Name,Value : AnsiString; Hash : Longint) : AnsiString;
  168. begin
  169. Result:=TheFile.Translate(Value,Hash);
  170. end;
  171. procedure TranslateResourceStrings(AFile: TMOFile);
  172. var
  173. i,j : Integer;
  174. s : String;
  175. begin
  176. TheFile:=AFile;
  177. SetResourceStrings(@Translate);
  178. end;
  179. {$else}
  180. procedure TranslateResourceStrings(AFile: TMOFile);
  181. var
  182. i, j, count: Integer;
  183. s: String;
  184. begin
  185. for i:=0 to ResourceStringTableCount - 1 do begin
  186. count := ResourceStringCount(I);
  187. for j := 0 to count - 1 do begin
  188. s := AFile.Translate(GetResourceStringDefaultValue(i, j),
  189. GetResourceStringHash(i, j));
  190. if Length(s) > 0 then begin
  191. SetResourceStringValue(i, j, s);
  192. GettextUsed := True;
  193. end;
  194. end;
  195. end;
  196. end;
  197. {$endif}
  198. procedure TranslateResourceStrings(const AFilename: String);
  199. var
  200. mo: TMOFile;
  201. lang: String;
  202. begin
  203. lang := Copy(GetEnv('LANG'), 1, 2);
  204. try
  205. mo := TMOFile.Create(Format(AFilename, [lang]));
  206. try
  207. TranslateResourceStrings(mo);
  208. finally
  209. mo.Free;
  210. end;
  211. except
  212. on e: Exception do;
  213. end;
  214. end;
  215. finalization
  216. if GettextUsed then
  217. ResetResourceTables;
  218. end.
  219. {
  220. $Log$
  221. Revision 1.11 2000-02-20 10:59:11 sg
  222. * Fixed dynamic array sizes
  223. Revision 1.10 2000/02/17 22:14:51 sg
  224. * Now calls "ResetResourceTables" on unit finalization if gettext has been
  225. used. This enabled programs using gettext to use heaptrc, which reported
  226. memory leaks for the translated strings until now.
  227. Revision 1.9 2000/01/30 22:16:59 sg
  228. * Fixed memory leaks
  229. Revision 1.8 2000/01/07 01:24:33 peter
  230. * updated copyright to 2000
  231. Revision 1.7 2000/01/06 01:20:33 peter
  232. * moved out of packages/ back to topdir
  233. Revision 1.1 2000/01/03 19:33:07 peter
  234. * moved to packages dir
  235. Revision 1.5 1999/10/15 19:42:18 michael
  236. hash is available in tables
  237. Revision 1.4 1999/08/28 13:35:16 michael
  238. * Uses now hash function of objpas
  239. Revision 1.3 1999/08/27 15:53:36 michael
  240. + Adapted to new resourcestring mechanism. Uses objpas interface only
  241. Revision 1.2 1999/08/26 11:05:15 peter
  242. * updated for new resourcestrings
  243. Revision 1.1 1999/08/04 11:31:09 michael
  244. * Added gettext
  245. Revision 1.1 1999/07/25 16:23:31 michael
  246. + Initial implementation from Sebastian Guenther
  247. }