forked from
smokesignal.events/smokesignal
i18n+filtering fork - fluent-templates v2
1#!/bin/bash
2
3# Script to detect and remove duplicate keys in FTL files
4# Usage: ./clean_ftl_duplicates.sh [directory]
5
6set -e
7
8# Colors for display
9RED='\033[0;31m'
10GREEN='\033[0;32m'
11YELLOW='\033[1;33m'
12BLUE='\033[0;34m'
13NC='\033[0m' # No Color
14
15# Help function
16show_help() {
17 echo "Usage: $0 [OPTIONS] [DIRECTORY]"
18 echo ""
19 echo "Detects and removes duplicate keys in FTL files"
20 echo ""
21 echo "OPTIONS:"
22 echo " -h, --help Show this help"
23 echo " -l, --list List mode (does not modify files, only shows duplicates)"
24 echo " -v, --verbose Verbose mode"
25 echo " -i, --interactive Interactive mode (choose which occurrences to keep)"
26 echo ""
27 echo "ARGUMENTS:"
28 echo " DIRECTORY Directory containing FTL files (default: current directory)"
29 echo ""
30 echo "Examples:"
31 echo " $0 # Process current directory"
32 echo " $0 ./translations # Process ./translations directory"
33 echo " $0 -l ./translations # List mode"
34}
35
36# Default variables
37DIRECTORY="."
38LIST_ONLY=false
39VERBOSE=false
40INTERACTIVE=true # Default to interactive mode
41
42# Parse arguments
43while [[ $# -gt 0 ]]; do
44 case $1 in
45 -h|--help)
46 show_help
47 exit 0
48 ;;
49 -l|--list)
50 LIST_ONLY=true
51 shift
52 ;;
53 -v|--verbose)
54 VERBOSE=true
55 shift
56 ;;
57 -i|--interactive)
58 INTERACTIVE=true
59 shift
60 ;;
61 -*)
62 echo -e "${RED}Unknown option: $1${NC}" >&2
63 show_help
64 exit 1
65 ;;
66 *)
67 DIRECTORY="$1"
68 shift
69 ;;
70 esac
71done
72
73# Check that directory exists
74if [[ ! -d "$DIRECTORY" ]]; then
75 echo -e "${RED}Error: Directory '$DIRECTORY' does not exist${NC}" >&2
76 exit 1
77fi
78
79# Function to extract keys from an FTL file
80extract_keys() {
81 local file="$1"
82 # Extract keys (lines that start with non-space characters followed by =)
83 grep -E "^[^#[:space:]][^=]*=" "$file" 2>/dev/null | cut -d'=' -f1 | sed 's/[[:space:]]*$//' || true
84}
85
86# Function to get all occurrences of a key with their context
87get_key_occurrences() {
88 local key="$1"
89 shift
90 local files=("$@")
91 local -a occurrences
92 local index=0
93
94 for file in "${files[@]}"; do
95 local line_num=1
96 while IFS= read -r line; do
97 if [[ "$line" =~ ^[[:space:]]*"$key"[[:space:]]*= ]]; then
98 occurrences[$index]="$file:$line_num:$line"
99 ((index++))
100 fi
101 ((line_num++))
102 done < "$file"
103 done
104
105 printf '%s\n' "${occurrences[@]}"
106}
107
108# Function to display occurrences and let user choose
109choose_occurrence_to_keep() {
110 local key="$1"
111 shift
112 local files=("$@")
113
114 echo -e "${RED}Duplicate key found:${NC} '$key'"
115 echo ""
116
117 # Get all occurrences
118 local -a occurrences
119 mapfile -t occurrences < <(get_key_occurrences "$key" "${files[@]}")
120
121 if [[ ${#occurrences[@]} -eq 0 ]]; then
122 echo -e "${RED}Error: No occurrences found${NC}"
123 return 1
124 fi
125
126 # Display all occurrences
127 echo "Found ${#occurrences[@]} occurrences:"
128 for i in "${!occurrences[@]}"; do
129 local occurrence="${occurrences[$i]}"
130 IFS=':' read -r file line_num content <<< "$occurrence"
131 echo -e " ${YELLOW}[$((i+1))]${NC} $(basename "$file"):$line_num"
132 echo -e " $content"
133 echo ""
134 done
135
136 # Ask user to choose
137 while true; do
138 if [[ "$LIST_ONLY" == true ]]; then
139 echo -e "${YELLOW}LIST MODE: Showing duplicates only${NC}"
140 echo ""
141 return 0
142 fi
143
144 echo -e "${BLUE}Which occurrence do you want to KEEP?${NC} [1-${#occurrences[@]}] (or 's' to skip): "
145 read -r choice
146
147 if [[ "$choice" == "s" || "$choice" == "S" ]]; then
148 echo -e "${YELLOW}Skipping '$key'${NC}"
149 echo ""
150 return 0
151 fi
152
153 if [[ "$choice" =~ ^[0-9]+$ ]] && [[ "$choice" -ge 1 ]] && [[ "$choice" -le ${#occurrences[@]} ]]; then
154 local keep_index=$((choice-1))
155 break
156 else
157 echo -e "${RED}Invalid choice. Please enter a number between 1 and ${#occurrences[@]}, or 's' to skip.${NC}"
158 fi
159 done
160
161 # Remove all occurrences except the chosen one
162 echo -e "Keeping occurrence #$choice, removing others..."
163
164 for i in "${!occurrences[@]}"; do
165 if [[ $i -ne $keep_index ]]; then
166 local occurrence="${occurrences[$i]}"
167 IFS=':' read -r file line_num content <<< "$occurrence"
168 remove_occurrence_at_line "$file" "$line_num" "$key"
169 fi
170 done
171
172 echo -e "${GREEN}✓${NC} Cleaned up duplicates for '$key'"
173 echo ""
174}
175
176# Function to remove a specific occurrence at a given line number
177remove_occurrence_at_line() {
178 local file="$1"
179 local target_line="$2"
180 local key="$3"
181
182 if [[ "$LIST_ONLY" == true ]]; then
183 echo -e "${YELLOW}[LIST MODE]${NC} Would remove line $target_line from $(basename "$file")"
184 return
185 fi
186
187 # Create temporary copy
188 local temp_file=$(mktemp)
189 local current_line=1
190
191 while IFS= read -r line; do
192 if [[ $current_line -eq $target_line ]]; then
193 if [[ "$VERBOSE" == true ]]; then
194 echo -e " ${RED}Removed:${NC} $(basename "$file"):$target_line: $line"
195 fi
196 # Skip this line (don't write it to temp file)
197 else
198 echo "$line" >> "$temp_file"
199 fi
200 ((current_line++))
201 done < "$file"
202
203 # Replace original file
204 mv "$temp_file" "$file"
205 echo -e " ${GREEN}✓${NC} Removed line $target_line from $(basename "$file")"
206}
207
208# Main function
209main() {
210 echo -e "${BLUE}=== FTL Duplicate Key Cleanup ===${NC}"
211 echo -e "Directory: ${YELLOW}$DIRECTORY${NC}"
212
213 if [[ "$LIST_ONLY" == true ]]; then
214 echo -e "Mode: ${YELLOW}LIST ONLY${NC} (no files will be modified)"
215 fi
216
217 echo ""
218
219 # Find all FTL files
220 local ftl_files=($(find "$DIRECTORY" -maxdepth 1 -name "*.ftl" -type f))
221
222 if [[ ${#ftl_files[@]} -eq 0 ]]; then
223 echo -e "${YELLOW}No FTL files found in '$DIRECTORY'${NC}"
224 exit 0
225 fi
226
227 echo -e "${BLUE}FTL files found: ${#ftl_files[@]}${NC}"
228 for file in "${ftl_files[@]}"; do
229 echo " - $(basename "$file")"
230 done
231 echo ""
232
233 # Collect all keys with their source files
234 declare -A key_files
235 declare -A key_counts
236
237 echo -e "${BLUE}Analyzing keys...${NC}"
238
239 for file in "${ftl_files[@]}"; do
240 if [[ "$VERBOSE" == true ]]; then
241 echo " Analyzing $(basename "$file")..."
242 fi
243
244 while IFS= read -r key; do
245 [[ -z "$key" ]] && continue
246
247 if [[ -z "${key_files[$key]}" ]]; then
248 key_files[$key]="$file"
249 key_counts[$key]=1
250 else
251 key_files[$key]="${key_files[$key]}|$file"
252 ((key_counts[$key]++))
253 fi
254 done < <(extract_keys "$file")
255 done
256
257 # Find and process duplicates
258 local duplicates_found=false
259
260 echo -e "${BLUE}Searching for duplicates...${NC}"
261 echo ""
262
263 for key in "${!key_counts[@]}"; do
264 if [[ ${key_counts[$key]} -gt 1 ]]; then
265 duplicates_found=true
266
267 # Split files for this key
268 IFS='|' read -ra files <<< "${key_files[$key]}"
269
270 # Let user choose which occurrence to keep
271 choose_occurrence_to_keep "$key" "${files[@]}"
272 fi
273 done
274
275 if [[ "$duplicates_found" == false ]]; then
276 echo -e "${GREEN}✓ No duplicate keys found!${NC}"
277 else
278 if [[ "$LIST_ONLY" == true ]]; then
279 echo -e "${YELLOW}List mode completed. Run again without -l to apply changes.${NC}"
280 else
281 echo -e "${GREEN}✓ Cleanup completed!${NC}"
282 fi
283 fi
284}
285
286# Check dependencies
287command -v grep >/dev/null 2>&1 || { echo -e "${RED}Error: 'grep' is not installed${NC}" >&2; exit 1; }
288command -v cut >/dev/null 2>&1 || { echo -e "${RED}Error: 'cut' is not installed${NC}" >&2; exit 1; }
289command -v sed >/dev/null 2>&1 || { echo -e "${RED}Error: 'sed' is not installed${NC}" >&2; exit 1; }
290
291# Main execution
292main