clocale.pp 8.6 KB

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