GLS.Language.pas 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297
  1. //
  2. // The multimedia graphics platform GLScene https://github.com/glscene
  3. //
  4. unit GLS.Language;
  5. (*
  6. Language created to localize your application.
  7. In Delphi, the text is encoded using Ansi cp1251 and can not be encoded \ decoding.
  8. In Lazarus has the ability to upload text from any encoding.
  9. *)
  10. interface
  11. {$I GLScene.inc}
  12. uses
  13. System.Classes,
  14. System.IniFiles,
  15. System.SysUtils,
  16. GLS.Utils;
  17. type
  18. TGLLanguageEntry = record
  19. ID: String; //**< identifier
  20. Text: String; //**< translation
  21. end;
  22. TGLLanguageEntryArray = array of TGLLanguageEntry;
  23. (* Class TGLLanguage is used only for downloading and translation,
  24. as in the final product it's no need for the text processing *)
  25. TGLLanguage = class
  26. private
  27. FCurrentLanguageFile: String;
  28. Entry: TGLLanguageEntryArray; //**< Entrys of Chosen Language
  29. public
  30. function FindID(const ID: String): integer;
  31. function Translate(const ID: String): String;
  32. procedure LoadLanguageFromFile(const Language: String);
  33. property CurrentLanguageFile: String read FCurrentLanguageFile;
  34. end;
  35. (* Advanced class is designed for loading and processing,
  36. will be useful for language editors *)
  37. TGLLanguageExt = class(TGLLanguage)
  38. private
  39. function GetEntry(Index: integer): TGLLanguageEntry;
  40. procedure SetEntry(Index: integer; const aValue: TGLLanguageEntry);
  41. function GetCount: integer;
  42. public
  43. procedure AddConst(const ID: String; const Text: String);
  44. procedure AddConsts(aValues: TStrings);
  45. procedure ChangeConst(const ID: String; const Text: String);
  46. property Items[Index: integer]: TGLLanguageEntry read GetEntry write SetEntry;
  47. property Count: integer read GetCount;
  48. procedure SaveLanguageFromFile(const Language: String); overload;
  49. procedure SaveLanguageFromFile; overload;
  50. end;
  51. // Abstract class for control Language
  52. TGLSLanguage = class(TComponent)
  53. private
  54. FLanguage: TGLLanguageExt;
  55. FLanguageList: TStrings;
  56. procedure SetLanguage(aValue: TGLLanguageExt);
  57. public
  58. constructor Create(AOwner: TComponent); override;
  59. destructor Destroy; override;
  60. procedure LoadLanguageFromFile(const Language: String);
  61. procedure SaveLanguageFromFile(const Language: String); overload;
  62. procedure SaveLanguageFromFile; overload;
  63. function Translate(const ID: String): String;
  64. property Language: TGLLanguageExt read FLanguage write SetLanguage;
  65. end;
  66. //-----------------------------------------------------------------------
  67. implementation
  68. //-----------------------------------------------------------------------
  69. uses
  70. GLS.Logger;
  71. //----------------------------
  72. // TGLLanguage
  73. //----------------------------
  74. // Load the specified LanguageFile
  75. procedure TGLLanguage.LoadLanguageFromFile(const Language: String);
  76. var
  77. IniFile: TMemIniFile;
  78. E: integer; // entry
  79. S: TStringList;
  80. I: integer;
  81. begin
  82. if Language = '' then
  83. Exit;
  84. if not FileExists(string(Language)) then
  85. begin
  86. {$IFDEF USE_LOGGING}
  87. GLSLogger.LogFatalError(ExtractFileName(string(Language)) +
  88. ' Languagefile missing!');
  89. {$ENDIF}
  90. Exit;
  91. end;
  92. SetLength(Entry, 0);
  93. FCurrentLanguageFile := Language;
  94. IniFile := TMemIniFile.Create(string(Language));
  95. S := TStringList.Create;
  96. IniFile.ReadSectionValues('Text', S);
  97. // Problem Solving with symbols wrap (#13#10)
  98. I := 0;
  99. for E := 0 to S.Count - 1 do
  100. begin
  101. If S.Names[E] = '' then
  102. begin
  103. S.Strings[I] := S.Strings[I] + #13#10 + GetValueFromStringsIndex(S, E);
  104. end
  105. else
  106. I := E;
  107. end;
  108. SetLength(Entry, S.Count);
  109. for E := 0 to high(Entry) do
  110. If S.Names[E] <> '' then
  111. begin
  112. Entry[E].ID := S.Names[E];
  113. Entry[E].Text := GetValueFromStringsIndex(S, E);
  114. end;
  115. S.Free;
  116. IniFile.Free;
  117. end;
  118. (* Find the index of ID an array of language entry.
  119. @returns the index on success, -1 otherwise *)
  120. function TGLLanguage.FindID(const ID: String): integer;
  121. var
  122. Index: integer;
  123. begin
  124. for Index := 0 to High(Entry) do
  125. begin
  126. if UpperCase(string(ID)) = UpperCase(string(Entry[Index].ID)) then
  127. begin
  128. Result := Index;
  129. Exit;
  130. end;
  131. end;
  132. Result := -1;
  133. end;
  134. (* Translate the Text.
  135. If Text is an ID, text will be translated according to the current language
  136. setting. If Text is not a known ID, it will be returned as is.
  137. @param Text either an ID or an UTF-8 encoded string *)
  138. function TGLLanguage.Translate(const ID: String): String;
  139. var
  140. EntryIndex: integer;
  141. begin
  142. // fallback result in case Text is not a known ID
  143. Result := ID;
  144. // Check if ID exists
  145. EntryIndex := FindID(ID);
  146. if (EntryIndex >= 0) then
  147. begin
  148. Result := Entry[EntryIndex].Text;
  149. Exit;
  150. end;
  151. end;
  152. // Add a Constant ID that will be Translated but not Loaded from the LanguageFile
  153. procedure TGLLanguageExt.AddConst(const ID: String; const Text: String);
  154. begin
  155. SetLength(Entry, Length(Entry) + 1);
  156. Entry[high(Entry)].ID := ID;
  157. Entry[high(Entry)].Text := Text;
  158. end;
  159. procedure TGLLanguageExt.AddConsts(aValues: TStrings);
  160. var
  161. I: integer;
  162. begin
  163. if aValues <> nil then
  164. for I := 0 to aValues.Count - 1 do
  165. If aValues.Names[I] <> '' then
  166. AddConst(aValues.Names[I],GetValueFromStringsIndex(aValues, I));
  167. end;
  168. procedure TGLLanguageExt.ChangeConst(const ID: String;
  169. const Text: String);
  170. var
  171. I: integer;
  172. begin
  173. for I := 0 to high(Entry) do
  174. begin
  175. if Entry[I].ID = ID then
  176. begin
  177. Entry[I].Text := Text;
  178. Break;
  179. end;
  180. end;
  181. end;
  182. function TGLLanguageExt.GetEntry(Index: integer): TGLLanguageEntry;
  183. begin
  184. Result := Entry[Index];
  185. end;
  186. procedure TGLLanguageExt.SetEntry(Index: integer; const aValue: TGLLanguageEntry);
  187. begin
  188. Entry[Index] := aValue;
  189. end;
  190. function TGLLanguageExt.GetCount: integer;
  191. begin
  192. Result := high(Entry) + 1;
  193. end;
  194. procedure TGLLanguageExt.SaveLanguageFromFile(const Language: String);
  195. var
  196. IniFile: TMemIniFile;
  197. E: integer; // entry
  198. begin
  199. if Language = '' then
  200. Exit;
  201. IniFile := TMemIniFile.Create(string(Language));
  202. for E := 0 to Count - 1 do
  203. begin
  204. IniFile.WriteString('Text', string(Items[E].ID), string(Items[E].Text));
  205. end;
  206. IniFile.UpdateFile;
  207. IniFile.Free;
  208. end;
  209. procedure TGLLanguageExt.SaveLanguageFromFile;
  210. begin
  211. SaveLanguageFromFile(CurrentLanguageFile);
  212. end;
  213. //----------------------------
  214. // TGLSLanguage
  215. //----------------------------
  216. constructor TGLSLanguage.Create(AOwner: TComponent);
  217. begin
  218. inherited Create(AOwner);
  219. FLanguage := TGLLanguageExt.Create;
  220. FLanguageList := TStringList.Create;
  221. end;
  222. destructor TGLSLanguage.Destroy;
  223. begin
  224. FLanguage.Free;
  225. FLanguageList.Free;
  226. inherited Destroy;
  227. end;
  228. procedure TGLSLanguage.LoadLanguageFromFile(const Language: String);
  229. begin
  230. FLanguage.LoadLanguageFromFile(Language);
  231. end;
  232. procedure TGLSLanguage.SetLanguage(aValue: TGLLanguageExt);
  233. begin
  234. if aValue <> nil then
  235. FLanguage := aValue;
  236. end;
  237. procedure TGLSLanguage.SaveLanguageFromFile(const Language: String);
  238. begin
  239. if Language = '' then
  240. Exit;
  241. FLanguage.SaveLanguageFromFile(Language);
  242. end;
  243. procedure TGLSLanguage.SaveLanguageFromFile;
  244. begin
  245. FLanguage.SaveLanguageFromFile;
  246. end;
  247. function TGLSLanguage.Translate(const ID: String): String;
  248. begin
  249. Result := FLanguage.Translate(ID);
  250. end;
  251. // ------------------------------------------------------------------------------
  252. initialization
  253. // ------------------------------------------------------------------------------
  254. RegisterClass(TGLSLanguage);
  255. end.