gettext.pp 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  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. if AHash = $FFFFFFFF then
  140. begin
  141. Result := '';
  142. exit;
  143. end;
  144. idx := AHash mod HashTableSize;
  145. incr := 1 + (AHash mod (HashTableSize - 2));
  146. while True do
  147. begin
  148. nstr := HashTable^[idx];
  149. if (nstr = 0) or (nstr > StringCount) then
  150. begin
  151. Result := '';
  152. exit;
  153. end;
  154. if (OrigTable^[nstr - 1].length = LongWord(ALen)) and
  155. (StrComp(OrigStrings^[nstr - 1], AOrig) = 0) then
  156. begin
  157. Result := TranslStrings^[nstr - 1];
  158. exit;
  159. end;
  160. if idx >= HashTableSize - incr then
  161. Dec(idx, HashTableSize - incr)
  162. else
  163. Inc(idx, incr);
  164. end;
  165. end;
  166. function TMOFile.Translate(AOrig: String; AHash: LongWord): String;
  167. begin
  168. Result := Translate(PChar(AOrig), Length(AOrig), AHash);
  169. end;
  170. function TMOFile.Translate(AOrig: String): String;
  171. begin
  172. Result := Translate(AOrig, Hash(AOrig));
  173. end;
  174. // -------------------------------------------------------
  175. // Resourcestring translation procedures
  176. // -------------------------------------------------------
  177. function Translate (Name,Value : AnsiString; Hash : Longint; arg:pointer) : AnsiString;
  178. begin
  179. Result:=TMOFile(arg).Translate(Value,Hash);
  180. end;
  181. procedure TranslateResourceStrings(AFile: TMOFile);
  182. begin
  183. SetResourceStrings(@Translate,AFile);
  184. end;
  185. procedure TranslateUnitResourceStrings(const AUnitName:string; AFile: TMOFile);
  186. begin
  187. // SetUnitResourceStrings(AUnitName,@Translate,AFile);
  188. end;
  189. {$ifdef windows}
  190. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  191. var
  192. Buffer: array[1..4] of {$ifdef Wince}WideChar{$else}char{$endif};
  193. Country: string;
  194. UserLCID: LCID;
  195. begin
  196. //defaults
  197. Lang := '';
  198. FallbackLang:='';
  199. UserLCID := GetUserDefaultLCID;
  200. if GetLocaleInfo(UserLCID, LOCALE_SABBREVLANGNAME, @Buffer[1], 4)<>0 then
  201. FallbackLang := lowercase(copy(Buffer,1,2));
  202. if GetLocaleInfo(UserLCID, LOCALE_SABBREVCTRYNAME, @Buffer[1], 4)<>0 then begin
  203. Country := copy(Buffer,1,2);
  204. // some 2 letter codes are not the first two letters of the 3 letter code
  205. // there are probably more, but first let us see if there are translations
  206. if (Buffer='PRT') then Country:='PT';
  207. Lang := FallbackLang+'_'+Country;
  208. end;
  209. end;
  210. {$else}
  211. procedure GetLanguageIDs(var Lang, FallbackLang: string);
  212. begin
  213. lang := GetEnvironmentVariable('LC_ALL');
  214. if Length(lang) = 0 then
  215. begin
  216. lang := GetEnvironmentVariable('LC_MESSAGES');
  217. if Length(lang) = 0 then
  218. begin
  219. lang := GetEnvironmentVariable('LANG');
  220. if Length(lang) = 0 then
  221. exit; // no language defined via environment variables
  222. end;
  223. end;
  224. FallbackLang := Copy(lang, 1, 2);
  225. end;
  226. {$endif}
  227. procedure TranslateResourceStrings(const AFilename: String);
  228. var
  229. mo: TMOFile;
  230. lang, FallbackLang: String;
  231. begin
  232. GetLanguageIDs(Lang, FallbackLang);
  233. try
  234. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  235. try
  236. TranslateResourceStrings(mo);
  237. finally
  238. mo.Free;
  239. end;
  240. except
  241. on e: Exception do;
  242. end;
  243. lang := Copy(lang, 1, 5);
  244. try
  245. mo := TMOFile.Create(Format(AFilename, [lang]));
  246. try
  247. TranslateResourceStrings(mo);
  248. finally
  249. mo.Free;
  250. end;
  251. except
  252. on e: Exception do;
  253. end;
  254. end;
  255. procedure TranslateUnitResourceStrings(const AUnitName:string; const AFilename: String);
  256. var
  257. mo: TMOFile;
  258. lang, FallbackLang: String;
  259. begin
  260. GetLanguageIDs(Lang, FallbackLang);
  261. try
  262. mo := TMOFile.Create(Format(AFilename, [FallbackLang]));
  263. try
  264. TranslateUnitResourceStrings(AUnitName,mo);
  265. finally
  266. mo.Free;
  267. end;
  268. except
  269. on e: Exception do;
  270. end;
  271. lang := Copy(lang, 1, 5);
  272. try
  273. mo := TMOFile.Create(Format(AFilename, [lang]));
  274. try
  275. TranslateUnitResourceStrings(AUnitName,mo);
  276. finally
  277. mo.Free;
  278. end;
  279. except
  280. on e: Exception do;
  281. end;
  282. end;
  283. finalization
  284. finalizeresourcetables;
  285. end.