clocale.pp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316
  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. clock12:boolean;
  105. begin
  106. clock12:=false; // should ampm get appended?
  107. TransformFormatStr := '';
  108. i := 1;
  109. l := Length(s);
  110. while i<=l do begin
  111. if s[i]='%' then begin
  112. inc(i);
  113. if (i<=l) and (s[i] in ['E', 'O']) then //ignore modifier
  114. inc(i);
  115. if i>l then
  116. Exit;
  117. case s[i] of
  118. 'a': TransformFormatStr := TransformFormatStr + 'ddd';
  119. 'A': TransformFormatStr := TransformFormatStr + 'dddd';
  120. 'b': TransformFormatStr := TransformFormatStr + 'mmm';
  121. 'B': TransformFormatStr := TransformFormatStr + 'mmmm';
  122. 'c': TransformFormatStr := TransformFormatStr + 'c';
  123. //'C':
  124. 'd': TransformFormatStr := TransformFormatStr + 'dd';
  125. 'D': TransformFormatStr := TransformFormatStr + 'mm"/"dd"/"yy';
  126. 'e': TransformFormatStr := TransformFormatStr + 'd';
  127. 'F': TransformFormatStr := TransformFormatStr + 'yyyy-mm-dd';
  128. 'g': TransformFormatStr := TransformFormatStr + 'yy';
  129. 'G': TransformFormatStr := TransformFormatStr + 'yyyy';
  130. 'h': TransformFormatStr := TransformFormatStr + 'mmm';
  131. 'H': TransformFormatStr := TransformFormatStr + 'hh';
  132. 'I': begin
  133. TransformFormatStr := TransformFormatStr + 'hh';
  134. clock12:=true;
  135. end;
  136. //'j':
  137. 'k': TransformFormatStr := TransformFormatStr + 'h';
  138. 'l': begin
  139. TransformFormatStr := TransformFormatStr + 'h';
  140. clock12:=true;
  141. end;
  142. 'm': TransformFormatStr := TransformFormatStr + 'mm';
  143. 'M': TransformFormatStr := TransformFormatStr + 'nn';
  144. 'n': TransformFormatStr := TransformFormatStr + sLineBreak;
  145. 'p','P':
  146. begin
  147. TransformFormatStr := TransformFormatStr + 'ampm';
  148. clock12:=false;
  149. end;
  150. 'r': begin
  151. TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  152. clock12:=true;
  153. end;
  154. 'R': TransformFormatStr := TransformFormatStr + 'hh:nn';
  155. //'s':
  156. 'S': TransformFormatStr := TransformFormatStr + 'ss';
  157. 't': TransformFormatStr := TransformFormatStr + #9;
  158. 'T': TransformFormatStr := TransformFormatStr + 'hh:nn:ss';
  159. //'u':
  160. //'U':
  161. //'V':
  162. //'w':
  163. //'W':
  164. 'x': TransformFormatStr := TransformFormatStr + 'ddddd';
  165. 'X': TransformFormatStr := TransformFormatStr + 't';
  166. 'y': TransformFormatStr := TransformFormatStr + 'yy';
  167. 'Y': TransformFormatStr := TransformFormatStr + 'yyyy';
  168. //'z':
  169. //'Z':
  170. '%': TransformFormatStr := TransformFormatStr + '%';
  171. end;
  172. end else
  173. TransformFormatStr := TransformFormatStr + s[i];
  174. inc(i);
  175. end;
  176. i:=length(TransformFormatStr);
  177. if clock12 and (i>0) then
  178. begin
  179. if transformformatstr[i]<>' ' then
  180. TransformFormatStr := TransformFormatStr + ' ';
  181. TransformFormatStr := TransformFormatStr + 'ampm';
  182. end;
  183. end;
  184. const
  185. // sign prec sep
  186. NegFormatsTable: array [0..4, 0..1, 0..1] of byte = (
  187. ( (4, 15), (0, 14) ), //Parentheses surround the quantity and currency_symbol
  188. ( (5, 8), (1, 9) ), //The sign string precedes the quantity and currency_symbol
  189. ( (7, 10), (3, 11) ), //The sign string follows the quantity and currency_symbol
  190. ( (6, 13), (1, 9) ), //The sign string immediately precedes the currency_symbol
  191. ( (7, 10), (2, 12) ) //The sign string immediately follows the currency_symbol
  192. );
  193. var
  194. i: integer;
  195. prec, sep, signp: byte;
  196. {$ifdef BSD}
  197. plocale : plconv;
  198. {$ENDIF}
  199. begin
  200. setlocale(__LC_ALL,'');
  201. for i := 1 to 12 do
  202. begin
  203. ShortMonthNames[i]:=GetLocaleStr(ABMON_1+i-1);
  204. LongMonthNames[i]:=GetLocaleStr(MON_1+i-1);
  205. end;
  206. for i := 1 to 7 do
  207. begin
  208. ShortDayNames[i]:=GetLocaleStr(ABDAY_1+i-1);
  209. LongDayNames[i]:=GetLocaleStr(DAY_1+i-1);
  210. end;
  211. //Date stuff
  212. ShortDateFormat := GetLocaleStr(D_FMT);
  213. {$ifdef localedebug}
  214. OrgFormatSettings.ShortDateFormat:=shortdateformat;
  215. {$endif}
  216. DateSeparator := FindSeparator(ShortDateFormat, DateSeparator);
  217. ShortDateFormat := TransformFormatStr(ShortDateFormat);
  218. LongDateFormat := GetLocaleStr(D_T_FMT);
  219. {$ifdef localedebug}
  220. OrgFormatSettings.LongDateFormat:=longdateformat;
  221. {$endif}
  222. LongDateFormat := TransformFormatStr(LongDateFormat);
  223. //Time stuff
  224. TimeAMString := GetLocaleStr(AM_STR);
  225. TimePMString := GetLocaleStr(PM_STR);
  226. ShortTimeFormat := GetLocaleStr(T_FMT);
  227. {$ifdef localedebug}
  228. OrgFormatSettings.ShortTimeFormat:=shorttimeformat;
  229. {$endif}
  230. TimeSeparator := FindSeparator(ShortTimeFormat, TimeSeparator);
  231. ShortTimeFormat := TransformFormatStr(ShortTimeFormat);
  232. LongTimeFormat := GetLocaleStr(T_FMT_AMPM);
  233. {$ifdef localedebug}
  234. OrgFormatSettings.LongTimeFormat:=longtimeformat;
  235. {$endif}
  236. LongTimeFormat := TransformFormatStr(LongTimeFormat);
  237. {$Ifdef BSD}
  238. plocale:=localeconv;
  239. // for these fields there is a separate BSD derived POSIX function.
  240. if not assigned(plocale) then exit; // for now.
  241. CurrencyString:=plocale^.currency_symbol; // int_CURR_SYMBOL (in latin chars)
  242. if CurrencyString='' then
  243. CurrencyString:=plocale^.int_curr_symbol;
  244. CurrencyDecimals:=ord(plocale^.FRAC_DIGITS);
  245. {$ifdef localedebug}
  246. OrgFormatSettings.CurrencyString1:=plocale^.currency_symbol;
  247. OrgFormatSettings.CurrencyString2:=plocale^.int_curr_symbol;
  248. {$endif}
  249. prec:=ord(plocale^.P_CS_PRECEDES);
  250. sep:=ord(plocale^.P_SEP_BY_SPACE);
  251. if (prec<=1) and (sep<=1) then
  252. CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  253. prec := ord(plocale^.N_CS_PRECEDES);
  254. sep := ord(plocale^.N_SEP_BY_SPACE);
  255. signp := ord(plocale^.N_SIGN_POSN);
  256. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  257. NegCurrFormat := NegFormatsTable[signp, prec, sep];
  258. //Number stuff
  259. ThousandSeparator:=plocale^.THOUSANDS_SEP[0];
  260. {$else}
  261. //Currency stuff
  262. CurrencyString := GetLocaleStr(_NL_MONETARY_CRNCYSTR);
  263. {$ifdef localedebug}
  264. OrgFormatSettings.CurrencyString1:=currencystring;
  265. OrgFormatSettings.CurrencyString2:='';
  266. {$endif}
  267. CurrencyString := Copy(CurrencyString, 2, Length(CurrencyString));
  268. CurrencyDecimals := StrToIntDef(GetLocaleStr(__FRAC_DIGITS), CurrencyDecimals);
  269. prec := byte(GetLocaleChar(__P_CS_PRECEDES));
  270. sep := byte(GetLocaleChar(__P_SEP_BY_SPACE));
  271. if (prec<=1) and (sep<=1) then
  272. CurrencyFormat := byte(not boolean(prec)) + sep shl 1;
  273. prec := byte(GetLocaleChar(__N_CS_PRECEDES));
  274. sep := byte(GetLocaleChar(__N_SEP_BY_SPACE));
  275. signp := byte(GetLocaleChar(__N_SIGN_POSN));
  276. if (signp in [0..4]) and (prec in [0, 1]) and (sep in [0, 1]) then
  277. NegCurrFormat := NegFormatsTable[signp, prec, sep];
  278. //Number stuff
  279. ThousandSeparator:=GetLocaleChar(__THOUSANDS_SEP);
  280. {$endif}
  281. DecimalSeparator:=GetLocaleChar(RADIXCHAR);
  282. end;
  283. initialization
  284. GetFormatSettings;
  285. end.