Skip to main content

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