PageRenderTime 42ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 0ms

/src/dwin/nls.d

http://github.com/simendsjo/dwin
D | 514 lines | 334 code | 85 blank | 95 comment | 16 complexity | 7d90ddd9a66092dc97473a6f7205331c MD5 | raw file
  1. /++
  2. National Language Support
  3. Windows globalization features
  4. Bugs:
  5. Code Page 20949 and 1147 is skipped as GetCPInfoEx fails for these code pages
  6. See_Also:
  7. $(UL
  8. $(LI $(MSDN dd319078.aspx, National Language Support))
  9. $(LI $(MSDN dd318140(v=VS.85).aspx, Globalization Services))
  10. $(LI $(MSDN dd317752(v=VS.85).aspx, Code Pages))
  11. )
  12. Macros:
  13. CP_LIST = $(MSDN dd317756(v=vs.85).aspx, Code Page Identifiers)
  14. CPINFO = $(MSDN dd317781(VS.85).aspx, CPINFOEX Structure)
  15. License: $(BOOST)
  16. Authors: $(SIMENDSJO)
  17. Source: $(MODULE_SOURCE_LINK)
  18. ++/
  19. module dwin.nls;
  20. private import std.conv;
  21. private import std.utf;
  22. private import std.algorithm;
  23. private import std.array;
  24. debug private import std.stdio;
  25. private import std.exception;
  26. private import std.traits;
  27. import dwin.core;
  28. import dwin.error;
  29. private import win32.winnt;
  30. private import win32.winnls;
  31. private import win32.winbase;
  32. // Missing translated functions
  33. pragma(lib, "kernel32.lib");
  34. extern(Windows) {
  35. LCID LocaleNameToLCID(LPCWSTR, DWORD);
  36. BOOL IsValidLocaleName(LPCWSTR);
  37. alias BOOL function(LPWSTR, DWORD, LPARAM) LOCALE_ENUMPROCEX;
  38. BOOL EnumSystemLocalesEx(LOCALE_ENUMPROCEX, DWORD, LPARAM, LPVOID);
  39. int GetLocaleInfoEx(LPCWSTR, LCTYPE, LPWSTR, int);
  40. int LCIDToLocaleName(LCID, LPWSTR, int, DWORD);
  41. }
  42. enum CP_INSTALLED = 0x00000001;
  43. enum CP_SUPPORTED = 0x00000002;
  44. enum LOCALE_ALLOW_NEUTRAL_NAMES = 0x08000000;
  45. enum LOCALE_ALL = 0;
  46. enum LOCALE_INVARIANT = MAKELCID(MAKELANGID(LANG_INVARIANT, SUBLANG_NEUTRAL), SORT_DEFAULT);
  47. enum LOCALE_SLOCALIZEDDISPLAYNAME = 0x00000002;
  48. enum LOCALE_SLOCALIZEDLANGUAGENAME = 0x0000006F;
  49. enum LOCALE_SLOCALIZEDCOUNTRYNAME = 0x00000006;
  50. enum LOCALE_SENGLISHDISPLAYNAME = 0x00000072;
  51. enum LOCALE_SENGLISHLANGUAGENAME = 0x00001001;
  52. enum LOCALE_SENGLISHCOUNTRYNAME = 0x00001002;
  53. enum LOCALE_SPERCENT = 0x00000076;
  54. enum LOCALE_SPERMILLE = 0x00000077;
  55. enum LOCALE_SSHORTTIME = 0x00000079;
  56. enum LOCALE_SSORTLOCALE = 0x0000007B;
  57. enum LOCALE_SPOSINFINITY = 0x0000006A;
  58. enum LOCALE_SNEGINFINITY = 0x0000006B;
  59. enum LOCALE_SNATIVEDISPLAYNAME = 0x00000073;
  60. enum LOCALE_SNATIVELANGUAGENAME = 0x00000004;
  61. enum LOCALE_SNATIVECOUNTRYNAME = 0x00000008;
  62. enum LOCALE_SISO639LANGNAME2 = 0x00000067;
  63. enum LOCALE_SISO3166CTRYNAME2 = 0x00000068;
  64. enum LOCALE_SNAN = 0x00000069;
  65. enum LOCALE_SNEGATIVESIGN = 0x00000051;
  66. // End translation
  67. /++
  68. ++/
  69. struct CodePage {
  70. immutable uint handle;
  71. private CPINFOEX _info;
  72. /// See_Also: $(CPINFO)
  73. @property immutable(CPINFOEX) info() {
  74. if(!_info.CodePage) {
  75. winEnforce(GetCPInfoEx(handle, 0/*reserved*/, cast(CPINFOEX*)&_info));
  76. }
  77. return _info;
  78. }
  79. /// See_Also: $(CP_LIST)
  80. this(uint codePage) {
  81. handle = codePage;
  82. }
  83. /// See_Also: $(CPINFO)
  84. @property uint maxCharSize() {
  85. return info.MaxCharSize;
  86. }
  87. /// ditto
  88. @property immutable(TCHAR)[] name() {
  89. int i;
  90. for(; i < info.CodePageName.length; ++i) {
  91. if(info.CodePageName[i] == '\0') break;
  92. }
  93. return info.CodePageName[0..i];
  94. }
  95. /// ditto
  96. @property TCHAR unicodeDefaultChar() {
  97. return info.UnicodeDefaultChar;
  98. }
  99. /// ditto
  100. @property ubyte[2] defaultChar() {
  101. return info.DefaultChar;
  102. }
  103. /// ditto
  104. @property ubyte[12] leadByte() {
  105. return info.LeadByte;
  106. }
  107. /// UTF-8 Code Page
  108. static @property CodePage utf8() {
  109. return CodePage(65001);
  110. }
  111. /++
  112. See_Also: $(MSDN dd317825(v=VS.85).aspx, EnumSystemCodePages)
  113. Returns: code pages on the system
  114. ++/
  115. static @property CodePage[] installed() {
  116. return getCodePages(CP_INSTALLED);
  117. }
  118. /// ditto
  119. static @property CodePage[] supported() {
  120. return getCodePages(CP_SUPPORTED);
  121. }
  122. /// ditto
  123. private static CodePage[] getCodePages(DWORD flags) {
  124. static CodePage[] result;
  125. if(!result.length)
  126. result.reserve(150);
  127. else
  128. result.clear();
  129. extern(Windows) static BOOL getNext(LPTSTR codePage) {
  130. immutable len = lstrlen(codePage);
  131. assert(len);
  132. uint codePageId = to!uint(codePage[0..len]);
  133. if(codePageId != 20949 && codePageId != 1147)
  134. result ~= CodePage(codePageId);
  135. return true;
  136. }
  137. winEnforce(EnumSystemCodePages(&getNext, flags));
  138. return result;
  139. }
  140. string toString() {
  141. return to!string(handle);
  142. }
  143. }
  144. /++
  145. ++/
  146. struct Language {
  147. immutable WORD handle;
  148. invariant() {
  149. assert(handle);
  150. }
  151. this(WORD handle) {
  152. this.handle = handle;
  153. }
  154. this(USHORT primary, USHORT sub) {
  155. handle = MAKELANGID(primary, sub);
  156. }
  157. @property WORD primary() immutable {
  158. return PRIMARYLANGID(handle);
  159. }
  160. @property WORD sub() immutable {
  161. return SUBLANGID(handle);
  162. }
  163. static @property Language neutral() {
  164. return Language(LANG_NEUTRAL, SUBLANG_NEUTRAL);
  165. }
  166. static @property Language userDefault() {
  167. return Language(LANG_NEUTRAL, SUBLANG_DEFAULT);
  168. }
  169. static @property Language systemDefault() {
  170. return Language(LANG_NEUTRAL, SUBLANG_SYS_DEFAULT);
  171. }
  172. static @property Language customDefault() {
  173. return Language(LANG_NEUTRAL, SUBLANG_CUSTOM_DEFAULT);
  174. }
  175. static @property Language customUnspecifiedDefault() {
  176. return Language(LANG_NEUTRAL, SUBLANG_CUSTOM_UNSPECIFIED);
  177. }
  178. static @property Language customMultilingualUserInterfaceDefault() {
  179. return Language(LANG_NEUTRAL, SUBLANG_UI_CUSTOM_DEFAULT);
  180. }
  181. }
  182. /++
  183. ++/
  184. struct SortOrder {
  185. immutable WORD handle;
  186. invariant() {
  187. assert(handle);
  188. }
  189. this(WORD sortId) {
  190. handle = sortId;
  191. }
  192. }
  193. /++
  194. ++/
  195. struct Locale {
  196. immutable LCID handle;
  197. private immutable(TCHAR)[] _name;
  198. invariant() {
  199. assert(handle);
  200. }
  201. ///
  202. this(LCID localeId) {
  203. handle = localeId;
  204. }
  205. ///
  206. this(WORD languageId, WORD sortId) {
  207. handle = MAKELCID(languageId, sortId);
  208. }
  209. ///
  210. this(Language language, SortOrder sort) {
  211. handle = MAKELCID(language.handle, sort.handle);
  212. }
  213. // FIXME: templated instead of TCHAR[], some bug prevents me..
  214. this(const TCHAR[] name, bool allowNeutral=true) {
  215. auto namez = toWinStringz(name);
  216. if(!IsValidLocaleName(namez))
  217. throw new Win32Exception("Unknown locale " ~ name);
  218. _name = to!(typeof(_name))(name);
  219. DWORD flags = allowNeutral ? LOCALE_ALLOW_NEUTRAL_NAMES : 0;
  220. handle = winEnforce(LocaleNameToLCID(namez, flags));
  221. }
  222. ///
  223. @property Language language() const {
  224. return Language(LANGIDFROMLCID(handle));
  225. }
  226. ///
  227. @property SortOrder sortOrder() const {
  228. return SortOrder(SORTIDFROMLCID(handle));
  229. }
  230. ///
  231. @property static Locale threadLocale() {
  232. return Locale(GetThreadLocale());
  233. }
  234. ///
  235. @property static void threadLocale(Locale locale) {
  236. SetThreadLocale(locale.handle);
  237. }
  238. ///
  239. @property static void threadLocale(const TCHAR[] localeName) {
  240. threadLocale = Locale(localeName);
  241. }
  242. ///
  243. @property immutable(TCHAR[]) name()
  244. out(result) {
  245. assert(isValidLocale(result));
  246. assert(result == _name);
  247. } body {
  248. if(!_name) {
  249. auto len = LCIDToLocaleName(handle, null, 0, LOCALE_ALLOW_NEUTRAL_NAMES) - 1 /* exclude \0 */;
  250. if(!len) {
  251. assert(handle == 127,
  252. "Non-invariant locale, "~to!string(handle)~", is missing locale name");
  253. _name = ""w; // idup doesn't append \0, so the string is still null
  254. } else {
  255. assert(len > 0);
  256. auto buf = new TCHAR[len];
  257. winEnforce(LCIDToLocaleName(handle, buf.ptr, len, LOCALE_ALLOW_NEUTRAL_NAMES));
  258. _name = assumeUnique(buf);
  259. }
  260. }
  261. assert(_name);
  262. return _name;
  263. }
  264. ///
  265. private immutable(TCHAR[]) getInfo(LCTYPE request) {
  266. auto namez = toWinStringz(name);
  267. immutable len = winEnforce(GetLocaleInfoEx(namez, request, null, 0));
  268. auto buf = new TCHAR[len];
  269. winEnforce(GetLocaleInfoEx(namez, request, buf.ptr, len));
  270. return assumeUnique(buf[0..len-1/*exclude \0*/]);
  271. }
  272. ///
  273. @property immutable(TCHAR[]) localizedDisplayName() {
  274. return getInfo(LOCALE_SLOCALIZEDDISPLAYNAME);
  275. }
  276. ///
  277. @property immutable(TCHAR[]) localizedCountryName() {
  278. return getInfo(LOCALE_SLOCALIZEDCOUNTRYNAME);
  279. }
  280. ///
  281. @property immutable(TCHAR[]) localizedLanguageName() {
  282. return getInfo(LOCALE_SLOCALIZEDLANGUAGENAME);
  283. }
  284. ///
  285. @property immutable(TCHAR[]) nativeDisplayName() {
  286. return getInfo(LOCALE_SNATIVEDISPLAYNAME);
  287. }
  288. ///
  289. @property immutable(TCHAR[]) nativeCountryName() {
  290. return getInfo(LOCALE_SNATIVECOUNTRYNAME);
  291. }
  292. ///
  293. @property immutable(TCHAR[]) nativeLanguageName() {
  294. return getInfo(LOCALE_SNATIVELANGUAGENAME);
  295. }
  296. ///
  297. @property immutable(TCHAR[]) displayName() {
  298. return getInfo(LOCALE_SENGLISHDISPLAYNAME);
  299. }
  300. ///
  301. @property immutable(TCHAR[]) countryName() {
  302. return getInfo(LOCALE_SENGLISHCOUNTRYNAME);
  303. }
  304. ///
  305. @property immutable(TCHAR[]) languageName() {
  306. return getInfo(LOCALE_SENGLISHLANGUAGENAME);
  307. }
  308. ///
  309. static bool isValidLocale(C)(C[] locale) if(isSomeChar!C) {
  310. return cast(bool)IsValidLocaleName(toWinStringz(locale));
  311. }
  312. ///
  313. @property static Locale[] systemLocales() {
  314. return Locale.getSystemLocales(LOCALE_ALL);
  315. }
  316. ///
  317. static Locale[] getSystemLocales(DWORD flags) {
  318. extern(Windows) static BOOL getNext(LPWSTR localeName, DWORD flags, LPARAM param) {
  319. auto locales = cast(Locale[]*)param;
  320. (*locales) ~= Locale(fromWinStringz(localeName));
  321. return true;
  322. }
  323. Locale[] locales;
  324. locales.reserve(512);
  325. winEnforce(EnumSystemLocalesEx(&getNext, flags, cast(LPARAM)&locales, null/*reserved*/));
  326. return locales;
  327. }
  328. ///
  329. @property static Locale invariantLocale() {
  330. return Locale(LOCALE_INVARIANT);
  331. }
  332. ///
  333. @property static Locale systemDefault() {
  334. return Locale(LOCALE_SYSTEM_DEFAULT);
  335. }
  336. ///
  337. @property immutable(TCHAR[]) percentSymbol() {
  338. return getInfo(LOCALE_SPERCENT);
  339. }
  340. ///
  341. @property immutable(TCHAR[]) permilleSymbol() {
  342. return getInfo(LOCALE_SPERMILLE);
  343. }
  344. ///
  345. @property immutable(TCHAR[]) decimalSymbol() {
  346. return getInfo(LOCALE_SDECIMAL);
  347. }
  348. ///
  349. @property immutable(TCHAR[]) listSeparatorSymbol() {
  350. return getInfo(LOCALE_SLIST);
  351. }
  352. ///
  353. @property immutable(TCHAR[]) currencySymbol() {
  354. return getInfo(LOCALE_SCURRENCY);
  355. }
  356. ///
  357. @property immutable(TCHAR[]) monetaryDecimalSymbol() {
  358. return getInfo(LOCALE_SMONDECIMALSEP);
  359. }
  360. // FIXME!
  361. ///
  362. @property immutable(TCHAR[]) monetaryDecimalGrouping() {
  363. return getInfo(LOCALE_SMONGROUPING);
  364. }
  365. ///
  366. @property immutable(TCHAR[]) monetaryThousandSeparatorSymbol() {
  367. return getInfo(LOCALE_SMONTHOUSANDSEP);
  368. }
  369. ///
  370. @property immutable(TCHAR[]) isoCountry() {
  371. return getInfo(LOCALE_SISO3166CTRYNAME2);
  372. }
  373. ///
  374. @property immutable(TCHAR[]) isoLanguage() {
  375. return getInfo(LOCALE_SISO639LANGNAME2);
  376. }
  377. ///
  378. @property immutable(TCHAR[]) shortTime() {
  379. return shortTimes[0];
  380. }
  381. ///
  382. @property immutable(TCHAR[][]) shortTimes() {
  383. // HACK: splitter, array etc doesn't work well with immutable values
  384. // yet, so lets cast away
  385. immutable sep = ';';
  386. auto times = cast(TCHAR[])getInfo(LOCALE_SSHORTTIME);
  387. auto splitted = array(splitter(times, sep));
  388. return assumeUnique(splitted);
  389. }
  390. ///
  391. @property immutable(TCHAR[]) longDate() {
  392. return getInfo(LOCALE_SLONGDATE);
  393. }
  394. ///
  395. @property immutable(TCHAR[]) amSymbol() {
  396. return getInfo(LOCALE_S1159);
  397. }
  398. ///
  399. @property immutable(TCHAR[]) pmSymbol() {
  400. return getInfo(LOCALE_S2359);
  401. }
  402. ///
  403. @property Locale sortLocale() {
  404. return Locale(getInfo(LOCALE_SSORTLOCALE));
  405. }
  406. ///
  407. @property immutable(TCHAR[]) posInfinitySymbol() {
  408. return getInfo(LOCALE_SPOSINFINITY);
  409. }
  410. ///
  411. @property immutable(TCHAR[]) negInfinitySymbol() {
  412. return getInfo(LOCALE_SNEGINFINITY);
  413. }
  414. ///
  415. @property immutable(TCHAR[]) nanSymbol() {
  416. return getInfo(LOCALE_SNAN);
  417. }
  418. ///
  419. @property immutable(TCHAR[]) negativeSymbol() {
  420. return getInfo(LOCALE_SNEGATIVESIGN);
  421. }
  422. }
  423. unittest {
  424. }