langcodec_cli/transformers/
json_array_language_map.rs

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