gettext.pp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. {
  2. $Id$
  3. This file is part of the Free Pascal run time library.
  4. Copyright (c) 1999-2001 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
  81. begin
  82. AStream.Position := OrigTable^[i].offset;
  83. { SetLength(s, OrigTable^[i].length);
  84. AStream.Read(s[1], OrigTable^[i].length);
  85. OrigStrings^[i] := StrNew(PChar(s));}
  86. GetMem(OrigStrings^[i], OrigTable^[i].length + 1);
  87. AStream.Read(OrigStrings^[i]^, OrigTable^[i].length);
  88. OrigStrings^[i][OrigTable^[i].length] := #0;
  89. end;
  90. for i := 0 to StringCount - 1 do
  91. begin
  92. AStream.Position := TranslTable^[i].offset;
  93. { SetLength(s, TranslTable^[i].length);
  94. AStream.Read(s[1], TranslTable^[i].length);
  95. TranslStrings^[i] := StrNew(PChar(s));}
  96. GetMem(TranslStrings^[i], TranslTable^[i].length+1);
  97. AStream.Read(TranslStrings^[i]^, TranslTable^[i].length);
  98. TranslStrings^[i][TranslTable^[i].length] := #0;
  99. end;
  100. // Read hashing table
  101. HashTableSize := header.HashTabSize;
  102. GetMem(HashTable, 4 * HashTableSize);
  103. AStream.Position := header.HashTabOffset;
  104. AStream.Read(HashTable^, 4 * HashTableSize);
  105. end;
  106. constructor TMOFile.Create(const AFilename: String);
  107. var
  108. f: TStream;
  109. begin
  110. f := TFileStream.Create(AFilename, fmOpenRead);
  111. try
  112. Self.Create(f);
  113. finally
  114. f.Free;
  115. end;
  116. end;
  117. destructor TMOFile.Destroy;
  118. var
  119. i: Integer;
  120. begin
  121. for i := 0 to StringCount - 1 do
  122. begin
  123. FreeMem(OrigStrings^[i]);
  124. FreeMem(TranslStrings^[i]);
  125. end;
  126. FreeMem(OrigTable);
  127. FreeMem(TranslTable);
  128. FreeMem(OrigStrings);
  129. FreeMem(TranslStrings);
  130. FreeMem(HashTable);
  131. inherited Destroy;
  132. end;
  133. function TMOFile.Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  134. var
  135. idx, incr, nstr: LongWord;
  136. begin
  137. idx := AHash mod HashTableSize;
  138. incr := 1 + (AHash mod (HashTableSize - 2));
  139. while True do
  140. begin
  141. nstr := HashTable^[idx];
  142. if nstr = 0 then
  143. begin
  144. Result := '';
  145. exit;
  146. end;
  147. if (OrigTable^[nstr - 1].length = ALen) and
  148. (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then
  149. begin
  150. Result := TranslStrings^[nstr - 1];
  151. exit;
  152. end;
  153. if idx >= HashTableSize - incr then
  154. Dec(idx, HashTableSize - incr)
  155. else
  156. Inc(idx, incr);
  157. end;
  158. end;
  159. function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
  160. begin
  161. Result := Translate(PChar(AOrig), Length(AOrig), AHash);
  162. end;
  163. function TMOFile.Translate(AOrig: String): String;
  164. begin
  165. Result := Translate(AOrig, Hash(AOrig));
  166. end;
  167. // -------------------------------------------------------
  168. // Resourcestring translation procedures
  169. // -------------------------------------------------------
  170. {
  171. Define USEITERATOR if you want to translate the strings using
  172. the SetResourceStrings call. This is not recommended for this
  173. particular iplementation, since we must pass through a global
  174. variable TheFile : TMOFile. However that works too.
  175. }
  176. {$ifdef USEITERATOR}
  177. var
  178. Thefile : TMOFile;
  179. function Translate (Name,Value : AnsiString; Hash : Longint) : AnsiString;
  180. begin
  181. Result:=TheFile.Translate(Value,Hash);
  182. end;
  183. procedure TranslateResourceStrings(AFile: TMOFile);
  184. var
  185. i,j : Integer;
  186. s : String;
  187. begin
  188. TheFile:=AFile;
  189. SetResourceStrings(@Translate);
  190. end;
  191. {$else}
  192. procedure TranslateResourceStrings(AFile: TMOFile);
  193. var
  194. i, j, count: Integer;
  195. s: String;
  196. begin
  197. for i:=0 to ResourceStringTableCount - 1 do
  198. begin
  199. count := ResourceStringCount(I);
  200. for j := 0 to count - 1 do
  201. begin
  202. s := AFile.Translate(GetResourceStringDefaultValue(i, j),
  203. GetResourceStringHash(i, j));
  204. if Length(s) > 0 then
  205. begin
  206. SetResourceStringValue(i, j, s);
  207. GettextUsed := True;
  208. end;
  209. end;
  210. end;
  211. end;
  212. {$endif}
  213. procedure TranslateResourceStrings(const AFilename: String);
  214. var
  215. mo: TMOFile;
  216. lang, FallbackLanguage: String;
  217. begin
  218. lang := GetEnv('LC_ALL');
  219. if Length(lang) = 0 then
  220. begin
  221. lang := GetEnv('LC_MESSAGES');
  222. if Length(lang) = 0 then
  223. begin
  224. lang := GetEnv('LANG');
  225. if Length(lang) = 0 then
  226. exit; // no language defined via environment variables
  227. end;
  228. end;
  229. FallbackLanguage := Copy(lang, 1, 2);
  230. try
  231. mo := TMOFile.Create(Format(AFilename, [FallbackLanguage]));
  232. try
  233. TranslateResourceStrings(mo);
  234. finally
  235. mo.Free;
  236. end;
  237. except
  238. on e: Exception do;
  239. end;
  240. lang := Copy(lang, 1, 5);
  241. try
  242. mo := TMOFile.Create(Format(AFilename, [lang]));
  243. try
  244. TranslateResourceStrings(mo);
  245. finally
  246. mo.Free;
  247. end;
  248. except
  249. on e: Exception do;
  250. end;
  251. end;
  252. finalization
  253. if GettextUsed then
  254. ResetResourceTables;
  255. end.
  256. {
  257. $Log$
  258. Revision 1.6 2002-09-07 15:15:24 peter
  259. * old logs removed and tabs fixed
  260. Revision 1.5 2002/01/19 11:54:52 peter
  261. * fixed wrong getmem
  262. }