1 module i18n.culture; 2 3 private { 4 string[] _countryCodes; 5 string[string] _countryNames; 6 7 string[] _languageCodes; 8 string[string] _languageNames; 9 string[string] _languageNativeNames; 10 11 string _getLangCode(string code) { 12 import std.uni : toLower; 13 import std.algorithm.searching : canFind; 14 15 // Impossible case 16 if (code.length < 2) return null; 17 18 // C = no language set 19 if (code == "C") return code; 20 21 // Lang codes are always first, so check the 2 first code digits 22 string codecv = code[0..2].toLower(); 23 if (_languageCodes.canFind(codecv)) return codecv; 24 return null; 25 } 26 27 string _getCountryCode(string code) { 28 import std.string : indexOf; 29 import std.uni : toUpper; 30 import std.algorithm.searching : canFind; 31 32 // Maybe it's just a country code? 33 if (code.length == 2) { 34 if (_countryCodes.canFind(code.toUpper)) return null; 35 36 return code; 37 } 38 39 // Check for the 2 types of seperators 40 ptrdiff_t sep = code.indexOf("_"); 41 if (sep == -1) sep = code.indexOf("-"); 42 if (sep == -1 && code.length == 2) return code; 43 44 // No seperators, no single-country code 45 return null; 46 } 47 } 48 49 /* 50 Initialize internal culture info 51 */ 52 static this() { 53 _countryCodes = [ 54 "AD", "AE", "AF", "AG", "AI", "AL", "AM", "AO", "AQ", "AR", "AS", "AT", 55 "AU", "AW", "AX", "AZ", "BA", "BB", "BD", "BE", "BF", "BG", "BH", "BI", 56 "BJ", "BL", "BM", "BN", "BO", "BQ", "BR", "BS", "BT", "BV", "BW", "BY", 57 "BZ", "CA", "CC", "CD", "CF", "CG", "CH", "CI", "CK", "CL", "CM", "CN", 58 "CO", "CR", "CU", "CV", "CW", "CX", "CY", "CZ", "DE", "DJ", "DK", "DO", 59 "DZ", "EC", "EE", "EG", "EH", "ER", "ES", "ET", "FI", "FJ", "FK", "FM", 60 "FO", "FR", "GA", "GB", "GD", "GE", "GF", "GG", "GH", "GI", "GL", "GM", 61 "GN", "GP", "GQ", "GR", "GS", "GT", "GU", "GW", "GY", "HK", "HM", "HN", 62 "HR", "HT", "HU", "ID", "IE", "IL", "IM", "IN", "IO", "IQ", "IR", "IS", 63 "IT", "JE", "JM", "JO", "JP", "KE", "KG", "KH", "KI", "KM", "KN", "KP", 64 "KR", "KW", "KY", "KZ", "LA", "LB", "LC", "LI", "LK", "LR", "LS", "LT", 65 "LU", "LV", "LY", "MA", "MC", "MD", "ME", "MF", "MG", "MH", "MK", "ML", 66 "MM", "MN", "MO", "MP", "MQ", "MR", "MS", "MT", "MU", "MV", "MW", "MX", 67 "MY", "MZ", "NA", "NC", "NE", "NF", "NG", "NI", "NL", "NO", "NP", "NR", 68 "NU", "NZ", "OM", "PA", "PE", "PF", "PG", "PH", "PK", "PL", "PM", "PN", 69 "PR", "PS", "PT", "PW", "PY", "QA", "RE", "RO", "RS", "RU", "RW", "SA", 70 "SB", "SC", "SD", "SE", "SG", "SH", "SI", "SJ", "SK", "SL", "SM", "SN", 71 "SO", "SR", "SS", "ST", "SV", "SX", "SY", "SZ", "TC", "TD", "TF", "TG", 72 "TG", "TH", "TJ", "TK", "TL", "TM", "TN", "TO", "TR", "TT", "TV", "TW", 73 "TZ", "UA", "UG", "UM", "US", "UY", "UZ", "VA", "VC", "VE", "VG", "VI", 74 "VN", "VU", "WF", "WS", "YE", "YT", "ZA", "ZM", "ZW" 75 ]; 76 77 _countryNames = [ 78 "AD": "Andorra", 79 "AE": "United Arab Emirates", 80 "AF": "Afghanistan", 81 "AG": "Antigua and Barbuda", 82 "AI": "Anguilla", 83 "AL": "Albania", 84 "AM": "Armenia", 85 "AO": "Angola", 86 "AQ": "Antarctica", 87 "AR": "Argentina", 88 "AS": "American Samoa", 89 "AT": "Austria", 90 "AU": "Australia", 91 "AW": "Aruba", 92 "AX": "Aaland Islands", 93 "AZ": "Azerbaijan", 94 "BA": "Bosnia & Herzegovina", 95 "BB": "Barbados", 96 "BD": "Bangladesh", 97 "BE": "Belgium", 98 "BF": "Burkina Faso", 99 "BG": "Bulgaria", 100 "BH": "Bahrain", 101 "BI": "Burundi", 102 "BJ": "Benin", 103 "BL": "Saint Barthelemy", 104 "BM": "Bermuda", 105 "BN": "Brunei Darussalam", 106 "BO": "Bolivia", 107 "BQ": "Bonaire, Sint Eustatius & Saba", 108 "BR": "Brazil", 109 "BS": "Bahamas", 110 "BT": "Bhutan", 111 "BV": "Bouvet Island", 112 "BW": "Botswana", 113 "BY": "Belarus", 114 "BZ": "Belize", 115 "CA": "Canada", 116 "CC": "Cocos Islands", 117 "CD": "Democratic Republic of Congo", 118 "CF": "Central African Republic", 119 "CG": "Congo", 120 "CH": "Switzerland", 121 "CI": "Côte d'Ivoire", 122 "CK": "Cook Islands", 123 "CL": "Chile", 124 "CM": "Cameroon", 125 "CN": "China", 126 "CO": "Colombia", 127 "CR": "Costa Rica", 128 "CU": "Cuba", 129 "CV": "Cape Verde", 130 "CW": "Curaçao", 131 "CX": "Christmas Island", 132 "CY": "Cyprus", 133 "CZ": "Czech Republic", 134 "DE": "Germany", 135 "DJ": "Djibouti", 136 "DK": "Denmark", 137 "DM": "Dominica", 138 "DO": "Dominican Republic", 139 "DZ": "Algeria", 140 "EC": "Ecuador", 141 "EE": "Estonia", 142 "EG": "Egypt", 143 "EH": "Sahrawi Arab Democratic Republic", 144 "ER": "Eritrea", 145 "ES": "Spain", 146 "ET": "Ethiopia", 147 "FI": "Finland", 148 "FJ": "Fiji", 149 "FK": "Falkland Islands (Malvinas)", 150 "FM": "Federated States of Micronesia", 151 "FO": "Faroe Islands", 152 "FR": "France", 153 "GA": "Gabon", 154 "GB": "United Kingdom", 155 "GD": "Grenada", 156 "GE": "Georgia", 157 "GF": "French Guiana", 158 "GG": "Guernsey", 159 "GH": "Ghana", 160 "GI": "Gibraltar", 161 "GL": "Greenland", 162 "GM": "Gambia", 163 "GN": "Guinea", 164 "GP": "Guadaloupe", 165 "GQ": "Equatorial Guinea", 166 "GR": "Greece", 167 "GS": "South Georgia & South Sandwich Islands", 168 "GT": "Guatemala", 169 "GU": "Guam", 170 "GW": "Guinea-Bissau", 171 "GY": "Guyana", 172 "HK": "Hong Kong", 173 "HM": "Heard Island & McDonald Islands", 174 "HN": "Honduras", 175 "HR": "Croatia", 176 "HT": "Haiti", 177 "HU": "Hungary", 178 "ID": "Indonesia", 179 "IE": "Ireland", 180 "IL": "Israel", 181 "IM": "Isle of Man", 182 "IN": "India", 183 "IO": "British Indian Ocean Territory", 184 "IQ": "Iraq", 185 "IR": "Islamic Republic of Iran", 186 "IS": "Iceland", 187 "IT": "Italy", 188 "JE": "Jersey", 189 "JM": "Jamaica", 190 "JO": "Jordan", 191 "JP": "Japan", 192 "KE": "Kenya", 193 "KG": "Kyrgyzstan", 194 "KH": "Cambodia", 195 "KI": "Kiribati", 196 "KM": "Comoros", 197 "KN": "Saint Kitts & Nevis", 198 "KP": "Democratic People's Republic of Korea", 199 "KR": "Republic of Korea", 200 "KW": "Kuwait", 201 "KY": "Cayman Islands", 202 "KZ": "Kazakhstan", 203 "LA": "Lao People's Democratic Republic", 204 "LB": "Lebalon", 205 "LC": "Saint Lucia", 206 "LI": "Lichtenstein", 207 "LK": "Sri Lanka", 208 "LR": "Liberia", 209 "LS": "Lesotho", 210 "LT": "Lithuania", 211 "LU": "Luxembourg", 212 "LV": "Latvia", 213 "LY": "Libya", 214 "MA": "Morocco", 215 "MC": "Monaco", 216 "MD": "Republic of Moldova", 217 "ME": "Montenegro", 218 "MF": "Saint Martin (French part)", 219 "MG": "Madagascar", 220 "MH": "Marshall Islands", 221 "MK": "North Macedonia", 222 "ML": "Mali", 223 "MM": "Myanmar", 224 "MN": "Mongolia", 225 "MO": "Macao", 226 "MP": "Northen Marina Islands", 227 "MQ": "Martinique", 228 "MR": "Mauritania", 229 "MS": "Montserrat", 230 "MT": "Malta", 231 "MU": "Mauritius", 232 "MV": "Maldives", 233 "MW": "Malawi", 234 "MX": "Mexico", 235 "MY": "Malaysia", 236 "MZ": "Mozambique", 237 "NA": "Namibia", 238 "NC": "New Caledonia", 239 "NE": "Niger", 240 "NF": "Norfolk Island", 241 "NG": "Nigeria", 242 "NI": "Nicaragua", 243 "NL": "Netherlands", 244 "NO": "Norway", 245 "NP": "Nepal", 246 "NR": "Nauru", 247 "NU": "Niue", 248 "NZ": "New Zealand", 249 "OM": "Oman", 250 "PA": "Panama", 251 "PE": "Peru", 252 "PF": "French Polynesia", 253 "PG": "Papua New Guinea", 254 "PH": "Phillipines", 255 "PK": "Pakistan", 256 "PL": "Poland", 257 "PM": "Saint Pierre & Miquelon", 258 "PN": "Pitcairn", 259 "PR": "Puerto Rico", 260 "PS": "Palestine", 261 "PT": "Portugal", 262 "PW": "Palau", 263 "PY": "Paraguay", 264 "QA": "Qatar", 265 "RE": "Reunion", 266 "RO": "Romania", 267 "RS": "Serbia", 268 "RU": "Russian Federation", 269 "RW": "Rwanda", 270 "SA": "Saudi Arabia", 271 "SB": "Solomon Islands", 272 "SC": "Seychelles", 273 "SD": "Sudan", 274 "SE": "Sweden", 275 "SG": "Singapore", 276 "SH": "Saint Helena, Ascension & Tristan da Cunha", 277 "SI": "Slovenia", 278 "SJ": "Svalbard & Jan Mayen", 279 "SK": "Slovakia", 280 "SL": "Sierra Leone", 281 "SM": "San Marino", 282 "SN": "Senegal", 283 "SO": "Somalia", 284 "SR": "Suriname", 285 "SS": "South Sudan", 286 "ST": "Sao Tome & Principe", 287 "SV": "El Salvador", 288 "SX": "Sint Maarten (Dutch part)", 289 "SY": "Syrian Arab Republic", 290 "SZ": "Swaziland", 291 "TC": "Turks & Caicos Islands", 292 "TD": "Chad", 293 "TF": "French Southern Territories", 294 "TG": "Togo", 295 "TH": "Thailand", 296 "TJ": "Tajikistan", 297 "TK": "Tokelau", 298 "TL": "Timor-Leste", 299 "TM": "Turkmenistan", 300 "TN": "Tunisia", 301 "TO": "Tonga", 302 "TR": "Turkey", 303 "TT": "Trinidad & Tobago", 304 "TV": "Tuvalu", 305 "TW": "Taiwan", 306 "TZ": "United Republic of Tanzania", 307 "UA": "Ukraine", 308 "UG": "Uganda", 309 "UM": "United States Minor Outlying Islands", 310 "US": "United States", 311 "UY": "Uruguay", 312 "UZ": "Uzbekistan", 313 "VA": "Holy See (Vatican City State)", 314 "VC": "Saint Vincent & Grenadines", 315 "VE": "Bolivarian Republic of Venezuela", 316 "VG": "British Virgin Islands", 317 "VI": "US Virgin Islands", 318 "VN": "Vietnam", 319 "VU": "Vanuatu", 320 "WF": "Wallis & Futuna", 321 "WS": "Samoa", 322 "YE": "Yemen", 323 "YT": "Mayotte", 324 "ZA": "South Africa", 325 "ZM": "Zambia", 326 "ZW": "Zimbabwe" 327 ]; 328 329 _languageCodes = [ 330 "aa", "ab", "ae", "af", "ak", "am", "an", "ar", "as", "av", "ay", "az", 331 "ba", "be", "bg", "bh", "bi", "bm", "bn", "bo", "br", "bs", "ca", "ce", 332 "ch", "co", "cr", "cs", "cu", "cv", "cy", "da", "de", "dv", "dz", "ee", 333 "el", "en", "eo", "es", "et", "eu", "fa", "ff", "fi", "fj", "fo", "fr", 334 "fy", "ga", "gd", "gl", "gn", "gu", "gv", "ha", "he", "hi", "ho", "hr", 335 "ht", "hu", "hy", "hz", "ia", "id", "ie", "ig", "ii", "ik", "io", "is", 336 "it", "iu", "ja", "jv", "ka", "kg", "ki", "kj", "kk", "kl", "km", "kn", 337 "ko", "kr", "ks", "ku", "kv", "kw", "ky", "la", "lb", "lg", "li", "ln", 338 "lo", "lt", "lu", "lv", "mg", "mh", "mi", "mk", "ml", "mn", "mr", "ms", 339 "mt", "my", "na", "nb", "nd", "ne", "ng", "nl", "nn", "no", "nr", "nv", 340 "ny", "oc", "oj", "om", "or", "os", "pa", "pi", "pl", "ps", "pt", "qu", 341 "rm", "rn", "ro", "ru", "rw", "sa", "sc", "sd", "se", "sg", "si", "sk", 342 "sl", "sm", "sn", "so", "sq", "sr", "ss", "st", "su", "sv", "sw", "ta", 343 "te", "tg", "th", "ti", "tk", "tl", "tn", "to", "tr", "ts", "tt", "tw", 344 "ty", "ug", "uk", "ur", "uz", "ve", "vi", "vo", "wa", "wo", "xh", "yi", 345 "yo", "za", "zh", "zu" 346 ]; 347 348 _languageNames = [ 349 "aa": "Afar", 350 "ab": "Abkahzian", 351 "ae": "Avestan", 352 "af": "Afrikaans", 353 "ak": "Akan", 354 "am": "Amharic", 355 "an": "Aragonese", 356 "ar": "Arabic", 357 "as": "Assamese", 358 "av": "Avaric", 359 "ay": "Aymara", 360 "az": "Azerbaijani", 361 "ba": "Bashkir", 362 "be": "Belarusian", 363 "bg": "Bulgarian", 364 "bh": "Bihari", 365 "bi": "Bislama", 366 "bm": "Bambara", 367 "bn": "Bengali", 368 "bo": "Tibetan", 369 "br": "Breton", 370 "bs": "Bosnian", 371 "ca": "Catlan", 372 "ce": "Chechen", 373 "ch": "Chamorro", 374 "co": "Corsican", 375 "cr": "Cree", 376 "cs": "Czech", 377 "cu": "Church Slavic", 378 "cv": "Chuvash", 379 "cy": "Welsh", 380 "da": "Danish", 381 "de": "German", 382 "dv": "Divehi", 383 "dz": "Dzongkha", 384 "ee": "Ewe", 385 "el": "Greek", 386 "en": "English", 387 "eo": "Esperanto", 388 "es": "Spanish", 389 "et": "Estonian", 390 "eu": "Basque", 391 "fa": "Persian", 392 "ff": "Fulah", 393 "fi": "Finnish", 394 "fj": "Fijian", 395 "fo": "Faroese", 396 "fr": "French", 397 "fy": "Western Frisian", 398 "ga": "Irish", 399 "gd": "Gaelic", 400 "gl": "Galacian", 401 "gn": "Guarani", 402 "gu": "Gujarati", 403 "gv": "Manx", 404 "ha": "Hausa", 405 "he": "Hebrew", 406 "hi": "Hindi", 407 "ho": "Hiri Motu", 408 "hr": "Croatian", 409 "ht": "Haitian", 410 "hu": "Hungarian", 411 "hy": "Armenian", 412 "hz": "Herero", 413 "ia": "Interlingua", 414 "id": "Indonesian", 415 "ie": "Interlingue", 416 "ig": "Igbo", 417 "ii": "Sichuan Yi", 418 "ik": "Inupiak", 419 "io": "Ido", 420 "is": "Icelandic", 421 "it": "Italian", 422 "iu": "Inuktitut", 423 "ja": "Japanese", 424 "jv": "Javanese", 425 "ka": "Georgian", 426 "kg": "Kongo", 427 "ki": "Kikuyu", 428 "kj": "Kuanyama", 429 "kk": "Kazakh", 430 "kl": "Greenlandic", 431 "km": "Central Khmer", 432 "kn": "Kannada", 433 "ko": "Korean", 434 "kr": "Kanuri", 435 "ks": "Kashmiri", 436 "ku": "Kurdish", 437 "kv": "Komi", 438 "kw": "Cornish", 439 "ky": "Kirghiz", 440 "la": "Latin", 441 "lb": "Luxembourgish", 442 "lg": "Ganda", 443 "li": "Limburgish", 444 "ln": "Lingala", 445 "lo": "Lao", 446 "lt": "Lithuanian", 447 "lu": "Luba-Katanga", 448 "lv": "Latvian", 449 "mg": "Malagasy", 450 "mh": "Marshallese", 451 "mi": "Maori", 452 "mk": "Macedonian", 453 "ml": "Malayalam", 454 "mn": "Mongolian", 455 "mr": "Marathi", 456 "ms": "Malay", 457 "mt": "Maltese", 458 "my": "Burmese", 459 "na": "Nauru", 460 "nb": "Norwegian Bokmål", 461 "nd": "North Ndebele", 462 "ne": "Nepali", 463 "ng": "Ndonga", 464 "nl": "Dutch", 465 "nn": "Norwegian Nynorsk", 466 "no": "Norwegian", 467 "nr": "South Ndebele", 468 "nv": "Navajo", 469 "ny": "Chichewa", 470 "oc": "Occitan", 471 "oj": "Ojibwa", 472 "om": "Omoro", 473 "or": "Oriya", 474 "os": "Ossetian", 475 "pa": "Panjabi", 476 "pi": "Pali", 477 "pl": "Polish", 478 "ps": "Pushto", 479 "pt": "Portugese", 480 "qu": "Quechua", 481 "rm": "Romansh", 482 "rn": "Rundi", 483 "ro": "Romanian", 484 "ru": "Russian", 485 "rw": "Kinyarwanda", 486 "sa": "Sanskrit", 487 "sc": "Sardinian", 488 "sd": "Sindhi", 489 "se": "Northen Sami", 490 "sg": "Sango", 491 "si": "Sinhala", 492 "sk": "Slovak", 493 "sl": "Slovenian", 494 "sm": "Samoan", 495 "sn": "Shona", 496 "so": "Somali", 497 "sq": "Albanian", 498 "sr": "Serbian", 499 "ss": "Swati", 500 "st": "Southern Sotho", 501 "su": "Sundanese", 502 "sv": "Swedish", 503 "sw": "Swahili", 504 "ta": "Tamil", 505 "te": "Tegulu", 506 "tg": "Tajik", 507 "th": "Thai", 508 "ti": "Tigrinya", 509 "tk": "Turkmen", 510 "tl": "Tagalog", 511 "tn": "Tswana", 512 "to": "Tonga", 513 "tr": "Turkish", 514 "ts": "Tsonga", 515 "tt": "Tatar", 516 "tw": "Twi", 517 "ty": "Tahitian", 518 "ug": "Ughur", 519 "uk": "Ukranian", 520 "ur": "Urdu", 521 "uz": "Uzbek", 522 "ve": "Venda", 523 "vi": "Vietnamese", 524 "vo": "Volapük", 525 "wa": "Walloon", 526 "wo": "Wolof", 527 "xh": "Xhosa", 528 "yi": "Yiddish", 529 "yo": "Yoruba", 530 "za": "Zhuang", 531 "zh": "Chinese", 532 "zu": "Zulu" 533 ]; 534 535 // _languageNativeNames = [ 536 // ]; 537 538 // Set to user preferred locale 539 i18nResetLocale(); 540 } 541 542 /** 543 Validates a country code 544 545 Returns true if the code is a valid culture code 546 Returns false if the code is not a valid culture code 547 548 TODO: Validate whether encoding spec is correct 549 */ 550 bool i18nValidateCultureCode(bool caseSensitive = true)(string code) { 551 552 // Special case, C = no language set. 553 if (code == "C") return true; 554 555 // Make sure we don't crash when handling lang/country get 556 // by escaping early if we have too few characters to work with 557 if (code.length < 2) return false; 558 559 import std.uni : toUpper, toLower; 560 import std.algorithm.searching : canFind; 561 string lang = code[0..2]; 562 string country = code.length >= 5 ? code[3..5] : ""; 563 564 if (code.length == 2) { 565 // Language code only 566 567 // The main validity test 568 static if (!caseSensitive) return _languageCodes.canFind(lang.toLower); 569 else return _languageCodes.canFind(lang); 570 571 } else if (code.length >= 5) { 572 // Country AND language code 573 574 // Make sure we either use a MS or gettext seperator 575 if (!(code[2] == '-' || code[2] == '_')) return false; 576 577 // The main validity test 578 static if (!caseSensitive) return _languageCodes.canFind(lang.toLower) && _countryCodes.canFind(country.toUpper); 579 else return _languageCodes.canFind(lang) && _countryCodes.canFind(country); 580 581 } 582 return false; 583 } 584 585 @("i18nValidateCultureCode Case Sensitive") 586 unittest { 587 assert(i18nValidateCultureCode("da")); // Should succeed 588 assert(i18nValidateCultureCode("da_DK")); // Should succeed 589 assert(i18nValidateCultureCode("de-DE")); // Should succeed 590 591 assert(!i18nValidateCultureCode("daDK")); // Should fail 592 assert(!i18nValidateCultureCode("daaaaaDK")); // Should fail 593 assert(!i18nValidateCultureCode("da/DK")); // Should fail 594 assert(!i18nValidateCultureCode("zz_CH")); // Should fail 595 assert(!i18nValidateCultureCode("ch_ZZ")); // Should fail 596 } 597 598 @("i18nValidateCultureCode Case Insensitive") 599 unittest { 600 assert(i18nValidateCultureCode!false("da")); // Should succeed 601 assert(i18nValidateCultureCode!false("da_dk")); // Should succeed 602 assert(i18nValidateCultureCode!false("da_DK")); // Should succeed 603 assert(i18nValidateCultureCode!false("en-US")); // Should succeed 604 assert(i18nValidateCultureCode!false("en-us")); // Should succeed 605 606 assert(!i18nValidateCultureCode!false("daDK")); // Should fail 607 assert(!i18nValidateCultureCode!false("daaaaaDK")); // Should fail 608 assert(!i18nValidateCultureCode!false("da/DK")); // Should fail 609 assert(!i18nValidateCultureCode!false("zz_CH")); // Should fail 610 assert(!i18nValidateCultureCode!false("ch_ZZ")); // Should fail 611 } 612 613 /** 614 Gets the (english) name of the language associated with a culture code 615 616 Returns null if code is invalid 617 */ 618 string i18nGetCultureLanguage(string code) { 619 if (!i18nValidateCultureCode(code)) return null; 620 if (code[0..2] !in _languageNames) return null; 621 return _languageNames[code[0..2]]; 622 } 623 624 @("i18nGetCultureLanguage") 625 unittest { 626 assert(i18nGetCultureLanguage("da") == "Danish"); 627 assert(i18nGetCultureLanguage("de") == "German"); 628 assert(i18nGetCultureLanguage("en") == "English"); 629 assert(i18nGetCultureLanguage("ja") == "Japanese"); 630 } 631 632 /** 633 Gets the country associated with a culture code 634 */ 635 string i18nGetCultureCountry(string code) { 636 if (!i18nValidateCultureCode(code)) return null; 637 if (code.length < 5) return null; 638 if (code[$-2..5] !in _countryNames) return null; 639 return _countryNames[code[5-2..5]]; 640 } 641 642 @("i18nGetCultureCountry") 643 unittest { 644 assert(i18nGetCultureCountry("da_DK") == "Denmark"); 645 assert(i18nGetCultureCountry("en_US") == "United States"); 646 assert(i18nGetCultureCountry("en_GB") == "United Kingdom"); 647 assert(i18nGetCultureCountry("de_DE") == "Germany"); 648 assert(i18nGetCultureCountry("ja_JP") == "Japan"); 649 } 650 651 /** 652 Gets the current locale. 653 */ 654 string i18nGetLocale() { 655 import core.stdc.locale : setlocale, LC_ALL; 656 import std.string : fromStringz; 657 string locale = cast(string)setlocale(LC_ALL, null).fromStringz.idup; 658 659 // TODO: Implement 660 return locale; 661 } 662 663 @("i18nGetLocale") 664 unittest { 665 assert(i18nValidateCultureCode(i18nGetLocale())); 666 } 667 668 /** 669 Sets the locale for the app. 670 */ 671 void i18nSetLocale(string locale) { 672 import core.stdc.locale : setlocale, LC_ALL; 673 import core.stdc.string : memcpy; 674 import core.stdc.stdlib : malloc; 675 import std.string : toStringz, fromStringz; 676 677 if (locale == "C") { 678 setlocale(LC_ALL, "C"); 679 } else if (locale == "") { 680 setlocale(LC_ALL, ""); 681 } else setlocale(LC_ALL, (locale).toStringz); 682 } 683 684 void i18nResetLocale() { 685 import core.stdc.locale : setlocale, LC_ALL; 686 setlocale(LC_ALL, ""); 687 } 688 689 struct LocaleConv { 690 string decimalPoint; 691 string thousandSep; 692 string grouping; 693 string intCurrSymbol; 694 string currencySymbol; 695 string monDecimalPoint; 696 string monThousandsSep; 697 string monGrouping; 698 string positiveSign; 699 string negativeSign; 700 byte intFracDigits; 701 byte fracDigits; 702 byte pCSPrecedes; 703 byte pSepBySpace; 704 byte nCSPrecedes; 705 byte nSepBySpace; 706 byte pSignPosN; 707 byte nSignPosN; 708 byte intPCSPrecedes; 709 byte intP_sep_by_space; 710 byte intNCSPrecedes; 711 byte intNSepBySpace; 712 byte intPSignPosN; 713 byte intNSignPosN; 714 } 715 716 /** 717 Returns the locale's conversion units. 718 */ 719 LocaleConv i18nGetLocaleConversions() { 720 import core.stdc.locale : localeconv, lconv; 721 import std.string : fromStringz; 722 lconv* conv = localeconv(); 723 return LocaleConv( 724 cast(string)conv.decimal_point.fromStringz, 725 cast(string)conv.thousands_sep.fromStringz, 726 cast(string)conv.grouping.fromStringz, 727 cast(string)conv.int_curr_symbol.fromStringz, 728 cast(string)conv.currency_symbol.fromStringz, 729 cast(string)conv.mon_decimal_point.fromStringz, 730 cast(string)conv.mon_thousands_sep.fromStringz, 731 cast(string)conv.mon_grouping.fromStringz, 732 cast(string)conv.positive_sign.fromStringz, 733 cast(string)conv.negative_sign.fromStringz, 734 conv.int_frac_digits, 735 conv.frac_digits, 736 conv.p_cs_precedes, 737 conv.p_sep_by_space, 738 conv.n_cs_precedes, 739 conv.n_sep_by_space, 740 conv.p_sign_posn, 741 conv.n_sign_posn, 742 conv.int_p_cs_precedes, 743 conv.int_p_sep_by_space, 744 conv.int_n_cs_precedes, 745 conv.int_n_sep_by_space, 746 conv.int_p_sign_posn, 747 conv.int_n_sign_posn 748 ); 749 }