use winapi::um::winnls::{GetLocaleInfoEx, GetUserDefaultLocaleName, GetSystemDefaultLocaleName, LCTYPE};
use winapi::um::winnt::{LOCALE_NAME_MAX_LENGTH, LPWSTR};
use super::*;
use crate::win32::base_helper::{to_utf16, from_utf16};
use crate::NwgError;
use std::{mem, ptr};
#[derive(Clone)]
pub struct Locale {
name: String,
name_buffer: Vec<u16>
}
impl Locale {
pub fn new(name: String) -> Result<Locale, NwgError> {
let name_buffer = to_utf16(&name);
match Locale::locale_valid(&name_buffer) {
true => Ok(Locale { name, name_buffer }),
false => Err(NwgError::bad_locale("Locale name is not valid"))
}
}
pub fn from_str<'a>(name: &'a str) -> Result<Locale, NwgError> {
let name_buffer = to_utf16(name);
match Locale::locale_valid(&name_buffer) {
true => Ok(Locale { name: name.to_string(), name_buffer }),
false => Err(NwgError::bad_locale("Locale name is not valid"))
}
}
pub fn all() -> Vec<String> {
use winapi::um::winnls::EnumSystemLocalesEx;
use winapi::shared::minwindef::{DWORD, BOOL, LPARAM};
use crate::win32::base_helper::from_wide_ptr;
unsafe extern "system" fn enum_locales(locale: LPWSTR, _flags: DWORD, p: LPARAM) -> BOOL {
let locales: *mut Vec<String> = p as *mut Vec<String>;
(&mut *locales).push(from_wide_ptr(locale, None));
1
}
unsafe {
let mut locales: Vec<String> = Vec::with_capacity(10);
EnumSystemLocalesEx(Some(enum_locales), 1, &mut locales as *mut Vec<String> as LPARAM, ptr::null_mut());
locales
}
}
pub fn user() -> Locale {
let mut name_buffer: Vec<u16> = Vec::with_capacity(LOCALE_NAME_MAX_LENGTH);
unsafe {
name_buffer.set_len(LOCALE_NAME_MAX_LENGTH);
GetUserDefaultLocaleName(name_buffer.as_mut_ptr(), LOCALE_NAME_MAX_LENGTH as i32);
}
Locale {
name: from_utf16(&name_buffer),
name_buffer
}
}
pub fn system() -> Locale {
let mut name_buffer: Vec<u16> = Vec::with_capacity(LOCALE_NAME_MAX_LENGTH);
unsafe {
name_buffer.set_len(LOCALE_NAME_MAX_LENGTH);
GetSystemDefaultLocaleName(name_buffer.as_mut_ptr(), LOCALE_NAME_MAX_LENGTH as i32);
}
Locale {
name: from_utf16(&name_buffer),
name_buffer
}
}
pub fn name(&self) -> &str {
&self.name
}
pub fn display_name(&self) -> String {
self.get_locale_info_string(0x00000002)
}
pub fn english_display_name(&self) -> String {
self.get_locale_info_string(0x00000072)
}
pub fn native_display_name(&self) -> String {
self.get_locale_info_string(0x00000072)
}
pub fn country_name(&self) -> String {
self.get_locale_info_string(0x00000006)
}
pub fn english_country_name(&self) -> String {
self.get_locale_info_string(0x00001002)
}
pub fn native_country_name(&self) -> String {
self.get_locale_info_string(0x00000008)
}
pub fn dialing_code(&self) -> i32 {
self.get_locale_info_int(0x00000005)
}
pub fn list_separator(&self) -> String {
self.get_locale_info_string(0x0000000C)
}
pub fn measurement_system(&self) -> MeasurementSystem {
match self.get_locale_info_int(0x0000000D) {
0 => MeasurementSystem::Metric,
_ => MeasurementSystem::Imperial,
}
}
pub fn decimal_separator(&self) -> String {
self.get_locale_info_string(0x0000000E)
}
pub fn thousand_separator(&self) -> String {
self.get_locale_info_string(0x0000000F)
}
pub fn digit_grouping(&self) -> String {
self.get_locale_info_string(0x00000010)
}
pub fn fractional_digit(&self) -> i32 {
self.get_locale_info_int(0x00000011)
}
pub fn leading_zeros(&self) -> i32 {
self.get_locale_info_int(0x00000012)
}
pub fn negative_number_mode(&self) -> NegativeNumberMode {
match self.get_locale_info_int(0x00001010) {
0 => NegativeNumberMode::Mode0,
1 => NegativeNumberMode::Mode1,
2 => NegativeNumberMode::Mode2,
3 => NegativeNumberMode::Mode3,
4 => NegativeNumberMode::Mode4,
_ => NegativeNumberMode::Mode1,
}
}
pub fn native_digits(&self) -> String {
self.get_locale_info_string(0x00000013)
}
pub fn currency_symbol(&self) -> String {
self.get_locale_info_string(0x00000014)
}
pub fn intl_monetary_symbol(&self) -> String {
self.get_locale_info_string(0x00000015)
}
pub fn monetary_decimal_separator(&self) -> String {
self.get_locale_info_string(0x00000016)
}
pub fn monetary_thousand_separator(&self) -> String {
self.get_locale_info_string(0x00000017)
}
pub fn monetary_digit_grouping(&self) -> String {
self.get_locale_info_string(0x00000018)
}
pub fn monetary_fractional_digit(&self) -> i32 {
self.get_locale_info_int(0x00000019)
}
pub fn currency_mode(&self) -> PositiveCurrency {
match self.get_locale_info_int(0x0000001B) {
0 => PositiveCurrency::Mode0,
1 => PositiveCurrency::Mode1,
2 => PositiveCurrency::Mode2,
3 => PositiveCurrency::Mode3,
_ => PositiveCurrency::Mode1,
}
}
pub fn negative_currency_mode(&self) -> NegativeCurrency {
let id = self.get_locale_info_int(0x00001009) as u32;
match id <= 15 {
true => unsafe { mem::transmute(id) },
false => NegativeCurrency::Mode1
}
}
pub fn short_date(&self) -> String {
self.get_locale_info_string(0x0000001F)
}
pub fn long_date(&self) -> String {
self.get_locale_info_string(0x00000020)
}
pub fn time(&self) -> String {
self.get_locale_info_string(0x00001003)
}
pub fn am(&self) -> String {
self.get_locale_info_string(0x00000028)
}
pub fn pm(&self) -> String {
self.get_locale_info_string(0x00000029)
}
pub fn calendar(&self) -> Calendar {
let id = self.get_locale_info_int(0x00001009) as u32;
match id <= 23 {
true => unsafe { mem::transmute(id) },
false => Calendar::Gregorian
}
}
pub fn calendar2(&self) -> Calendar {
let id = self.get_locale_info_int(0x0000100B) as u32;
match id <= 23 {
true => unsafe { mem::transmute(id) },
false => Calendar::Gregorian
}
}
pub fn first_day_of_week(&self) -> i32 {
self.get_locale_info_int(0x0000100C)
}
pub fn first_day_of_year(&self) -> FirstDayOfYear {
match self.get_locale_info_int(0x0000100D) {
0 => FirstDayOfYear::Mode0,
1 => FirstDayOfYear::Mode1,
2 => FirstDayOfYear::Mode2,
_ => FirstDayOfYear::Mode0,
}
}
pub fn iso_lang_name(&self) -> String {
self.get_locale_info_string(0x00000059)
}
pub fn iso_country_name(&self) -> String {
self.get_locale_info_string(0x0000005A)
}
pub fn currency_name(&self) -> String {
self.get_locale_info_string(0x00001007)
}
pub fn native_currency_name(&self) -> String {
self.get_locale_info_string(0x00001008)
}
pub fn month_name(&self, month_index: u32) -> String {
match month_index {
1 => self.get_locale_info_string(0x00000038),
2 => self.get_locale_info_string(0x00000039),
3 => self.get_locale_info_string(0x0000003A),
4 => self.get_locale_info_string(0x0000003B),
5 => self.get_locale_info_string(0x0000003C),
6 => self.get_locale_info_string(0x0000003D),
7 => self.get_locale_info_string(0x0000003E),
8 => self.get_locale_info_string(0x0000003F),
9 => self.get_locale_info_string(0x00000040),
10 => self.get_locale_info_string(0x00000041),
11 => self.get_locale_info_string(0x00000042),
12 => self.get_locale_info_string(0x00000043),
13 => self.get_locale_info_string(0x0000100E),
x => panic!("{} is not a valid month index", x)
}
}
pub fn month_name_abv(&self, month_index: u32) -> String {
match month_index {
1 => self.get_locale_info_string(0x00000044),
2 => self.get_locale_info_string(0x00000045),
3 => self.get_locale_info_string(0x00000046),
4 => self.get_locale_info_string(0x00000047),
5 => self.get_locale_info_string(0x00000048),
6 => self.get_locale_info_string(0x00000049),
7 => self.get_locale_info_string(0x0000004A),
8 => self.get_locale_info_string(0x0000004B),
9 => self.get_locale_info_string(0x0000004C),
10 => self.get_locale_info_string(0x0000004D),
11 => self.get_locale_info_string(0x0000004E),
12 => self.get_locale_info_string(0x0000004F),
13 => self.get_locale_info_string(0x0000100F),
x => panic!("{} is not a valid month index", x)
}
}
pub fn day_name(&self, day_index: u32) -> String {
match day_index {
1 => self.get_locale_info_string(0x0000002A),
2 => self.get_locale_info_string(0x0000002B),
3 => self.get_locale_info_string(0x0000002C),
4 => self.get_locale_info_string(0x0000002D),
5 => self.get_locale_info_string(0x0000002E),
6 => self.get_locale_info_string(0x0000002F),
7 => self.get_locale_info_string(0x00000030),
x => panic!("{} is not a valid day index", x)
}
}
pub fn day_name_abv(&self, day_index: u32) -> String {
match day_index {
1 => self.get_locale_info_string(0x00000031),
2 => self.get_locale_info_string(0x00000032),
3 => self.get_locale_info_string(0x00000033),
4 => self.get_locale_info_string(0x00000034),
5 => self.get_locale_info_string(0x00000035),
6 => self.get_locale_info_string(0x00000036),
7 => self.get_locale_info_string(0x00000037),
x => panic!("{} is not a valid day index", x)
}
}
fn get_locale_info_string(&self, info: LCTYPE) -> String {
unsafe {
let buffer_size = GetLocaleInfoEx(self.name_buffer.as_ptr(), info, ptr::null_mut(), 0) as usize;
let mut buffer: Vec<u16> = Vec::with_capacity(buffer_size);
buffer.set_len(buffer_size);
GetLocaleInfoEx(self.name_buffer.as_ptr(), info, buffer.as_mut_ptr(), buffer_size as i32);
from_utf16(&buffer)
}
}
fn get_locale_info_int(&self, info: LCTYPE) -> i32 {
let mut out = 0i32;
let return_number = 0x20000000;
unsafe {
let out_ptr = &mut out as *mut i32 as *mut u16;
GetLocaleInfoEx(self.name_buffer.as_ptr(), info | return_number, out_ptr, mem::size_of::<i32>() as i32);
}
out
}
fn locale_valid(buffer: &[u16]) -> bool {
use winapi::um::winnls::IsValidLocaleName;
unsafe { IsValidLocaleName(buffer.as_ptr()) != 0 }
}
}
use std::fmt;
impl fmt::Debug for Locale {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Locale {{ name: {}, }}", self.name)
}
}
impl fmt::Display for Locale {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.name)
}
}