gettext.pp 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340
  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. {$ifdef Windows}
  60. uses
  61. windows;
  62. {$endif}
  63. constructor TMOFile.Create(AStream: TStream);
  64. var
  65. header: TMOFileHeader;
  66. i: Integer;
  67. begin
  68. inherited Create;
  69. AStream.Read(header, Sizeof(header));
  70. if header.magic <> MOFileHeaderMagic then
  71. raise EMOFileError.Create('Invalid magic - not a MO file?');
  72. GetMem(OrigTable, header.nstrings * SizeOf(TMOStringInfo));
  73. GetMem(TranslTable, header.nstrings * SizeOf(TMOStringInfo));
  74. GetMem(OrigStrings, header.nstrings * SizeOf(PChar));
  75. GetMem(TranslStrings, header.nstrings * SizeOf(PChar));
  76. AStream.Position := header.OrigTabOffset;
  77. AStream.Read(OrigTable^, header.nstrings * SizeOf(TMOStringInfo));
  78. AStream.Position := header.TransTabOffset;
  79. AStream.Read(TranslTable^, header.nstrings * SizeOf(TMOStringInfo));
  80. StringCount := header.nstrings;
  81. // Read strings
  82. for i := 0 to StringCount - 1 do
  83. begin
  84. AStream.Position := OrigTable^[i].offset;
  85. { SetLength(s, OrigTable^[i].length);
  86. AStream.Read(s[1], OrigTable^[i].length);
  87. OrigStrings^[i] := StrNew(PChar(s));}
  88. GetMem(OrigStrings^[i], OrigTable^[i].length + 1);
  89. AStream.Read(OrigStrings^[i]^, OrigTable^[i].length);
  90. OrigStrings^[i][OrigTable^[i].length] := #0;
  91. end;
  92. for i := 0 to StringCount - 1 do
  93. begin
  94. AStream.Position := TranslTable^[i].offset;
  95. { SetLength(s, TranslTable^[i].length);
  96. AStream.Read(s[1], TranslTable^[i].length);
  97. TranslStrings^[i] := StrNew(PChar(s));}
  98. GetMem(TranslStrings^[i], TranslTable^[i].length+1);
  99. AStream.Read(TranslStrings^[i]^, TranslTable^[i].length);
  100. TranslStrings^[i][TranslTable^[i].length] := #0;
  101. end;
  102. // Read hashing table
  103. HashTableSize := header.HashTabSize;
  104. GetMem(HashTable, 4 * HashTableSize);
  105. AStream.Position := header.HashTabOffset;
  106. AStream.Read(HashTable^, 4 * HashTableSize);
  107. end;
  108. constructor TMOFile.Create(const AFilename: String);
  109. var
  110. f: TStream;
  111. begin
  112. f := TFileStream.Create(AFilename, fmOpenRead);
  113. try
  114. Self.Create(f);
  115. finally
  116. f.Free;
  117. end;
  118. end;
  119. destructor TMOFile.Destroy;
  120. var
  121. i: Integer;
  122. begin
  123. for i := 0 to StringCount - 1 do
  124. begin
  125. Dispose(OrigStrings^[i]);
  126. Dispose(TranslStrings^[i]);
  127. end;
  128. Dispose(OrigTable);
  129. Dispose(TranslTable);
  130. Dispose(OrigStrings);
  131. Dispose(TranslStrings);
  132. Dispose(HashTable);
  133. inherited Destroy;
  134. end;
  135. function TMOFile.Translate(AOrig: PChar; ALen: Integer; AHash: LongWord): String;
  136. var
  137. idx, incr, nstr: LongWord;
  138. begin
  139. idx := AHash mod HashTableSize;
  140. incr := 1 + (AHash mod (HashTableSize - 2));
  141. while True do
  142. begin
  143. nstr := HashTable^[idx];
  144. if nstr = 0 then
  145. begin
  146. Result := '';
  147. exit;
  148. end;
  149. if (OrigTable^[nstr - 1].length = LongWord(ALen)) and
  150. (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then
  151. begin
  152. Result := TranslStrings^[nstr - 1];
  153. exit;
  154. end;
  155. if idx >= HashTableSize - incr then
  156. Dec(idx, HashTableSize - incr)
  157. else
  158. Inc(idx, incr);
  159. end;
  160. end;
  161. function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
  162. begin
  163. Result := Translate(PChar(AOrig), Length(AOrig), AHash);
  164. end;
  165. function TMOFile.Translate(AOrig: String): String;
  166. begin
  167. Result := Translate(AOrig, Hash(AOrig));
  168. end;
  169. // -------------------------------------------------------
  170. // Resourcestring translation procedures
  171. // -------------------------------------------------------
  172. function Translate (Name,Value : AnsiString; Hash : Longint; arg:pointer) : AnsiString;
  173. begin
  174. Result:=TMOFile(arg).Translate(Value,Hash);
  175. end;
  176. procedure TranslateResourceStrings(AFile: TMOFile);
  177. begin
  178. SetResourceStrings(@Translate,AFile);
  179. end;
  180. procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
  181. begin
  182. // SetUnitResourceStrings(AUnitName,@Translate,AFile);
  183. end;
  184. {$ifdef windows}
  185. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  186. var
  187. Buffer: array[1..4] of {$ifdef Wince}WideChar{$else}char{$endif};
  188. Country: string;
  189. UserLCID: LCID;
  190. begin
  191. //defaults
  192. Lang := '';
  193. FallbackLang:='';
  194. UserLCID := GetUserDefaultLCID;
  195. if GetLocaleInfo(UserLCID, LOCALE_SABBREVLANGNAME, @Buffer[1], 4)<>0 then
  196. FallbackLang := lowercase(copy(Buffer,1,2));
  197. if GetLocaleInfo(UserLCID, LOCALE_SABBREVCTRYNAME, @Buffer[1], 4)<>0 then begin
  198. Country := copy(Buffer,1,2);
  199. // some 2 letter codes are not the first two letters of the 3 letter code
  200. // there are probably more, but first let us see if there are translations
  201. if (Buffer='PRT') then Country:='PT';
  202. Lang := FallbackLang+'_'+Country;
  203. end;
  204. end;
  205. {$else}
  206. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  207. begin
  208. lang := GetEnvironmentVariable('LC_ALL');
  209. if Length(lang) = 0 then
  210. begin
  211. lang := GetEnvironmentVariable('LC_MESSAGES');
  212. if Length(lang) = 0 then
  213. begin
  214. lang := GetEnvironmentVariable('LANG');
  215. if Length(lang) = 0 then
  216. exit; // no language defined via environment variables
  217. end;
  218. end;
  219. FallbackLang := Copy(lang, 1, 2);
  220. end;
  221. {$endif}
  222. procedure TranslateResourceStrings(const AFilename: String);
  223. var
  224. mo: TMOFile;
  225. lang, FallbackLang: String;
  226. begin
  227. GetLanguageIDs(Lang, FallbackLang);
  228. try
  229. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  230. try
  231. TranslateResourceStrings(mo);
  232. finally
  233. mo.Free;
  234. end;
  235. except
  236. on e: Exception do;
  237. end;
  238. lang := Copy(lang, 1, 5);
  239. try
  240. mo := TMOFile.Create(Format(AFilename, [lang]));
  241. try
  242. TranslateResourceStrings(mo);
  243. finally
  244. mo.Free;
  245. end;
  246. except
  247. on e: Exception do;
  248. end;
  249. end;
  250. procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
  251. var
  252. mo: TMOFile;
  253. lang, FallbackLang: String;
  254. begin
  255. GetLanguageIDs(Lang, FallbackLang);
  256. try
  257. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  258. try
  259. TranslateUnitResourceStrings(AUnitName,mo);
  260. finally
  261. mo.Free;
  262. end;
  263. except
  264. on e: Exception do;
  265. end;
  266. lang := Copy(lang, 1, 5);
  267. try
  268. mo := TMOFile.Create(Format(AFilename, [lang]));
  269. try
  270. TranslateUnitResourceStrings(AUnitName,mo);
  271. finally
  272. mo.Free;
  273. end;
  274. except
  275. on e: Exception do;
  276. end;
  277. end;
  278. finalization
  279. finalizeresourcetables;
  280. end.