langcodec_cli/transformers/
json_language_map.rs

1use std::collections::HashMap;
2
3use langcodec::{Entry, EntryStatus, Metadata, Resource, Translation};
4
5/// Transform a JSON language map file into Resources.
6///
7/// Expected format:
8/// ```json
9/// {
10///     "key": "hello_world",
11///     "en": "Hello, World!",
12///     "fr": "Bonjour, le monde!"
13/// }
14/// ```
15pub fn transform(input: String) -> Result<Vec<Resource>, String> {
16    let file_content = match std::fs::read_to_string(&input) {
17        Ok(content) => content,
18        Err(e) => return Err(format!("Error reading file {}: {}", input, e)),
19    };
20
21    // Try to parse as JSON key-value pairs
22    let json_object: HashMap<String, String> = match serde_json::from_str(&file_content) {
23        Ok(obj) => obj,
24        Err(e) => {
25            return Err(format!(
26                "Error parsing JSON from {}: {}. Expected format: {{\"en\": \"Hello\", \"fr\": \"Bonjour\"}}",
27                input, e
28            ));
29        }
30    };
31
32    if json_object.is_empty() {
33        return Err("Error: JSON object is empty".to_string());
34    }
35
36    // Find the localization key
37    // Priority: "key" field > "en" field > first field value
38    let localization_key = json_object.get("key").unwrap_or(
39        json_object
40            .get("en")
41            .unwrap_or(json_object.iter().next().unwrap().1),
42    );
43
44    let mut resources = Vec::new();
45
46    for (lang_code, value) in json_object.iter() {
47        // Skip the "key" field as it's not a language code
48        if lang_code == "key" {
49            continue;
50        }
51
52        let mut metadata_custom: HashMap<String, String> = HashMap::new();
53        metadata_custom.insert("source_language".to_string(), "en".to_string());
54        metadata_custom.insert("version".to_string(), "1.0".to_string());
55        metadata_custom.insert("format".to_string(), "JSONLanguageMap".to_string());
56
57        let metadata = Metadata {
58            language: lang_code.clone(),
59            domain: "".to_string(),
60            custom: metadata_custom,
61        };
62
63        let mut entry_custom = HashMap::new();
64        entry_custom.insert("extraction_state".to_string(), "manual".to_string());
65
66        let entry = Entry {
67            id: localization_key.clone(),
68            value: Translation::Singular(value.clone()),
69            status: EntryStatus::NeedsReview,
70            comment: None,
71            custom: entry_custom,
72        };
73
74        resources.push(Resource {
75            metadata,
76            entries: vec![entry],
77        });
78    }
79
80    Ok(resources)
81}