mirror of
https://github.com/CodeZeno/Claude-Code-Usage-Monitor.git
synced 2026-06-20 10:19:27 +08:00
Add Korean language support with full UI translations including menu items, update messages, and time format suffixes. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
212 lines
6.0 KiB
Rust
212 lines
6.0 KiB
Rust
mod english;
|
|
mod french;
|
|
mod german;
|
|
mod japanese;
|
|
mod korean;
|
|
mod spanish;
|
|
|
|
use windows::core::PWSTR;
|
|
use windows::Win32::Globalization::{
|
|
GetUserDefaultLocaleName, GetUserDefaultUILanguage, GetUserPreferredUILanguages,
|
|
LCIDToLocaleName, LOCALE_ALLOW_NEUTRAL_NAMES, MAX_LOCALE_NAME, MUI_LANGUAGE_NAME,
|
|
};
|
|
|
|
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
|
|
pub enum LanguageId {
|
|
English,
|
|
Spanish,
|
|
French,
|
|
German,
|
|
Japanese,
|
|
Korean,
|
|
}
|
|
|
|
impl LanguageId {
|
|
pub const ALL: [LanguageId; 6] = [
|
|
LanguageId::English,
|
|
LanguageId::Spanish,
|
|
LanguageId::French,
|
|
LanguageId::German,
|
|
LanguageId::Japanese,
|
|
LanguageId::Korean,
|
|
];
|
|
|
|
pub fn code(self) -> &'static str {
|
|
match self {
|
|
Self::English => "en",
|
|
Self::Spanish => "es",
|
|
Self::French => "fr",
|
|
Self::German => "de",
|
|
Self::Japanese => "ja",
|
|
Self::Korean => "ko",
|
|
}
|
|
}
|
|
|
|
pub fn native_name(self) -> &'static str {
|
|
match self {
|
|
Self::English => "English",
|
|
Self::Spanish => "Español",
|
|
Self::French => "Français",
|
|
Self::German => "Deutsch",
|
|
Self::Japanese => "日本語",
|
|
Self::Korean => "한국어",
|
|
}
|
|
}
|
|
|
|
pub fn strings(self) -> Strings {
|
|
match self {
|
|
Self::English => english::STRINGS,
|
|
Self::Spanish => spanish::STRINGS,
|
|
Self::French => french::STRINGS,
|
|
Self::German => german::STRINGS,
|
|
Self::Japanese => japanese::STRINGS,
|
|
Self::Korean => korean::STRINGS,
|
|
}
|
|
}
|
|
|
|
pub fn update_via_winget_label(self) -> &'static str {
|
|
match self {
|
|
Self::English => english::UPDATE_VIA_WINGET_LABEL,
|
|
Self::Spanish => spanish::UPDATE_VIA_WINGET_LABEL,
|
|
Self::French => french::UPDATE_VIA_WINGET_LABEL,
|
|
Self::German => german::UPDATE_VIA_WINGET_LABEL,
|
|
Self::Japanese => japanese::UPDATE_VIA_WINGET_LABEL,
|
|
Self::Korean => korean::UPDATE_VIA_WINGET_LABEL,
|
|
}
|
|
}
|
|
|
|
pub fn from_code(code: &str) -> Option<Self> {
|
|
let normalized = code.trim().replace('_', "-").to_ascii_lowercase();
|
|
if normalized.is_empty() || normalized == "system" {
|
|
return None;
|
|
}
|
|
|
|
match normalized.split('-').next().unwrap_or_default() {
|
|
"en" => Some(Self::English),
|
|
"es" => Some(Self::Spanish),
|
|
"fr" => Some(Self::French),
|
|
"de" => Some(Self::German),
|
|
"ja" => Some(Self::Japanese),
|
|
"ko" => Some(Self::Korean),
|
|
_ => None,
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Clone, Copy, Debug)]
|
|
pub struct Strings {
|
|
pub window_title: &'static str,
|
|
pub refresh: &'static str,
|
|
pub update_frequency: &'static str,
|
|
pub one_minute: &'static str,
|
|
pub five_minutes: &'static str,
|
|
pub fifteen_minutes: &'static str,
|
|
pub one_hour: &'static str,
|
|
pub settings: &'static str,
|
|
pub start_with_windows: &'static str,
|
|
pub reset_position: &'static str,
|
|
pub language: &'static str,
|
|
pub system_default: &'static str,
|
|
pub check_for_updates: &'static str,
|
|
pub checking_for_updates: &'static str,
|
|
pub updates: &'static str,
|
|
pub update_in_progress: &'static str,
|
|
pub up_to_date: &'static str,
|
|
pub up_to_date_short: &'static str,
|
|
pub update_failed: &'static str,
|
|
pub applying_update: &'static str,
|
|
pub update_to: &'static str,
|
|
pub update_available: &'static str,
|
|
pub update_prompt_now: &'static str,
|
|
pub exit: &'static str,
|
|
pub session_window: &'static str,
|
|
pub weekly_window: &'static str,
|
|
pub now: &'static str,
|
|
pub day_suffix: &'static str,
|
|
pub hour_suffix: &'static str,
|
|
pub minute_suffix: &'static str,
|
|
pub second_suffix: &'static str,
|
|
}
|
|
|
|
pub fn resolve_language(language_override: Option<LanguageId>) -> LanguageId {
|
|
language_override.unwrap_or_else(detect_system_language)
|
|
}
|
|
|
|
pub fn detect_system_language() -> LanguageId {
|
|
preferred_ui_languages()
|
|
.into_iter()
|
|
.find_map(|locale| LanguageId::from_code(&locale))
|
|
.or_else(default_ui_locale)
|
|
.or_else(default_locale_name)
|
|
.unwrap_or(LanguageId::English)
|
|
}
|
|
|
|
pub fn update_via_winget(language: LanguageId) -> &'static str {
|
|
language.update_via_winget_label()
|
|
}
|
|
|
|
fn preferred_ui_languages() -> Vec<String> {
|
|
unsafe {
|
|
let mut num_languages = 0u32;
|
|
let mut buffer_len = 0u32;
|
|
if GetUserPreferredUILanguages(
|
|
MUI_LANGUAGE_NAME,
|
|
&mut num_languages,
|
|
PWSTR::null(),
|
|
&mut buffer_len,
|
|
)
|
|
.is_err()
|
|
|| buffer_len == 0
|
|
{
|
|
return Vec::new();
|
|
}
|
|
|
|
let mut buffer = vec![0u16; buffer_len as usize];
|
|
if GetUserPreferredUILanguages(
|
|
MUI_LANGUAGE_NAME,
|
|
&mut num_languages,
|
|
PWSTR(buffer.as_mut_ptr()),
|
|
&mut buffer_len,
|
|
)
|
|
.is_err()
|
|
{
|
|
return Vec::new();
|
|
}
|
|
|
|
buffer
|
|
.split(|unit| *unit == 0)
|
|
.filter(|part| !part.is_empty())
|
|
.map(String::from_utf16_lossy)
|
|
.collect()
|
|
}
|
|
}
|
|
|
|
fn default_ui_locale() -> Option<LanguageId> {
|
|
unsafe {
|
|
let lang_id = GetUserDefaultUILanguage();
|
|
let mut buffer = [0u16; MAX_LOCALE_NAME as usize];
|
|
let len = LCIDToLocaleName(
|
|
lang_id as u32,
|
|
Some(&mut buffer),
|
|
LOCALE_ALLOW_NEUTRAL_NAMES,
|
|
);
|
|
if len <= 1 {
|
|
return None;
|
|
}
|
|
let locale = String::from_utf16_lossy(&buffer[..(len as usize - 1)]);
|
|
LanguageId::from_code(&locale)
|
|
}
|
|
}
|
|
|
|
fn default_locale_name() -> Option<LanguageId> {
|
|
unsafe {
|
|
let mut buffer = [0u16; MAX_LOCALE_NAME as usize];
|
|
let len = GetUserDefaultLocaleName(&mut buffer);
|
|
if len <= 1 {
|
|
return None;
|
|
}
|
|
let locale = String::from_utf16_lossy(&buffer[..(len as usize - 1)]);
|
|
LanguageId::from_code(&locale)
|
|
}
|
|
}
|