Skip to main content

Rest/Fn/NLS/
bundle.rs

1//! NLS bundle generation and management
2//!
3//! Creates and manages localization bundles for different languages.
4
5use std::{collections::HashMap, path::Path};
6
7use super::{LocalizationBundle, NLSConfig};
8
9/// Generates an NLS bundle from extracted keys
10pub struct NLSBundle {
11	_config:NLSConfig,
12	/// Bundle for each language
13	bundles:HashMap<String, LocalizationBundle>,
14}
15
16impl NLSBundle {
17	pub fn new(config:NLSConfig) -> Self {
18		let mut bundles = HashMap::new();
19
20		// Initialize bundles for each language
21		for lang in &config.languages {
22			bundles.insert(lang.clone(), LocalizationBundle::new(lang));
23		}
24
25		Self { _config:config, bundles }
26	}
27
28	/// Add a localization entry to a specific language
29	pub fn add_entry(&mut self, language:&str, key:impl Into<String>, value:impl Into<String>) {
30		if let Some(bundle) = self.bundles.get_mut(language) {
31			bundle.add_entry(key, value);
32		}
33	}
34
35	/// Add entries from a key-value map
36	pub fn add_entries(&mut self, language:&str, entries:&HashMap<String, String>) {
37		if let Some(bundle) = self.bundles.get_mut(language) {
38			for (key, value) in entries {
39				bundle.add_entry(key.clone(), value.clone());
40			}
41			bundle.compute_hash();
42		}
43	}
44
45	/// Generate bundle files for all languages
46	pub fn generate(&self, output_dir:&Path) -> std::io::Result<()> {
47		use std::fs;
48
49		// Create output directory if it doesn't exist
50		fs::create_dir_all(output_dir)?;
51
52		for (lang, bundle) in &self.bundles {
53			let filename = format!("{}.json", lang);
54			let path = output_dir.join(&filename);
55
56			let json =
57				serde_json::to_string_pretty(bundle).map_err(|e| std::io::Error::new(std::io::ErrorKind::Other, e))?;
58
59			fs::write(path, json)?;
60		}
61
62		Ok(())
63	}
64
65	/// Get a specific bundle
66	pub fn get_bundle(&self, language:&str) -> Option<&LocalizationBundle> { self.bundles.get(language) }
67
68	/// Get all bundles
69	pub fn all_bundles(&self) -> &HashMap<String, LocalizationBundle> { &self.bundles }
70
71	/// Load a bundle from a file
72	pub fn load_bundle(language:&str, path:&Path) -> std::io::Result<LocalizationBundle> {
73		let content = std::fs::read_to_string(path)?;
74		let bundle:LocalizationBundle =
75			serde_json::from_str(&content).map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e))?;
76
77		// Verify language matches
78		if bundle.language != language {
79			return Err(std::io::Error::new(
80				std::io::ErrorKind::InvalidData,
81				format!("Language mismatch: expected {}, got {}", language, bundle.language),
82			));
83		}
84
85		Ok(bundle)
86	}
87
88	/// Create a HashMap from a bundle for use with NLSReplacer
89	pub fn to_hashmap(&self, language:&str) -> HashMap<String, String> {
90		let mut map = HashMap::new();
91
92		if let Some(bundle) = self.bundles.get(language) {
93			for entry in &bundle.entries {
94				map.insert(entry.key.clone(), entry.value.clone());
95			}
96		}
97
98		map
99	}
100}
101
102/// Generate VSCode-compatible NLS bundle format
103pub fn generate_vscode_bundle(entries:&HashMap<String, String>, language:&str) -> LocalizationBundle {
104	let mut bundle = LocalizationBundle::new(language);
105
106	for (key, value) in entries {
107		bundle.add_entry(key, value);
108	}
109
110	bundle.compute_hash();
111	bundle
112}
113
114/// Format bundle as VSCode's nls.metadata.json format
115pub fn format_metadata(bundle:&LocalizationBundle) -> serde_json::Value {
116	let mut metadata = serde_json::Map::new();
117
118	for entry in &bundle.entries {
119		let mut item = serde_json::Map::new();
120		item.insert("value".to_string(), serde_json::Value::String(entry.value.clone()));
121
122		if let Some(comment) = &entry.comment {
123			item.insert("comment".to_string(), serde_json::Value::String(comment.clone()));
124		}
125
126		metadata.insert(entry.key.clone(), serde_json::Value::Object(item));
127	}
128
129	serde_json::Value::Object(metadata)
130}
131
132#[cfg(test)]
133mod tests {
134	use super::*;
135
136	#[test]
137	fn test_bundle_creation() {
138		let config = NLSConfig { languages:vec!["en".to_string(), "de".to_string()], ..Default::default() };
139
140		let bundle = NLSBundle::new(config);
141		assert!(bundle.get_bundle("en").is_some());
142		assert!(bundle.get_bundle("de").is_some());
143	}
144
145	#[test]
146	fn test_add_entry() {
147		let config = NLSConfig { languages:vec!["en".to_string()], ..Default::default() };
148
149		let mut bundle = NLSBundle::new(config);
150		bundle.add_entry("en", "hello", "Hello World");
151
152		let en_bundle = bundle.get_bundle("en").unwrap();
153		assert_eq!(en_bundle.entries.len(), 1);
154		assert_eq!(en_bundle.entries[0].key, "hello");
155	}
156
157	#[test]
158	fn test_to_hashmap() {
159		let config = NLSConfig { languages:vec!["en".to_string()], ..Default::default() };
160
161		let mut bundle = NLSBundle::new(config);
162		bundle.add_entry("en", "hello", "Hello World");
163
164		let map = bundle.to_hashmap("en");
165		assert_eq!(map.get("hello"), Some(&"Hello World".to_string()));
166	}
167}