clocale.pp 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293
  1. {
  2. This file is part of the Free Pascal run time library.
  3. Copyright (c) 2008 by the Free Pascal development team.
  4. Init rtl formating variables based on libc locales
  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. { Initial implementation by petr kristan }
  12. unit clocale;
  13. {$mode objfpc}
  14. interface
  15. {$ifdef localedebug}
  16. // for easier debugging, allows to print untransformed values in test
  17. Type TOrgFormatSettings = record
  18. ShortDateFormat,
  19. LongDateFormat ,
  20. ShortTimeFormat,
  21. LongTimeFormat ,
  22. CurrencyString1,
  23. CurrencyString2: string;
  24. end;
  25. var OrgFormatSettings : TOrgFormatSettings;
  26. {$endif}
  27. implementation
  28. {$linklib c}
  29. Uses
  30. SysUtils, unixtype, initc;
  31. Const
  32. {$ifdef BSD}
  33. // Darwin and FreeBSD. Note the lead underscores are added.
  34. {$i clocale.inc}
  35. {$else}
  36. // checked for Linux only, but might be general glibc.
  37. __LC_CTYPE = 0;
  38. __LC_NUMERIC = 1;
  39. __LC_TIME = 2;
  40. __LC_COLLATE = 3;
  41. __LC_MONETARY = 4;
  42. __LC_MESSAGES = 5;
  43. __LC_ALL = 6;
  44. ABDAY_1 = (__LC_TIME shl 16);
  45. DAY_1 = (ABDAY_1)+7;
  46. ABMON_1 = (ABDAY_1)+14;
  47. MON_1 = (ABDAY_1)+26;
  48. AM_STR = (ABDAY_1)+38;
  49. PM_STR = (ABDAY_1)+39;
  50. D_T_FMT = (ABDAY_1)+40;
  51. D_FMT = (ABDAY_1)+41;
  52. T_FMT = (ABDAY_1)+42;
  53. T_FMT_AMPM = (ABDAY_1)+43;
  54. __DECIMAL_POINT = (__LC_NUMERIC shl 16);
  55. RADIXCHAR = __DECIMAL_POINT;
  56. __THOUSANDS_SEP = (__DECIMAL_POINT)+1;
  57. __INT_CURR_SYMBOL = (__LC_MONETARY shl 16);
  58. __CURRENCY_SYMBOL = (__INT_CURR_SYMBOL)+1;
  59. __MON_DECIMAL_POINT = (__INT_CURR_SYMBOL)+2;
  60. __MON_THOUSANDS_SEP = (__INT_CURR_SYMBOL)+3;
  61. __MON_GROUPING = (__INT_CURR_SYMBOL)+4;
  62. __POSITIVE_SIGN = (__INT_CURR_SYMBOL)+5;
  63. __NEGATIVE_SIGN = (__INT_CURR_SYMBOL)+6;
  64. __INT_FRAC_DIGITS = (__INT_CURR_SYMBOL)+7;
  65. __FRAC_DIGITS = (__INT_CURR_SYMBOL)+8;
  66. __P_CS_PRECEDES = (__INT_CURR_SYMBOL)+9;
  67. __P_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+10;
  68. __N_CS_PRECEDES = (__INT_CURR_SYMBOL)+11;
  69. __N_SEP_BY_SPACE = (__INT_CURR_SYMBOL)+12;
  70. __P_SIGN_POSN = (__INT_CURR_SYMBOL)+13;
  71. __N_SIGN_POSN = (__INT_CURR_SYMBOL)+14;
  72. _NL_MONETARY_CRNCYSTR = (__INT_CURR_SYMBOL)+15;
  73. {$endif}
  74. function setlocale(category: cint; locale: pchar): pchar; cdecl; external clib name 'setlocale';
  75. function nl_langinfo(__item: cint):Pchar;cdecl;external clib name 'nl_langinfo';
  76. procedure GetFormatSettings;
  77. function GetLocaleStr(item: cint): string;
  78. begin
  79. GetLocaleStr := AnsiString(nl_langinfo(item));
  80. end;
  81. function GetLocaleChar(item: cint): char;
  82. begin
  83. GetLocaleChar := nl_langinfo(item)^;
  84. end;
  85. function FindSeparator(const s: string; Def: char): char;
  86. var
  87. i, l: integer;
  88. begin
  89. FindSeparator := Def;
  90. i := Pos('%', s);
  91. if i=0 then
  92. Exit;
  93. l := Length(s);
  94. inc(i);
  95. if (i<=l) and (s[i] in ['E', 'O']) then //possible modifier
  96. inc(i);
  97. inc(i);
  98. if i<=l then
  99. FindSeparator := s[i];
  100. end;
  101. function TransformFormatStr(const s: string): string;
  102. var
  103. i, l: integer;
  104. begin
  105. TransformFormatStr := '';
  106. i := 1;
  107. l := Length(s);
  108. while i<=l do begin
  109. if s[i]='%' then begin
  110. inc(i);
  111. if (i<=l) and (s[i] in ['E', 'O']) then //ignore modifier
  112. inc(i);
  113. if i>l then
  114. Exit;
  115. case s[i] of
  116. 'a': TransformFormatStr := TransformFormatStr + 'ddd';
  117. 'A': TransformFormatStr := TransformFormatStr + 'dddd';
  118. 'b': TransformFormatStr := TransformFormatStr + 'mmm';
  119. 'B': TransformFormatStr := TransformFormatStr + 'mmmm';
  120. 'c': TransformFormatStr := TransformFormatStr + 'c';
  121. //'C':
  122. 'd': TransformFormatStr := TransformFormatStr + 'dd';
  123. 'D': TransformFormatStr := TransformFormatStr + 'mm"/"dd"/"yy';
  124. 'e': TransformFormatStr := TransformFormatStr + 'd';
  125. 'F': TransformFormatStr := TransformFormatStr + 'yyyy-mm-dd';
  126. 'g': TransformFormatStr := TransformFormatStr + 'yy';
  127. 'G': TransformFormatStr := TransformFormatStr + 'yyyy';
  128. 'h': TransformFormatStr := TransformFormatStr + 'mmm';
  129. 'H': TransformFormatStr := TransformFormatStr + 'hh';
  130. 'I': TransformFormatStr := TransformFormatStr + 'hhampm';
  131. //'j':
  132. 'k': TransformFormatStr := TransformFormatStr + 'h';
  133. 'l': TransformFormatStr := TransformFormatStr + 'hampm';
  134. 'm': TransformFormatStr := TransformFormatStr + 'mm';
  135. 'M': TransformFormatStr := TransformFormatStr + 'nn';
  136. 'n': TransformFormatStr := TransformFormatStr + sLineBreak;
  137. 'p': TransformFormatStr := TransformFormatStr + 'ampm';
  138. 'P': TransformFormatStr := TransformFormatStr + 'ampm';
  139. 'r': TransformFormatStr := TransformFormatStr + 'hhampm:nn:ss';
  140. 'R': TransformFormatStr := TransformFormatStr + 'hh:nn';
  141. //'s':
  142. 'S': TransformFormatStr := TransformFormatStr + 'ss';
  143. 't': TransformFormatStr := TransformFormatStr + #9;
  144. 'T': TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  145. //'u':
  146. //'U':
  147. //'V':
  148. //'w':
  149. //'W':
  150. 'x': TransformFormatStr := TransformFormatStr + 'ddddd';
  151. 'X': TransformFormatStr := TransformFormatStr + 't';
  152. 'y': TransformFormatStr := TransformFormatStr + 'yy';
  153. 'Y': TransformFormatStr := TransformFormatStr + 'yyyy';
  154. //'z':
  155. //'Z':
  156. '%': TransformFormatStr := TransformFormatStr + '%';
  157. end;
  158. end else
  159. TransformFormatStr := TransformFormatStr + s[i];
  160. inc(i);
  161. end;
  162. end;
  163. const
  164. // sign prec sep
  165. NegFormatsTable: array [0..4, 0..1, 0..1] of byte = (
  166. ( (4, 15), (0, 14) ), //Parentheses surround the quantity and currency_symbol
  167. ( (5, 8), (1, 9) ), //The sign string precedes the quantity and currency_symbol
  168. ( (7, 10), (3, 11) ), //The sign string follows the quantity and currency_symbol
  169. ( (6, 13), (1, 9) ), //The sign string immediately precedes the currency_symbol
  170. ( (7, 10), (2, 12) ) //The sign string immediately follows the currency_symbol
  171. );
  172. var
  173. i: integer;
  174. prec, sep, signp: byte;
  175. {$ifdef BSD}
  176. plocale : plconv;
  177. {$ENDIF}
  178. begin
  179. setlocale(__LC_ALL,'');
  180. for i := 1 to 12 do
  181. begin
  182. ShortMonthNames[i]:=GetLocaleStr(ABMON_1+i-1);
  183. LongMonthNames[i]:=GetLocaleStr(MON_1+i-1);
  184. end;
  185. for i := 1 to 7 do
  186. begin
  187. ShortDayNames[i]:=GetLocaleStr(ABDAY_1+i-1);
  188. LongDayNames[i]:=GetLocaleStr(DAY_1+i-1);
  189. end;
  190. //Date stuff
  191. ShortDateFormat := GetLocaleStr(D_FMT);
  192. {$ifdef localedebug}
  193. OrgFormatSettings.ShortDateFormat:=shortdateformat;
  194. {$endif}
  195. DateSeparator := FindSeparator(ShortDateFormat, DateSeparator);
  196. ShortDateFormat := TransformFormatStr(ShortDateFormat);
  197. LongDateFormat := GetLocaleStr(D_T_FMT);
  198. {$ifdef localedebug}
  199. OrgFormatSettings.LongDateFormat:=longdateformat;
  200. {$endif}
  201. LongDateFormat := TransformFormatStr(LongDateFormat);
  202. //Time stuff
  203. TimeAMString := GetLocaleStr(AM_STR);
  204. TimePMString := GetLocaleStr(PM_STR);
  205. ShortTimeFormat := GetLocaleStr(T_FMT);
  206. {$ifdef localedebug}
  207. OrgFormatSettings.ShortTimeFormat:=shorttimeformat;
  208. {$endif}
  209. TimeSeparator := FindSeparator(ShortTimeFormat, TimeSeparator);
  210. ShortTimeFormat := TransformFormatStr(ShortTimeFormat);
  211. LongTimeFormat := GetLocaleStr(T_FMT_AMPM);
  212. {$ifdef localedebug}
  213. OrgFormatSettings.LongTimeFormat:=longtimeformat;
  214. {$endif}
  215. LongTimeFormat := TransformFormatStr(LongTimeFormat);
  216. {$Ifdef BSD}
  217. plocale:=localeconv;
  218. // for these fields there is a separate BSD derived POSIX function.
  219. if not assigned(plocale) then exit; // for now.
  220. CurrencyString:=plocale^.CURRENCY_SYMBOL;
  221. CurrencyString := Copy(CurrencyString, 1, Length(CurrencyString));
  222. CurrencyDecimals:=ord(plocale^.FRAC_DIGITS);
  223. {$ifdef localedebug}
  224. OrgFormatSettings.CurrencyString1:=plocale^.currency_symbol;
  225. OrgFormatSettings.CurrencyString2:=plocale^.int_curr_symbol;
  226. {$endif}
  227. prec:=ord(plocale^.P_CS_PRECEDES);
  228. sep:=ord(plocale^.P_SEP_BY_SPACE);
  229. if (prec<=1) and (sep<=1) then
  230. CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  231. prec := ord(plocale^.N_CS_PRECEDES);
  232. sep := ord(plocale^.N_SEP_BY_SPACE);
  233. signp := ord(plocale^.N_SIGN_POSN);
  234. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  235. NegCurrFormat := NegFormatsTable[signp, prec, sep];
  236. //Number stuff
  237. ThousandSeparator:=plocale^.THOUSANDS_SEP[0];
  238. {$else}
  239. //Currency stuff
  240. CurrencyString := GetLocaleStr(_NL_MONETARY_CRNCYSTR);
  241. {$ifdef localedebug}
  242. OrgFormatSettings.CurrencyString1:=currencystring;
  243. OrgFormatSettings.CurrencyString2:='';
  244. {$endif}
  245. CurrencyString := Copy(CurrencyString, 2, Length(CurrencyString));
  246. CurrencyDecimals := StrToIntDef(GetLocaleStr(__FRAC_DIGITS), CurrencyDecimals);
  247. prec := byte(GetLocaleChar(__P_CS_PRECEDES));
  248. sep := byte(GetLocaleChar(__P_SEP_BY_SPACE));
  249. if (prec<=1) and (sep<=1) then
  250. CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  251. prec := byte(GetLocaleChar(__N_CS_PRECEDES));
  252. sep := byte(GetLocaleChar(__N_SEP_BY_SPACE));
  253. signp := byte(GetLocaleChar(__N_SIGN_POSN));
  254. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  255. NegCurrFormat := NegFormatsTable[signp, prec, sep];
  256. //Number stuff
  257. ThousandSeparator:=GetLocaleChar(__THOUSANDS_SEP);
  258. {$endif}
  259. DecimalSeparator:=GetLocaleChar(RADIXCHAR);
  260. end;
  261. initialization
  262. GetFormatSettings;
  263. end.