523 lines
17 KiB
Bash
Executable File
523 lines
17 KiB
Bash
Executable File
#!/bin/bash
|
||
|
||
################################################################################
|
||
# Swiss Football Matchdata - i18n Management Script
|
||
#
|
||
# Complete i18n workflow using wp-cli:
|
||
# 1. Extract strings from PHP/JS to .pot file
|
||
# 2. Translate .pot to specified languages
|
||
# 3. Generate .mo files from .po files
|
||
# 4. Clean up regional language variants
|
||
#
|
||
# Usage:
|
||
# ./dev-scripts/i18n-manage.sh [extract|translate|build|clean|all]
|
||
#
|
||
# Commands:
|
||
# extract - Extract i18n strings and generate .pot file
|
||
# translate - Generate .po files for all languages (requires jq)
|
||
# build - Generate .mo files from .po files
|
||
# clean - Remove regional language variants
|
||
# all - Run all steps: extract → translate → build → clean
|
||
#
|
||
# Configuration:
|
||
# Modify the CONFIG section below to match your environment
|
||
#
|
||
# Dependencies:
|
||
# - wp-cli (via docker compose when WP_CLI_WRAPPER is used)
|
||
# - jq (for JSON translation data handling)
|
||
# - msgfmt (for generating .mo files)
|
||
#
|
||
################################################################################
|
||
|
||
set -e
|
||
|
||
################################################################################
|
||
# CONFIGURATION - Modify these values for your environment
|
||
################################################################################
|
||
|
||
# WordPress CLI command wrapper (for docker environments)
|
||
# Use local wp-cli: WP_CLI_CMD="wp"
|
||
# Use docker compose with working directory: WP_CLI_CMD="docker compose -f ~/Development/wordpress-dev/wp-dev-compose.yml run -w /var/www/html/wp-content/plugins/swiss-football-matchdata --rm wp-cli"
|
||
WP_CLI_CMD="docker compose -f ~/Development/wordpress-dev/wp-dev-compose.yml run -w /var/www/html/wp-content/plugins/swiss-football-matchdata --rm wp-cli"
|
||
|
||
# Plugin slug
|
||
PLUGIN_SLUG="swi_foot_matchdata"
|
||
|
||
# Text domain (must match in plugin)
|
||
TEXT_DOMAIN="swi_foot_matchdata"
|
||
|
||
# Languages to support: language_code:language_name
|
||
# Format: LANGUAGE_CODE|LANGUAGE_NAME (space-separated for compatibility)
|
||
LANGUAGES="de_DE:German fr_FR:French it_IT:Italian en_US:English"
|
||
|
||
# Regional variants to clean up (remove if they exist)
|
||
# These will be removed to keep only the main language codes
|
||
REGIONAL_VARIANTS=(
|
||
"de_AT" # Austrian German
|
||
"de_CH" # Swiss German
|
||
)
|
||
|
||
################################################################################
|
||
# COLORS & OUTPUT
|
||
################################################################################
|
||
|
||
RED='\033[0;31m'
|
||
GREEN='\033[0;32m'
|
||
YELLOW='\033[1;33m'
|
||
BLUE='\033[0;34m'
|
||
CYAN='\033[0;36m'
|
||
NC='\033[0m'
|
||
|
||
log_info() {
|
||
echo -e "${BLUE}ℹ${NC} $1"
|
||
}
|
||
|
||
log_success() {
|
||
echo -e "${GREEN}✓${NC} $1"
|
||
}
|
||
|
||
log_warning() {
|
||
echo -e "${YELLOW}⚠${NC} $1"
|
||
}
|
||
|
||
log_error() {
|
||
echo -e "${RED}✗${NC} $1"
|
||
}
|
||
|
||
log_section() {
|
||
echo -e "\n${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
|
||
echo -e "${CYAN} $1${NC}"
|
||
echo -e "${CYAN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}\n"
|
||
}
|
||
|
||
################################################################################
|
||
# UTILITY FUNCTIONS
|
||
################################################################################
|
||
|
||
check_dependencies() {
|
||
local missing_deps=0
|
||
|
||
# Check for wp-cli
|
||
if ! eval "$WP_CLI_CMD --version" &>/dev/null; then
|
||
log_error "wp-cli not found or not accessible via: $WP_CLI_CMD"
|
||
missing_deps=1
|
||
fi
|
||
|
||
# Check for msgfmt (gettext tools)
|
||
if ! command -v msgfmt &>/dev/null; then
|
||
log_error "msgfmt not found. Install gettext tools:"
|
||
echo " macOS: brew install gettext"
|
||
echo " Ubuntu: sudo apt-get install gettext"
|
||
missing_deps=1
|
||
fi
|
||
|
||
# Check for jq (optional but recommended)
|
||
if ! command -v jq &>/dev/null; then
|
||
log_warning "jq not installed (optional). Install for better translation support:"
|
||
echo " macOS: brew install jq"
|
||
echo " Ubuntu: sudo apt-get install jq"
|
||
fi
|
||
|
||
if [[ $missing_deps -eq 1 ]]; then
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
get_language_name() {
|
||
local code=$1
|
||
for pair in $LANGUAGES; do
|
||
local lang_code="${pair%:*}"
|
||
local lang_name="${pair#*:}"
|
||
if [[ "$lang_code" == "$code" ]]; then
|
||
echo "$lang_name"
|
||
return 0
|
||
fi
|
||
done
|
||
echo "Unknown"
|
||
}
|
||
|
||
################################################################################
|
||
# EXTRACTION
|
||
################################################################################
|
||
|
||
extract_strings() {
|
||
log_section "Extracting i18n Strings"
|
||
|
||
local pot_file="languages/${PLUGIN_SLUG}.pot"
|
||
local exclude_patterns="node_modules,vendor,assets/build,wp-json"
|
||
|
||
log_info "Extracting strings to: $pot_file"
|
||
|
||
# Create languages directory if it doesn't exist
|
||
mkdir -p languages
|
||
|
||
# Use wp-cli to extract strings
|
||
# Note: wp-cli automatically handles:
|
||
# - Translators comments (/* translators: ... */)
|
||
# - Multiple placeholders with ordered syntax (%1$d, %2$s, etc.)
|
||
# - WordPress i18n functions (__, _e, _x, _n, etc.)
|
||
# Syntax: wp i18n make-pot <source-directory> <output-file-path>
|
||
log_info "Running wp-cli extraction command..."
|
||
log_info " Working directory (Docker): /var/www/html/wp-content/plugins/swiss-football-matchdata"
|
||
log_info " Plugin directory (local): $(pwd)"
|
||
|
||
# Execute the extraction with proper output handling
|
||
# WP_CLI_CMD is already configured with -w flag, so . refers to the plugin directory
|
||
local extract_output
|
||
extract_output=$(eval "$WP_CLI_CMD i18n make-pot --exclude=$exclude_patterns --domain=$TEXT_DOMAIN . languages/${PLUGIN_SLUG}.pot 2>&1" || echo "FAILED")
|
||
|
||
# Small delay to ensure file is written
|
||
sleep 1
|
||
|
||
# Check if extraction was successful by verifying the file exists
|
||
local retries=3
|
||
local pot_exists=0
|
||
|
||
while [[ $retries -gt 0 ]] && [[ $pot_exists -eq 0 ]]; do
|
||
if [[ -f "$pot_file" ]]; then
|
||
pot_exists=1
|
||
break
|
||
fi
|
||
|
||
((retries--))
|
||
if [[ $retries -gt 0 ]]; then
|
||
log_warning "Pot file not found yet, retrying... ($retries attempts left)"
|
||
sleep 1
|
||
fi
|
||
done
|
||
|
||
if [[ $pot_exists -eq 1 ]]; then
|
||
log_success "Extracted strings to: $pot_file"
|
||
|
||
# Show statistics
|
||
local string_count=$(grep -c "^msgid \"" "$pot_file" 2>/dev/null || echo "0")
|
||
string_count=$(echo "$string_count" | tr -d ' \n')
|
||
log_info "Total strings found: $string_count"
|
||
|
||
# Check for obsolete entries
|
||
local obsolete_count=$(grep -c "^#~" "$pot_file" 2>/dev/null || echo "0")
|
||
obsolete_count=$(echo "$obsolete_count" | tr -d ' \n')
|
||
if [[ $obsolete_count -gt 0 ]]; then
|
||
log_warning "Found $obsolete_count obsolete entries (marked with #~)"
|
||
fi
|
||
|
||
return 0
|
||
else
|
||
log_error "Failed to generate .pot file"
|
||
log_error "wp-cli output: $extract_output"
|
||
log_error "Checked path: $pot_file (local filesystem)"
|
||
log_info "Directory context:"
|
||
log_info " • Local plugin dir: $(pwd)"
|
||
log_info " • Docker plugin dir: /var/www/html/wp-content/plugins/swiss-football-matchdata"
|
||
log_info " • Expected output (local): $(pwd)/languages/${PLUGIN_SLUG}.pot"
|
||
log_info ""
|
||
log_info "If the file exists in Docker but not locally, verify:"
|
||
log_info " 1. Docker volume mounting is correct"
|
||
log_info " 2. Run: docker compose -f ~/Development/wordpress-dev/wp-dev-compose.yml exec wp-cli ls -la /var/www/html/wp-content/plugins/swiss-football-matchdata/"
|
||
return 1
|
||
fi
|
||
}
|
||
|
||
################################################################################
|
||
# TRANSLATION
|
||
################################################################################
|
||
|
||
translate_strings() {
|
||
log_section "Generating .po files from .pot"
|
||
|
||
local pot_file="languages/${PLUGIN_SLUG}.pot"
|
||
|
||
if [[ ! -f "$pot_file" ]]; then
|
||
log_error ".pot file not found: $pot_file"
|
||
log_info "Run 'extract' command first"
|
||
return 1
|
||
fi
|
||
|
||
local created_count=0
|
||
|
||
for pair in $LANGUAGES; do
|
||
local lang_code="${pair%:*}"
|
||
local lang_name="${pair#*:}"
|
||
local po_file="languages/${PLUGIN_SLUG}-${lang_code}.po"
|
||
|
||
log_info "Processing: $lang_name ($lang_code)"
|
||
|
||
if [[ -f "$po_file" ]]; then
|
||
# Update existing .po file
|
||
log_info " Updating existing .po file..."
|
||
eval "$WP_CLI_CMD i18n update-po languages/${PLUGIN_SLUG}.pot languages/${PLUGIN_SLUG}-${lang_code}.po 2>/dev/null" || true
|
||
log_success " Updated: $po_file"
|
||
else
|
||
# Create new .po file from .pot
|
||
log_info " Creating new .po file..."
|
||
cp "$pot_file" "$po_file"
|
||
|
||
# Update language headers in .po file
|
||
sed -i.bak "s/Language: /Language: ${lang_code}/" "$po_file"
|
||
rm -f "${po_file}.bak"
|
||
|
||
log_success " Created: $po_file"
|
||
((created_count++))
|
||
fi
|
||
done
|
||
|
||
log_success "Created/Updated $created_count translation files"
|
||
return 0
|
||
}
|
||
|
||
################################################################################
|
||
# BUILD .MO FILES
|
||
################################################################################
|
||
|
||
build_mo_files() {
|
||
log_section "Generating .mo files"
|
||
|
||
local compiled_count=0
|
||
local errors=0
|
||
|
||
for pair in $LANGUAGES; do
|
||
local lang_code="${pair%:*}"
|
||
local lang_name="${pair#*:}"
|
||
local po_file="languages/${PLUGIN_SLUG}-${lang_code}.po"
|
||
local mo_file="languages/${PLUGIN_SLUG}-${lang_code}.mo"
|
||
|
||
if [[ -f "$po_file" ]]; then
|
||
log_info "Compiling: $lang_name ($lang_code)"
|
||
|
||
if msgfmt -o "$mo_file" "$po_file" 2>/dev/null; then
|
||
log_success " Generated: $mo_file"
|
||
((compiled_count++))
|
||
else
|
||
log_error " Failed to compile: $po_file"
|
||
((errors++))
|
||
fi
|
||
else
|
||
log_warning " .po file not found: $po_file"
|
||
fi
|
||
done
|
||
|
||
log_success "Compiled $compiled_count .mo files"
|
||
|
||
if [[ $errors -gt 0 ]]; then
|
||
log_warning "Encountered $errors errors during compilation"
|
||
return 1
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
################################################################################
|
||
# CLEANUP
|
||
################################################################################
|
||
|
||
cleanup_regional_variants() {
|
||
log_section "Cleaning up regional language variants"
|
||
|
||
local removed_count=0
|
||
|
||
for variant in "${REGIONAL_VARIANTS[@]}"; do
|
||
# Check for .po files
|
||
local po_file="languages/${PLUGIN_SLUG}-${variant}.po"
|
||
if [[ -f "$po_file" ]]; then
|
||
log_info "Removing: $po_file"
|
||
rm -f "$po_file"
|
||
((removed_count++))
|
||
fi
|
||
|
||
# Check for .mo files
|
||
local mo_file="languages/${PLUGIN_SLUG}-${variant}.mo"
|
||
if [[ -f "$mo_file" ]]; then
|
||
log_info "Removing: $mo_file"
|
||
rm -f "$mo_file"
|
||
((removed_count++))
|
||
fi
|
||
|
||
# Also check for backup files created by sed
|
||
local po_backup="${po_file}.bak"
|
||
if [[ -f "$po_backup" ]]; then
|
||
rm -f "$po_backup"
|
||
fi
|
||
done
|
||
|
||
if [[ $removed_count -eq 0 ]]; then
|
||
log_info "No regional variants found to remove"
|
||
else
|
||
log_success "Removed $removed_count regional variant files"
|
||
fi
|
||
return 0
|
||
}
|
||
|
||
################################################################################
|
||
# DISPLAY SUMMARY
|
||
################################################################################
|
||
|
||
show_summary() {
|
||
log_section "i18n Management Summary"
|
||
|
||
echo -e "${YELLOW}📁 Language Files:${NC}\n"
|
||
|
||
for pair in $LANGUAGES; do
|
||
local lang_code="${pair%:*}"
|
||
local lang_name="${pair#*:}"
|
||
local po_file="languages/${PLUGIN_SLUG}-${lang_code}.po"
|
||
local mo_file="languages/${PLUGIN_SLUG}-${lang_code}.mo"
|
||
|
||
printf " %-20s %-20s " "$lang_code" "$lang_name"
|
||
|
||
if [[ -f "$po_file" ]] && [[ -f "$mo_file" ]]; then
|
||
local po_size=$(du -h "$po_file" | cut -f1)
|
||
local mo_size=$(du -h "$mo_file" | cut -f1)
|
||
echo -e "${GREEN}✓${NC} PO: $po_size, MO: $mo_size"
|
||
elif [[ -f "$po_file" ]]; then
|
||
local po_size=$(du -h "$po_file" | cut -f1)
|
||
echo -e "${YELLOW}⚠${NC} PO: $po_size (no .mo)"
|
||
else
|
||
echo -e "${RED}✗${NC} Missing files"
|
||
fi
|
||
done
|
||
|
||
local lang_count=$(echo "$LANGUAGES" | wc -w)
|
||
|
||
echo -e "\n${YELLOW}Configuration:${NC}\n"
|
||
echo " Plugin slug: $PLUGIN_SLUG"
|
||
echo " Text domain: $TEXT_DOMAIN"
|
||
echo " Languages: $lang_count"
|
||
echo " Languages dir: languages/"
|
||
|
||
echo -e "\n${YELLOW}Next Steps:${NC}\n"
|
||
echo " • Verify .po files contain all necessary translations"
|
||
echo " • Update empty translation entries in .po files"
|
||
echo " • Re-run 'build' to regenerate .mo files after translations"
|
||
echo ""
|
||
}
|
||
|
||
################################################################################
|
||
# HELP
|
||
################################################################################
|
||
|
||
show_help() {
|
||
cat << 'EOF'
|
||
Usage: ./dev-scripts/i18n-manage.sh [COMMAND]
|
||
|
||
COMMANDS:
|
||
extract Extract i18n strings from PHP/JS files to .pot
|
||
translate Generate .po files for all configured languages
|
||
build Compile .po files to .mo files
|
||
clean Remove regional language variants
|
||
all Run all steps: extract → translate → build → clean
|
||
help Show this help message
|
||
|
||
EXAMPLES:
|
||
# Extract strings and generate translations
|
||
./dev-scripts/i18n-manage.sh extract
|
||
|
||
# Generate .po files for translation
|
||
./dev-scripts/i18n-manage.sh translate
|
||
|
||
# Compile .po to .mo files
|
||
./dev-scripts/i18n-manage.sh build
|
||
|
||
# Run complete workflow
|
||
./dev-scripts/i18n-manage.sh all
|
||
|
||
# Clean up regional variants
|
||
./dev-scripts/i18n-manage.sh clean
|
||
|
||
CONFIGURATION:
|
||
Edit the script's CONFIG section to adjust:
|
||
- WP_CLI_CMD: WordPress CLI command wrapper
|
||
- LANGUAGES: Supported language codes
|
||
- REGIONAL_VARIANTS: Language variants to remove
|
||
|
||
TRANSLATORS COMMENTS:
|
||
In PHP code, add translators comments above i18n functions:
|
||
|
||
/* translators: %s is the error message from the API */
|
||
__('Error loading teams: %s', 'swi_foot_matchdata')
|
||
|
||
For multiple placeholders, use ordered syntax:
|
||
|
||
/* translators: %1$d is count, %2$d is duration in seconds */
|
||
__('Currently caching %1$d records with %2$d second duration.', 'swi_foot_matchdata')
|
||
|
||
SUPPORTED LANGUAGES (default):
|
||
de_DE - German
|
||
fr_FR - French
|
||
it_IT - Italian
|
||
en_US - English (US)
|
||
|
||
DEPENDENCIES:
|
||
- wp-cli (WordPress CLI)
|
||
- msgfmt (gettext tools)
|
||
- jq (optional, for translation management)
|
||
|
||
EOF
|
||
}
|
||
|
||
################################################################################
|
||
# MAIN
|
||
################################################################################
|
||
|
||
main() {
|
||
local command="${1:-all}"
|
||
|
||
# Show banner
|
||
echo -e "${CYAN}╔════════════════════════════════════════════════╗${NC}"
|
||
echo -e "${CYAN}║ Swiss Football Matchdata - i18n Management ║${NC}"
|
||
echo -e "${CYAN}╚════════════════════════════════════════════════╝${NC}\n"
|
||
|
||
# Check dependencies
|
||
log_info "Checking dependencies..."
|
||
if ! check_dependencies; then
|
||
log_error "Missing required dependencies. Please install and try again."
|
||
exit 1
|
||
fi
|
||
log_success "All dependencies available\n"
|
||
|
||
# Execute command
|
||
case "$command" in
|
||
extract)
|
||
extract_strings
|
||
;;
|
||
translate)
|
||
translate_strings
|
||
show_summary
|
||
;;
|
||
build)
|
||
build_mo_files
|
||
show_summary
|
||
;;
|
||
clean)
|
||
cleanup_regional_variants
|
||
;;
|
||
all)
|
||
extract_strings && \
|
||
translate_strings && \
|
||
build_mo_files && \
|
||
cleanup_regional_variants && \
|
||
show_summary
|
||
;;
|
||
help|--help|-h)
|
||
show_help
|
||
;;
|
||
*)
|
||
log_error "Unknown command: $command"
|
||
echo ""
|
||
show_help
|
||
exit 1
|
||
;;
|
||
esac
|
||
|
||
local exit_code=$?
|
||
if [[ $exit_code -eq 0 ]]; then
|
||
log_success "\nTask completed successfully!"
|
||
else
|
||
log_error "\nTask failed with exit code $exit_code"
|
||
fi
|
||
|
||
exit $exit_code
|
||
}
|
||
|
||
# Run main function with all arguments
|
||
main "$@"
|