diff --git a/.cargo/config.toml b/.cargo/config.toml index 79d7d98d..04791248 100644 --- a/.cargo/config.toml +++ b/.cargo/config.toml @@ -28,8 +28,8 @@ WAMR_BUILD_PLATFORM = "XILA" WAMR_SHARED_PLATFORM_CONFIG = { value = "executables/wasm/bindings/wamr/shared_platform.cmake", relative = true } LLVM_LIB_CFG_PATH = "/usr/lib/llvm-19/cmake/" LITTLEFS_CONFIG = { value = "modules/little_fs/include/little_fs_config.h", relative = true } -INTERNATIONALIZATION_LOCALE = "en" -INTERNATIONALIZATION_FALLBACK = "en" +EMBEDDED_I18N_LOCALE = "en" +EMBEDDED_I18N_FALLBACK = "en" [target.wasm32-unknown-unknown] rustflags = ['--cfg', 'getrandom_backend="wasm_js"'] diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index ed85f06a..3e98b670 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -40,7 +40,7 @@ jobs: - name: Build | WASM demonstration (en) working-directory: ./examples/wasm env: - INTERNATIONALIZATION_LOCALE: en + EMBEDDED_I18N_LOCALE: en run: | (cd ../../ && cargo make generate-fonts) trunk build --release --public-url ./ @@ -50,7 +50,7 @@ jobs: - name: Build | WASM demonstration (fr) working-directory: ./examples/wasm env: - INTERNATIONALIZATION_LOCALE: fr + EMBEDDED_I18N_LOCALE: fr run: | (cd ../../ && cargo make generate-fonts) trunk build --release --public-url ./ diff --git a/Cargo.toml b/Cargo.toml index d55f1e76..60fef334 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -16,7 +16,7 @@ lto = true [dependencies] # - Always included -internationalization = { workspace = true } +embedded-i18n = { workspace = true } # - Host @@ -88,7 +88,7 @@ target = { path = "modules/target" } network = { path = "modules/network" } peripherals = { path = "modules/peripherals" } bootsplash = { path = "modules/bootsplash" } -internationalization = { path = "modules/internationalization" } +embedded-i18n = { git = "https://github.com/AlixANNERAUD/embedded-i18n.git" } testing = { path = "modules/testing" } device = { path = "modules/device" } @@ -175,8 +175,7 @@ members = [ "drivers/shared", "drivers/std", "drivers/wasm", - "modules/internationalization", - "modules/internationalization/macros", + "modules/graphics/fonts_generator", "modules/testing", "modules/device", diff --git a/executables/calculator/Cargo.toml b/executables/calculator/Cargo.toml index b7c50b34..5c629ca3 100644 --- a/executables/calculator/Cargo.toml +++ b/executables/calculator/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -internationalization = { workspace = true } +embedded-i18n = { workspace = true } [target.'cfg(target_arch = "wasm32")'.dependencies] wasm = { workspace = true, features = ["guest", "graphics"] } diff --git a/executables/calculator/src/evaluator.rs b/executables/calculator/src/evaluator.rs index 1d78d296..fd2209f6 100644 --- a/executables/calculator/src/evaluator.rs +++ b/executables/calculator/src/evaluator.rs @@ -1,6 +1,6 @@ use crate::parser::{BinaryOperator, Expression, UnaryOperator}; use crate::token::FunctionToken; -use internationalization::translate; +use embedded_i18n::translate; pub struct Evaluator; diff --git a/executables/calculator/src/interface.rs b/executables/calculator/src/interface.rs index 8c35d59f..c741d712 100644 --- a/executables/calculator/src/interface.rs +++ b/executables/calculator/src/interface.rs @@ -1,5 +1,5 @@ use crate::{evaluator::Evaluator, parser::Parser}; -use internationalization::translate; +use embedded_i18n::translate; use std::{ptr::null_mut, thread::sleep}; use wasm::{ self, Color, EventCode, FlexFlow, Object, ObjectFlag, buttonmatrix_create, diff --git a/executables/calculator/src/main.rs b/executables/calculator/src/main.rs index 1a88297f..6b783637 100644 --- a/executables/calculator/src/main.rs +++ b/executables/calculator/src/main.rs @@ -24,8 +24,6 @@ fn main() { fn main() { println!( "{}", - internationalization::translate!( - "This executable is intended to be run in a WASM environment." - ) + embedded_i18n::translate!("This executable is intended to be run in a WASM environment.") ); } diff --git a/executables/calculator/src/parser.rs b/executables/calculator/src/parser.rs index c59bc091..9a1f8769 100644 --- a/executables/calculator/src/parser.rs +++ b/executables/calculator/src/parser.rs @@ -1,6 +1,6 @@ use std::iter::Peekable; -use internationalization::translate; +use embedded_i18n::translate; use crate::{ lexer::Lexer, diff --git a/executables/weather/Cargo.toml b/executables/weather/Cargo.toml index e7275684..1b294884 100644 --- a/executables/weather/Cargo.toml +++ b/executables/weather/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -internationalization = { workspace = true } +embedded-i18n = { workspace = true } miniserde = { workspace = true } shared = { workspace = true } task = { workspace = true } diff --git a/modules/authentication/Cargo.toml b/modules/authentication/Cargo.toml index 32ae8181..d42d770d 100644 --- a/modules/authentication/Cargo.toml +++ b/modules/authentication/Cargo.toml @@ -18,4 +18,4 @@ file_system = { workspace = true } device = { workspace = true } miniserde = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } diff --git a/modules/authentication/src/error.rs b/modules/authentication/src/error.rs index 292c1092..6e9c8b12 100644 --- a/modules/authentication/src/error.rs +++ b/modules/authentication/src/error.rs @@ -4,7 +4,7 @@ //! including user and group management, file I/O, and password validation. use core::fmt::Display; -use internationalization::translate; +use embedded_i18n::translate; /// Result type alias for authentication operations. /// diff --git a/modules/executable/Cargo.toml b/modules/executable/Cargo.toml index e1d75edd..9a1f00f2 100644 --- a/modules/executable/Cargo.toml +++ b/modules/executable/Cargo.toml @@ -10,7 +10,7 @@ task = { workspace = true } users = { workspace = true } shared = { workspace = true } log = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } [dev-dependencies] drivers_native = { workspace = true } diff --git a/modules/executable/src/error.rs b/modules/executable/src/error.rs index 3e08b8e1..5bc271b1 100644 --- a/modules/executable/src/error.rs +++ b/modules/executable/src/error.rs @@ -1,5 +1,5 @@ use core::fmt::Display; -use internationalization::translate; +use embedded_i18n::translate; pub type Result = core::result::Result; diff --git a/modules/file_system/Cargo.toml b/modules/file_system/Cargo.toml index 79998491..7b2ad0f5 100644 --- a/modules/file_system/Cargo.toml +++ b/modules/file_system/Cargo.toml @@ -8,7 +8,7 @@ task = { workspace = true } users = { workspace = true } shared = { workspace = true } synchronization = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } embedded-io-async = { workspace = true } diff --git a/modules/file_system/src/error.rs b/modules/file_system/src/error.rs index 28727e70..684a60e7 100644 --- a/modules/file_system/src/error.rs +++ b/modules/file_system/src/error.rs @@ -4,7 +4,7 @@ //! along with conversion traits and display implementations for comprehensive error reporting. use core::{fmt::Display, num::NonZeroU32}; -use internationalization::translate; +use embedded_i18n::translate; /// Standard result type for file system operations. /// diff --git a/modules/graphics/Cargo.toml b/modules/graphics/Cargo.toml index eb4cfdf1..5bcf2e4c 100644 --- a/modules/graphics/Cargo.toml +++ b/modules/graphics/Cargo.toml @@ -15,7 +15,7 @@ shared = { workspace = true } synchronization = { workspace = true } log = { workspace = true } abi_declarations = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } task = { workspace = true } diff --git a/modules/graphics/fonts_generator/Cargo.toml b/modules/graphics/fonts_generator/Cargo.toml index c979b1db..b46189e4 100644 --- a/modules/graphics/fonts_generator/Cargo.toml +++ b/modules/graphics/fonts_generator/Cargo.toml @@ -4,4 +4,4 @@ version = "0.1.0" edition = "2024" [dependencies] -internationalization = { workspace = true, features = ["std"] } +embedded-i18n = { workspace = true, features = ["std"] } diff --git a/modules/graphics/fonts_generator/src/main.rs b/modules/graphics/fonts_generator/src/main.rs index 68c09971..831ab479 100644 --- a/modules/graphics/fonts_generator/src/main.rs +++ b/modules/graphics/fonts_generator/src/main.rs @@ -1,6 +1,6 @@ use std::{env, fs, ops::Range, path::Path, process::Command}; -use internationalization::{format_ranges, merge_contiguous_ranges}; +use embedded_i18n::{format_ranges, merge_contiguous_ranges}; pub const FONTS: &[(u8, &str, &str, bool)] = &[ (10, "Montserrat-Regular.ttf", "montserrat", true), @@ -98,13 +98,12 @@ pub fn generate_font<'a>( } fn main() { - let locale = internationalization::get_locale_build(); - let fallback_locale = internationalization::get_fallback_locale_build(); + let locale = embedded_i18n::get_locale_build(); + let fallback_locale = embedded_i18n::get_fallback_locale_build(); - let locale_range = - internationalization::get_locale_ranges(&locale).expect("Unsupported locale"); - let fallback_range = internationalization::get_locale_ranges(&fallback_locale) - .expect("Unsupported fallback locale"); + let locale_range = embedded_i18n::get_locale_ranges(&locale).expect("Unsupported locale"); + let fallback_range = + embedded_i18n::get_locale_ranges(&fallback_locale).expect("Unsupported fallback locale"); let ranges = locale_range .iter() diff --git a/modules/graphics/src/error.rs b/modules/graphics/src/error.rs index 899047f7..e4ac4906 100644 --- a/modules/graphics/src/error.rs +++ b/modules/graphics/src/error.rs @@ -1,5 +1,5 @@ use core::fmt::Display; -use internationalization::translate; +use embedded_i18n::translate; pub type Result = core::result::Result; diff --git a/modules/internationalization/Cargo.toml b/modules/internationalization/Cargo.toml deleted file mode 100644 index b2bc346f..00000000 --- a/modules/internationalization/Cargo.toml +++ /dev/null @@ -1,12 +0,0 @@ -[package] -name = "internationalization" -version = "0.1.0" -edition = "2024" - -[dependencies] -internationalization_macros = { path = "./macros" } -shared = { path = "../shared" } - -[features] -default = [] -std = [] diff --git a/modules/internationalization/macros/Cargo.toml b/modules/internationalization/macros/Cargo.toml deleted file mode 100644 index 2436a22b..00000000 --- a/modules/internationalization/macros/Cargo.toml +++ /dev/null @@ -1,15 +0,0 @@ -[package] -name = "internationalization_macros" -version = "0.1.0" -edition = "2024" - -[dependencies] -syn = { workspace = true } -quote = { workspace = true } -proc-macro2 = { workspace = true } -regex = { workspace = true } -once_cell = "1.21" -serde_json = "1.0" - -[lib] -proc-macro = true diff --git a/modules/internationalization/macros/build.rs b/modules/internationalization/macros/build.rs deleted file mode 100644 index aff3d247..00000000 --- a/modules/internationalization/macros/build.rs +++ /dev/null @@ -1,4 +0,0 @@ -fn main() { - println!("cargo:rerun-if-env-changed=INTERNATIONALIZATION_LOCALE"); - println!("cargo:rerun-if-env-changed=INTERNATIONALIZATION_FALLBACK"); -} diff --git a/modules/internationalization/macros/src/lib.rs b/modules/internationalization/macros/src/lib.rs deleted file mode 100644 index 5b24d334..00000000 --- a/modules/internationalization/macros/src/lib.rs +++ /dev/null @@ -1,116 +0,0 @@ -use std::{collections::HashMap, fs}; - -use once_cell::sync::Lazy; -use proc_macro::TokenStream; -use quote::quote; - -static TRANSLATION_PATH: Lazy = Lazy::new(|| { - let path = std::env::var("CARGO_MANIFEST_DIR") - .map(std::path::PathBuf::from) - .expect("CARGO_MANIFEST_DIR is not set"); - path.join("locales") - .canonicalize() - .expect("Failed to canonicalize path") -}); - -static LOCALE: Lazy = - Lazy::new(|| std::env::var("INTERNATIONALIZATION_LOCALE").unwrap_or_else(|_| "en".to_string())); - -static FALLBACK_LOCALE: Lazy = Lazy::new(|| { - std::env::var("INTERNATIONALIZATION_FALLBACK").unwrap_or_else(|_| "en".to_string()) -}); - -static TRANSLATION_MAP: Lazy> = Lazy::new(|| { - let mut generated_items = HashMap::new(); - - // Load locale file - let locale_file_path = TRANSLATION_PATH - .clone() - .join(format!("{}.json", LOCALE.to_lowercase())); - if locale_file_path.exists() { - match fs::read_to_string(&locale_file_path) { - Ok(content) => match serde_json::from_str::>(&content) { - Ok(translations) => { - for (key, value) in translations { - if !value.is_empty() { - generated_items.insert(key, value); - } - } - } - Err(e) => { - eprintln!("Error parsing JSON file {:?}: {}", locale_file_path, e); - } - }, - Err(e) => { - eprintln!("Failed to read locale file {:?}: {}", locale_file_path, e); - } - } - } - - // Load fallback file - let fallback_file_path = TRANSLATION_PATH - .clone() - .join(format!("{}.json", FALLBACK_LOCALE.to_lowercase())); - if fallback_file_path.exists() { - match fs::read_to_string(&fallback_file_path) { - Ok(content) => match serde_json::from_str::>(&content) { - Ok(translations) => { - for (key, value) in translations { - generated_items.entry(key).or_insert(value); - } - } - Err(e) => { - eprintln!( - "Error parsing fallback JSON file {:?}: {}", - fallback_file_path, e - ); - } - }, - Err(e) => { - eprintln!( - "Failed to read fallback locale file {:?}: {}", - fallback_file_path, e - ); - } - } - } - - generated_items -}); - -#[proc_macro] -pub fn translate(input: TokenStream) -> TokenStream { - let input = input.to_string(); - - let identifier = input.trim(); - let (c, identifier) = if let Some(s) = identifier.strip_prefix("c\"") { - (true, s) - } else { - (false, identifier.strip_prefix("\"").unwrap_or(identifier)) - }; - - let identifier = identifier.strip_suffix("\"").unwrap_or(identifier); - - let value = TRANSLATION_MAP.get(identifier).cloned().unwrap_or_else(|| { - panic!( - "Translation for '{}' not found in locale or fallback (path: {:?})", - identifier, - TRANSLATION_PATH.clone() - ) - }); - - let value = if c { - let c_string_value = syn::LitCStr::new( - std::ffi::CString::new(value) - .expect("Failed to create CString") - .as_c_str(), - proc_macro2::Span::call_site(), - ); - quote! { #c_string_value } - } else { - let value = syn::LitStr::new(&value, proc_macro2::Span::call_site()); - quote! { #value } - }; - - value.into() -} diff --git a/modules/internationalization/src/lib.rs b/modules/internationalization/src/lib.rs deleted file mode 100644 index 4df413ec..00000000 --- a/modules/internationalization/src/lib.rs +++ /dev/null @@ -1,51 +0,0 @@ -#![no_std] - -extern crate alloc; - -#[cfg(feature = "std")] -extern crate std; - -mod range; -mod time; - -pub use range::*; -pub use time::*; - -pub use internationalization_macros::translate; - -const DEFAULT_LOCALE: &str = "en"; -const DEFAULT_FALLBACK_LOCALE: &str = "en"; - -pub const fn get_locale() -> &'static str { - match option_env!("INTERNATIONALIZATION_LOCALE") { - Some(locale) => locale, - None => DEFAULT_LOCALE, - } -} - -pub const fn get_fallback_locale() -> &'static str { - match option_env!("INTERNATIONALIZATION_FALLBACK") { - Some(locale) => locale, - None => DEFAULT_FALLBACK_LOCALE, - } -} - -#[cfg(feature = "std")] -pub fn get_locale_build() -> std::string::String { - use std::string::ToString; - - match std::env::var("INTERNATIONALIZATION_LOCALE") { - Ok(locale) => locale, - Err(_) => DEFAULT_LOCALE.to_string(), - } -} - -#[cfg(feature = "std")] -pub fn get_fallback_locale_build() -> std::string::String { - use std::string::ToString; - - match std::env::var("INTERNATIONALIZATION_FALLBACK") { - Ok(locale) => locale, - Err(_) => DEFAULT_FALLBACK_LOCALE.to_string(), - } -} diff --git a/modules/internationalization/src/range.rs b/modules/internationalization/src/range.rs deleted file mode 100644 index 90a565ff..00000000 --- a/modules/internationalization/src/range.rs +++ /dev/null @@ -1,275 +0,0 @@ -use alloc::format; -use alloc::string::String; -use alloc::vec::Vec; -use core::ops::Range; - -pub const BASIC_LATIN: Range = 0x20..0x7F; -pub const LATIN_1_SUPPLEMENT: Range = 0xA0..0xFF; -pub const LATIN_EXTENDED_A: Range = 0x100..0x17F; -pub const LATIN_EXTENDED_B: Range = 0x180..0x24F; -pub const LATIN_EXTENDED_ADDITIONAL: Range = 0x1E00..0x1EFF; -pub const CYRILLIC: Range = 0x400..0x4FF; -pub const CYRILLIC_SUPPLEMENT: Range = 0x500..0x52F; -pub const GREEK: Range = 0x370..0x3FF; -pub const GREEK_EXTENDED: Range = 0x1F00..0x1FFF; -pub const ARABIC: Range = 0x600..0x6FF; -pub const ARABIC_SUPPLEMENT: Range = 0x750..0x77F; -pub const HEBREW: Range = 0x590..0x5FF; -pub const CJK_UNIFIED_IDEOGRAPHS: Range = 0x4E00..0x9FFF; -pub const HIRAGANA: Range = 0x3040..0x309F; -pub const KATAKANA: Range = 0x30A0..0x30FF; -pub const HANGUL_SYLLABLES: Range = 0xAC00..0xD7AF; -pub const DEVANAGARI: Range = 0x900..0x97F; -pub const THAI: Range = 0xE00..0xE7F; -pub const VIETNAMESE_EXTENSIONS: Range = 0x1EA0..0x1EFF; - -pub fn get_locale_ranges(locale: &str) -> Option<&'static [Range]> { - let ranges: &[Range] = match locale { - // English - "en" => &[BASIC_LATIN], - - // French - "fr" => &[ - BASIC_LATIN, - LATIN_1_SUPPLEMENT, - LATIN_EXTENDED_A, - LATIN_EXTENDED_B, - ], - - // German - "de" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Spanish - "es" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT], - - // Italian - "it" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT], - - // Portuguese - "pt" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Dutch - "nl" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT], - - // Swedish, Norwegian, Danish - "sv" | "no" | "da" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT], - - // Polish - "pl" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Czech, Slovak - "cs" | "sk" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Hungarian - "hu" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Romanian - "ro" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Turkish - "tr" => &[BASIC_LATIN, LATIN_1_SUPPLEMENT, LATIN_EXTENDED_A], - - // Russian, Ukrainian, Belarusian - "ru" | "uk" | "be" => &[BASIC_LATIN, CYRILLIC, CYRILLIC_SUPPLEMENT], - - // Greek - "el" => &[BASIC_LATIN, GREEK, GREEK_EXTENDED], - - // Arabic - "ar" => &[BASIC_LATIN, ARABIC, ARABIC_SUPPLEMENT], - - // Hebrew - "he" => &[BASIC_LATIN, HEBREW], - - // Japanese - "ja" => &[BASIC_LATIN, HIRAGANA, KATAKANA, CJK_UNIFIED_IDEOGRAPHS], - - // Chinese (Simplified and Traditional) - "zh" | "zh-CN" | "zh-TW" => &[BASIC_LATIN, CJK_UNIFIED_IDEOGRAPHS], - - // Korean - "ko" => &[BASIC_LATIN, HANGUL_SYLLABLES, CJK_UNIFIED_IDEOGRAPHS], - - // Hindi - "hi" => &[BASIC_LATIN, DEVANAGARI], - - // Thai - "th" => &[BASIC_LATIN, THAI], - - // Vietnamese - "vi" => &[ - BASIC_LATIN, - LATIN_1_SUPPLEMENT, - LATIN_EXTENDED_A, - LATIN_EXTENDED_ADDITIONAL, - VIETNAMESE_EXTENSIONS, - ], - _ => return None, - }; - - Some(ranges) -} - -pub fn merge_contiguous_ranges(ranges: Vec>) -> Vec> { - if ranges.is_empty() { - return ranges; - } - - let mut sorted_ranges = ranges; - sorted_ranges.sort_by_key(|r| r.start); - - let mut merged_ranges = Vec::new(); - let mut current_range = sorted_ranges[0].clone(); - - for range in sorted_ranges.into_iter().skip(1) { - if range.start <= current_range.end { - current_range.end = current_range.end.max(range.end); - } else { - merged_ranges.push(current_range); - current_range = range; - } - } - - merged_ranges.push(current_range); - - merged_ranges -} - -pub fn format_range(range: &Range) -> String { - if range.start + 1 == range.end { - format!("{}", range.start) - } else { - format!("{}-{}", range.start, range.end - 1) - } -} - -pub fn format_ranges<'a>(ranges: impl IntoIterator>) -> String { - ranges - .into_iter() - .map(format_range) - .collect::>() - .join(",") -} - -#[cfg(test)] -mod tests { - use super::*; - - use alloc::vec; - - #[test] - fn test_get_locale_ranges_english() { - let ranges = get_locale_ranges("en").unwrap(); - assert_eq!(ranges.len(), 1); - assert_eq!(ranges[0], BASIC_LATIN); - } - - #[test] - fn test_get_locale_ranges_french() { - let ranges = get_locale_ranges("fr").unwrap(); - assert_eq!(ranges.len(), 4); - assert!(ranges.contains(&BASIC_LATIN)); - assert!(ranges.contains(&LATIN_1_SUPPLEMENT)); - } - - #[test] - fn test_get_locale_ranges_russian() { - let ranges = get_locale_ranges("ru").unwrap(); - assert_eq!(ranges.len(), 3); - assert!(ranges.contains(&CYRILLIC)); - } - - #[test] - fn test_get_locale_ranges_japanese() { - let ranges = get_locale_ranges("ja").unwrap(); - assert_eq!(ranges.len(), 4); - assert!(ranges.contains(&HIRAGANA)); - assert!(ranges.contains(&KATAKANA)); - assert!(ranges.contains(&CJK_UNIFIED_IDEOGRAPHS)); - } - - #[test] - fn test_get_locale_ranges_invalid() { - assert!(get_locale_ranges("invalid").is_none()); - assert!(get_locale_ranges("xx").is_none()); - } - - #[test] - fn test_merge_contiguous_ranges_empty() { - let ranges = vec![]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 0); - } - - #[test] - fn test_merge_contiguous_ranges_single() { - let ranges = vec![0x20..0x7F]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 1); - assert_eq!(merged[0], 0x20..0x7F); - } - - #[test] - fn test_merge_contiguous_ranges_adjacent() { - let ranges = vec![0x20..0x7F, 0x7F..0xFF]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 1); - assert_eq!(merged[0], 0x20..0xFF); - } - - #[test] - fn test_merge_contiguous_ranges_overlapping() { - let ranges = vec![0x20..0x80, 0x50..0xFF]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 1); - assert_eq!(merged[0], 0x20..0xFF); - } - - #[test] - fn test_merge_contiguous_ranges_non_overlapping() { - let ranges = vec![0x20..0x7F, 0x100..0x17F]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 2); - assert_eq!(merged[0], 0x20..0x7F); - assert_eq!(merged[1], 0x100..0x17F); - } - - #[test] - fn test_merge_contiguous_ranges_unsorted() { - let ranges = vec![0x100..0x17F, 0x20..0x7F, 0x7F..0xFF]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 2); - assert_eq!(merged[0], 0x20..0xFF); - assert_eq!(merged[1], 0x100..0x17F); - } - - #[test] - fn test_merge_contiguous_ranges_multiple_groups() { - let ranges = vec![0x20..0x7F, 0x7F..0xFF, 0x200..0x2FF, 0x2FF..0x3FF]; - let merged = merge_contiguous_ranges(ranges); - assert_eq!(merged.len(), 2); - assert_eq!(merged[0], 0x20..0xFF); - assert_eq!(merged[1], 0x200..0x3FF); - } - - #[test] - fn test_unicode_range_boundaries() { - assert_eq!(BASIC_LATIN.start, 0x20); - assert_eq!(BASIC_LATIN.end, 0x7F); - assert_eq!(CJK_UNIFIED_IDEOGRAPHS.start, 0x4E00); - assert_eq!(CJK_UNIFIED_IDEOGRAPHS.end, 0x9FFF); - } - - #[test] - fn test_get_locale_ranges_aliases() { - // Test Chinese variants - assert!(get_locale_ranges("zh").is_some()); - assert!(get_locale_ranges("zh-CN").is_some()); - assert!(get_locale_ranges("zh-TW").is_some()); - - // Test Scandinavian languages - assert!(get_locale_ranges("sv").is_some()); - assert!(get_locale_ranges("no").is_some()); - assert!(get_locale_ranges("da").is_some()); - } -} diff --git a/modules/internationalization/src/time.rs b/modules/internationalization/src/time.rs deleted file mode 100644 index 199f6a86..00000000 --- a/modules/internationalization/src/time.rs +++ /dev/null @@ -1,90 +0,0 @@ -use alloc::format; -use alloc::string::String; - -/// Formats a Unix timestamp using Python-like `strftime` tokens. -/// -/// Supported tokens: -/// - `%Y` year with century (e.g. `2026`) -/// - `%m` month as zero-padded decimal (`01`..`12`) -/// - `%d` day of month as zero-padded decimal (`01`..`31`) -/// - `%H` hour (24-hour clock) as zero-padded decimal (`00`..`23`) -/// - `%I` hour (12-hour clock) as zero-padded decimal (`01`..`12`) -/// - `%M` minute as zero-padded decimal (`00`..`59`) -/// - `%S` second as zero-padded decimal (`00`..`59`) -/// - `%p` locale-independent `AM`/`PM` -/// - `%%` literal `%` -pub fn format_unix_timestamp(unix_timestamp: i64, pattern: &str) -> String { - let (year, month, day, hour, minute, second) = shared::decompose_unix_timestamp(unix_timestamp); - - let mut output = String::with_capacity(pattern.len() + 16); - let mut characters = pattern.chars(); - - while let Some(character) = characters.next() { - if character != '%' { - output.push(character); - continue; - } - - match characters.next() { - Some('Y') => output.push_str(&format!("{:04}", year)), - Some('m') => output.push_str(&format!("{:02}", month)), - Some('d') => output.push_str(&format!("{:02}", day)), - Some('H') => output.push_str(&format!("{:02}", hour)), - Some('I') => output.push_str(&format!("{:02}", hour_12(hour))), - Some('M') => output.push_str(&format!("{:02}", minute)), - Some('S') => output.push_str(&format!("{:02}", second)), - Some('p') => output.push_str(if hour < 12 { "AM" } else { "PM" }), - Some('%') => output.push('%'), - Some(other) => { - output.push('%'); - output.push(other); - } - None => output.push('%'), - } - } - - output -} - -const fn hour_12(hour_24: u8) -> u8 { - match hour_24 % 12 { - 0 => 12, - value => value, - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn format_24_hour_time() { - let timestamp = 13 * 3600 + 5 * 60; - assert_eq!(format_unix_timestamp(timestamp, "%H:%M"), "13:05"); - } - - #[test] - fn format_12_hour_time_with_am_pm() { - let midnight = 0; - let afternoon = 13 * 3600 + 5 * 60; - - assert_eq!(format_unix_timestamp(midnight, "%I:%M %p"), "12:00 AM"); - assert_eq!(format_unix_timestamp(afternoon, "%I:%M %p"), "01:05 PM"); - } - - #[test] - fn format_date_and_time() { - assert_eq!( - format_unix_timestamp(0, "%Y-%m-%d %H:%M:%S"), - "1970-01-01 00:00:00" - ); - } - - #[test] - fn format_negative_unix_time() { - assert_eq!( - format_unix_timestamp(-1, "%Y-%m-%d %H:%M:%S"), - "1969-12-31 23:59:59" - ); - } -} diff --git a/modules/network/Cargo.toml b/modules/network/Cargo.toml index 75922c76..845a603a 100644 --- a/modules/network/Cargo.toml +++ b/modules/network/Cargo.toml @@ -12,7 +12,7 @@ users = { workspace = true } log = { workspace = true } virtual_file_system = { workspace = true } shared = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } embedded-io-async = { workspace = true } embassy-time = { workspace = true } diff --git a/modules/network/src/error.rs b/modules/network/src/error.rs index d35c016e..d37164c7 100644 --- a/modules/network/src/error.rs +++ b/modules/network/src/error.rs @@ -1,5 +1,5 @@ use core::{fmt::Display, num::NonZeroU8}; -use internationalization::translate; +use embedded_i18n::translate; use smoltcp::socket::{dns, icmp, udp}; pub type Result = core::result::Result; diff --git a/modules/task/Cargo.toml b/modules/task/Cargo.toml index b2ac2bec..bac56289 100644 --- a/modules/task/Cargo.toml +++ b/modules/task/Cargo.toml @@ -10,7 +10,7 @@ embassy-executor = { workspace = true, default-features = false } embassy-futures = { workspace = true } smol_str = { workspace = true } synchronization = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } [target.'cfg(any(target_os = "linux", target_os = "macos", target_os = "windows"))'.dependencies] diff --git a/modules/task/src/error.rs b/modules/task/src/error.rs index f3248fac..a26b5568 100644 --- a/modules/task/src/error.rs +++ b/modules/task/src/error.rs @@ -1,5 +1,5 @@ use core::{fmt, num::NonZeroU32}; -use internationalization::translate; +use embedded_i18n::translate; pub type Result = core::result::Result; diff --git a/modules/time/Cargo.toml b/modules/time/Cargo.toml index c8ae6638..5ce32d20 100644 --- a/modules/time/Cargo.toml +++ b/modules/time/Cargo.toml @@ -8,7 +8,7 @@ file_system = { workspace = true } log = { workspace = true } shared = { workspace = true } synchronization = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } [dev-dependencies] drivers_native = { workspace = true } diff --git a/modules/time/src/error.rs b/modules/time/src/error.rs index a4f48db7..1ff88358 100644 --- a/modules/time/src/error.rs +++ b/modules/time/src/error.rs @@ -1,5 +1,5 @@ use core::fmt::Display; -use internationalization::translate; +use embedded_i18n::translate; #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Error { diff --git a/modules/users/Cargo.toml b/modules/users/Cargo.toml index 0eeea6b0..066fa716 100644 --- a/modules/users/Cargo.toml +++ b/modules/users/Cargo.toml @@ -5,7 +5,7 @@ edition = "2024" [dependencies] synchronization = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } [dev-dependencies] drivers_native = { workspace = true } diff --git a/modules/users/src/error.rs b/modules/users/src/error.rs index 655dd8e2..884ca59e 100644 --- a/modules/users/src/error.rs +++ b/modules/users/src/error.rs @@ -1,5 +1,5 @@ use core::fmt::Display; -use internationalization::translate; +use embedded_i18n::translate; pub type Result = core::result::Result; diff --git a/modules/virtual_file_system/Cargo.toml b/modules/virtual_file_system/Cargo.toml index 807ef1bd..0fc6cf88 100644 --- a/modules/virtual_file_system/Cargo.toml +++ b/modules/virtual_file_system/Cargo.toml @@ -11,7 +11,7 @@ time = { workspace = true } synchronization = { workspace = true } shared = { workspace = true } log = { workspace = true } -internationalization = { workspace = true } +embedded-i18n = { workspace = true } embedded-io-async = { workspace = true } diff --git a/modules/virtual_file_system/src/error.rs b/modules/virtual_file_system/src/error.rs index b78e85c0..943a77d3 100644 --- a/modules/virtual_file_system/src/error.rs +++ b/modules/virtual_file_system/src/error.rs @@ -1,7 +1,7 @@ use core::{fmt::Display, num::NonZeroU32}; +use embedded_i18n::translate; use embedded_io_async::ErrorKind; -use internationalization::translate; pub type Result = core::result::Result; diff --git a/src/lib.rs b/src/lib.rs index be0a0e83..d962291f 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -4,9 +4,9 @@ pub mod about; pub use abi_declarations; pub use authentication; +pub use embedded_i18n as internationalization; pub use executable; pub use file_system; -pub use internationalization; pub use little_fs; pub use log; pub use memory;