diff --git a/Makefile b/Makefile index 37879b8..fa4acf4 100644 --- a/Makefile +++ b/Makefile @@ -22,7 +22,7 @@ help: @echo " logs - Show logs from all services" @echo " db-clean - Clean up all Docker resources" @echo " prune - Clean up everything (containers, images, volumes)" - @echo " i18n - Validate translation files" + @echo " i18n - List missing keys in translation file (use FILE=path/to/file.json)" @echo " lint - Lint the project" @echo " format - Format the project" @echo " migrate-up - Apply invite-only events migration" @@ -94,8 +94,10 @@ format: @echo "Formatting the project..." npm run format -#TODO: not working yet +# List missing keys in a translation file i18n: - @echo "Validating translation files..." - @if [ -n "$(FILE)" ]; then \ - ./scripts/i18n-check.sh $(FILE); \ + @if [ -z "$(FILE)" ]; then \ + echo "Error: FILE variable is required. Example: make i18n FILE=src/lib/i18n/it.json"; \ + exit 1; \ + fi + @./scripts/i18n-check.sh --missing-only $(FILE) diff --git a/scripts/i18n-check.sh b/scripts/i18n-check.sh index 865e69a..9a0e7a2 100755 --- a/scripts/i18n-check.sh +++ b/scripts/i18n-check.sh @@ -1,191 +1,89 @@ -#!/bin/bash +#!/usr/bin/env bash +# Find keys present in messages.json but missing in a translation file. -# Translation validation script -# Compares a translation file against the source messages.json to find missing keys +set -euo pipefail -set -e +SOURCE_DEFAULT="src/lib/i18n/messages.json" -# Colors for output -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -NC='\033[0m' # No Color - -# Default paths -SOURCE_FILE="src/lib/i18n/messages.json" -SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" - -# Function to show usage -show_usage() { - echo "Usage: $0 [LANGUAGE_FILE]" - echo "" - echo "Validates a translation file against the source messages.json" - echo "" - echo "Arguments:" - echo " LANGUAGE_FILE Path to the translation file to validate (e.g., src/lib/i18n/it.json)" - echo "" - echo "Examples:" - echo " $0 src/lib/i18n/it.json" - echo " $0 src/lib/i18n/fr.json" - echo "" - echo "If no file is provided, it will check all .json files in src/lib/i18n/ except messages.json" -} - -# Function to get all keys from a JSON file recursively -get_keys() { - local file="$1" - local prefix="$2" - - # Use jq to extract all keys recursively - jq -r 'paths(scalars) as $p | $p | join(".")' "$file" | while read -r key; do - if [ -n "$prefix" ]; then - echo "${prefix}.${key}" - else - echo "$key" - fi - done -} - -# Function to validate a single translation file -validate_file() { - local translation_file="$1" - local source_file="$2" - - echo -e "${YELLOW}Validating: $translation_file${NC}" - echo "----------------------------------------" - - # Check if files exist - if [ ! -f "$source_file" ]; then - echo -e "${RED}Error: Source file $source_file not found${NC}" - return 1 - fi - - if [ ! -f "$translation_file" ]; then - echo -e "${RED}Error: Translation file $translation_file not found${NC}" - return 1 - fi - - # Get all keys from source file - local source_keys - source_keys=$(get_keys "$source_file") - - # Get all keys from translation file - local translation_keys - translation_keys=$(get_keys "$translation_file") - - # Find missing keys - local missing_keys - missing_keys=$(comm -23 <(echo "$source_keys" | sort) <(echo "$translation_keys" | sort)) - - # Find extra keys (in translation but not in source) - local extra_keys - extra_keys=$(comm -13 <(echo "$source_keys" | sort) <(echo "$translation_keys" | sort)) - - # Count missing and extra keys - local missing_count - if [ -z "$missing_keys" ]; then - missing_count=0 - else - missing_count=$(echo "$missing_keys" | wc -l | tr -d ' ') - fi - - local extra_count - if [ -z "$extra_keys" ]; then - extra_count=0 - else - extra_count=$(echo "$extra_keys" | wc -l | tr -d ' ') - fi - - # Report results - if [ "$missing_count" -eq 0 ] && [ "$extra_count" -eq 0 ]; then - echo -e "${GREEN} Perfect! All keys match.${NC}" - return 0 - fi - - if [ "$missing_count" -gt 0 ]; then - echo -e "${RED} Missing $missing_count key(s) in translation:${NC}" - echo "$missing_keys" | while read -r key; do - echo -e " ${RED}• $key${NC}" - done - echo "" - fi - - if [ "$extra_count" -gt 0 ]; then - echo -e "${YELLOW} Extra $extra_count key(s) in translation (not in source):${NC}" - echo "$extra_keys" | while read -r key; do - echo -e " ${YELLOW}• $key${NC}" - done - echo "" - fi - - # Return error code if there are missing keys - if [ "$missing_count" -gt 0 ]; then - return 1 - fi - - return 0 -} - -# Main function -main() { - local translation_file="$1" - local source_file="$PROJECT_ROOT/$SOURCE_FILE" - local exit_code=0 - - # Change to project root directory - cd "$PROJECT_ROOT" - - # If no file specified, check all translation files - if [ -z "$translation_file" ]; then - echo -e "${YELLOW}No file specified. Checking all translation files...${NC}" - echo "" - - # Find all .json files in i18n directory except messages.json - local files - files=$(find src/lib/i18n -name "*.json" -not -name "messages.json" 2>/dev/null || true) - - if [ -z "$files" ]; then - echo -e "${YELLOW}No translation files found in src/lib/i18n/${NC}" - return 0 - fi - - # Validate each file - echo "$files" | while read -r file; do - if [ -n "$file" ]; then - if ! validate_file "$file" "$source_file"; then - exit_code=1 - fi - echo "" - fi - done - - return $exit_code - fi - - # Validate the specified file - if ! validate_file "$translation_file" "$source_file"; then - exit_code=1 - fi - - return $exit_code +usage() { + echo "Usage: $0 [--missing-only] [source_messages.json]" + echo "Compares against messages.json and prints missing keys." + exit 1 } # Check if jq is installed -if ! command -v jq &> /dev/null; then - echo -e "${RED}Error: jq is required but not installed.${NC}" - echo "Please install jq:" - echo " macOS: brew install jq" - echo " Ubuntu/Debian: sudo apt-get install jq" - echo " CentOS/RHEL: sudo yum install jq" - exit 1 +command -v jq >/dev/null 2>&1 || { + echo "Error: jq is required but not installed." >&2 + echo "Please install jq:" >&2 + echo " macOS: brew install jq" >&2 + echo " Ubuntu/Debian: sudo apt-get install jq" >&2 + echo " CentOS/RHEL: sudo yum install jq" >&2 + exit 127 +} + +# Parse arguments (handle --missing-only flag for Makefile compatibility) +TRANSLATION="" +SOURCE="$SOURCE_DEFAULT" + +while [[ $# -gt 0 ]]; do + case "$1" in + --missing-only|-m) + # Flag is accepted but ignored (this script only does missing keys) + shift + ;; + --help|-h) + usage + ;; + -*) + echo "Error: Unknown option: $1" >&2 + usage + ;; + *) + if [[ -z "$TRANSLATION" ]]; then + TRANSLATION="$1" + elif [[ "$SOURCE" == "$SOURCE_DEFAULT" ]]; then + SOURCE="$1" + else + echo "Error: Too many arguments" >&2 + usage + fi + shift + ;; + esac +done + +# Validate arguments +[[ -z "$TRANSLATION" ]] && { + echo "Error: Translation file is required" >&2 + usage +} + +# Validate files exist +[[ -f "$SOURCE" ]] || { + echo "Error: Source file not found: $SOURCE" >&2 + exit 1 +} + +[[ -f "$TRANSLATION" ]] || { + echo "Error: Translation file not found: $TRANSLATION" >&2 + exit 1 +} + +# Extract all keys from a JSON file (dot-joined paths to scalar values) +keys() { + jq -r 'paths(scalars) | join(".")' "$1" | sort -u +} + +# Find missing keys: in SOURCE but not in TRANSLATION +missing=$(comm -23 <(keys "$SOURCE") <(keys "$TRANSLATION")) + +if [[ -z "$missing" ]]; then + echo "No missing keys found." + exit 0 fi -# Handle help flag -if [ "$1" = "-h" ] || [ "$1" = "--help" ]; then - show_usage - exit 0 -fi - -# Run main function -main "$1" +echo "Missing keys in $(basename "$TRANSLATION"):" +echo "$missing" | sed 's/^/ - /' +echo +echo "Total missing keys: $(echo "$missing" | wc -l | tr -d ' ')" +exit 1