Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/new_app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,7 @@ body:
required: true
- label: If `app-configuration/translations/` is included, `en-US.json` is present and covers every `taskKey` from `tasksList.json` and every (componentKey, attribute id) pair from `adminComponents.json`
required: true
- label: All locale filenames use BCP-47 tags from the supported set in CONTRIBUTING.md
- label: All required default BM locale files are present, and any additional locale filenames use the BM-supported BCP-47 format documented in CONTRIBUTING.md
required: true
- label: I have tested the app in a sandbox environment
required: true
Expand Down
13 changes: 13 additions & 0 deletions .github/config/required-bm-locales.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
ar-MA
de
en-US
es
fr
it
ja
ko
nl
pl
pt
zh-CN
zh-TW
99 changes: 99 additions & 0 deletions .github/scripts/test-validate-locale-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VALIDATE="$SCRIPT_DIR/validate-locale-files.sh"
REQUIRED_LOCALES="$SCRIPT_DIR/../config/required-bm-locales.txt"

PASS=0
FAIL=0
TMPDIR_ROOT="$(mktemp -d)"
trap 'rm -rf "$TMPDIR_ROOT"' EXIT

make_translations_dir() {
local translations_dir
translations_dir="$(mktemp -d "$TMPDIR_ROOT/translations.XXXXXX")"
while IFS= read -r locale || [[ -n "$locale" ]]; do
locale="${locale%$'\r'}"
[[ -z "$locale" ]] && continue
printf '{}\n' > "$translations_dir/$locale.json"
done < "$REQUIRED_LOCALES"
echo "$translations_dir"
}

assert_passes() {
local desc="$1"
local translations_dir="$2"
local output
if output="$(bash "$VALIDATE" "$translations_dir" "Test translations" 2>&1)"; then
echo " PASS: $desc"
PASS=$((PASS + 1))
else
echo " FAIL: $desc"
echo " output: $output"
FAIL=$((FAIL + 1))
fi
}

assert_rejects() {
local desc="$1"
local expect_substr="$2"
local translations_dir="$3"
local output rc=0
output="$(bash "$VALIDATE" "$translations_dir" "Test translations" 2>&1)" || rc=$?
if [[ "$rc" -eq 1 && "$output" == *"$expect_substr"* ]]; then
echo " PASS: $desc"
PASS=$((PASS + 1))
else
echo " FAIL: $desc (expected exit 1 containing '$expect_substr', got exit $rc)"
echo " output: $output"
FAIL=$((FAIL + 1))
fi
}

assert_usage_error() {
local desc="$1"
local expect_substr="$2"
shift 2
local output rc=0
output="$("$@" 2>&1)" || rc=$?
if [[ "$rc" -eq 2 && "$output" == *"$expect_substr"* ]]; then
echo " PASS: $desc"
PASS=$((PASS + 1))
else
echo " FAIL: $desc (expected exit 2 containing '$expect_substr', got exit $rc)"
echo " output: $output"
FAIL=$((FAIL + 1))
fi
}

echo "=== locale files validator tests ==="

translations_dir="$(make_translations_dir)"
assert_passes "all required locales are present" "$translations_dir"

translations_dir="$(make_translations_dir)"
rm "$translations_dir/fr.json"
assert_rejects "missing required locale is rejected" \
"missing required locale file(s): fr.json" "$translations_dir"

translations_dir="$(make_translations_dir)"
printf '{}\n' > "$translations_dir/fr-CA.json"
assert_passes "additional valid BCP-47 locale is accepted" "$translations_dir"

translations_dir="$(make_translations_dir)"
printf '{}\n' > "$translations_dir/zh_CN.json"
assert_rejects "misformatted locale filename is rejected" \
"misformatted locale file(s): zh_CN.json" "$translations_dir"

assert_usage_error "missing directory is a usage error" \
"not found" bash "$VALIDATE" "$TMPDIR_ROOT/missing"

translations_dir="$(make_translations_dir)"
assert_usage_error "missing required locale configuration is a usage error" \
"configuration not found" env REQUIRED_BM_LOCALES_FILE="$TMPDIR_ROOT/missing-locales.txt" \
bash "$VALIDATE" "$translations_dir"

echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
[[ "$FAIL" -eq 0 ]]
84 changes: 84 additions & 0 deletions .github/scripts/test-validate-registry-translations.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env bash
set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VALIDATE="$SCRIPT_DIR/validate-registry-translations.sh"
REQUIRED_LOCALES="$SCRIPT_DIR/../config/required-bm-locales.txt"

PASS=0
FAIL=0
TMPDIR_ROOT="$(mktemp -d)"
trap 'rm -rf "$TMPDIR_ROOT"' EXIT

make_translations_dir() {
local translations_dir
translations_dir="$(mktemp -d "$TMPDIR_ROOT/translations.XXXXXX")"
while IFS= read -r locale || [[ -n "$locale" ]]; do
locale="${locale%$'\r'}"
[[ -z "$locale" ]] && continue
printf '{}\n' > "$translations_dir/$locale.json"
done < "$REQUIRED_LOCALES"
for spec in "$@"; do
case "$spec" in
add:*)
printf '{}\n' > "$translations_dir/${spec#add:}.json"
;;
remove:*)
rm "$translations_dir/${spec#remove:}.json"
;;
*)
echo "Unknown fixture spec: $spec" >&2
exit 99
;;
esac
done
echo "$translations_dir"
}

assert_passes() {
local desc="$1"
shift
local translations_dir
translations_dir="$(make_translations_dir "$@")"
local output
if output="$(bash "$VALIDATE" "$translations_dir" 2>&1)"; then
echo " PASS: $desc"
PASS=$((PASS + 1))
else
echo " FAIL: $desc"
echo " output: $output"
FAIL=$((FAIL + 1))
fi
}

assert_rejects() {
local desc="$1"
local expect_substr="$2"
shift 2
local translations_dir
translations_dir="$(make_translations_dir "$@")"

local output rc=0
output="$(bash "$VALIDATE" "$translations_dir" 2>&1)" || rc=$?
if [[ "$rc" -eq 1 && "$output" == *"$expect_substr"* ]]; then
echo " PASS: $desc"
PASS=$((PASS + 1))
else
echo " FAIL: $desc (expected exit 1 containing '$expect_substr', got exit $rc)"
echo " output: $output"
FAIL=$((FAIL + 1))
fi
}

echo "=== registry translations validator tests ==="

assert_passes "all required default locale files are valid"
assert_passes "additional valid BCP-47 locale is accepted" add:fr-CA
assert_rejects "missing default locale is rejected" \
"missing required locale file(s): ja.json" remove:ja
assert_rejects "misformatted locale is rejected" \
"misformatted locale file(s): zh_CN.json" add:zh_CN

echo ""
echo "=== Results: $PASS passed, $FAIL failed ==="
[[ "$FAIL" -eq 0 ]]
47 changes: 41 additions & 6 deletions .github/scripts/test-validate-translations.sh
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
VALIDATE="$SCRIPT_DIR/validate-translations.sh"
REQUIRED_LOCALES="$SCRIPT_DIR/../config/required-bm-locales.txt"

PASS=0
FAIL=0
Expand All @@ -19,14 +20,29 @@ LAST_RC=0
# tasksList:<json> (writes app-configuration/tasksList.json)
# adminComponents:<json> (writes app-configuration/adminComponents.json)
# no-translations (skips creating translations dir)
# no-seed-required (leaves required locale files unseeded)
# seed-required-without-en-US (creates every required locale except en-US)
make_cap() {
local cap; cap="$(mktemp -d "$TMPDIR_ROOT/cap.XXXXXX")"
local has_translations=true
local seed_required=true
for spec in "$@"; do
case "$spec" in
no-translations)
has_translations=false
;;
no-seed-required)
seed_required=false
;;
seed-required-without-en-US)
seed_required=false
mkdir -p "$cap/app-configuration/translations"
while IFS= read -r locale || [[ -n "$locale" ]]; do
locale="${locale%$'\r'}"
[[ -z "$locale" || "$locale" == "en-US" ]] && continue
printf '%s' "$DE_BASIC" > "$cap/app-configuration/translations/$locale.json"
done < "$REQUIRED_LOCALES"
;;
tasks:*)
local rest="${spec#tasks:}"
local filename="${rest%%:*}"
Expand All @@ -51,6 +67,16 @@ make_cap() {
if [[ "$has_translations" == "true" && ! -d "$cap/app-configuration/translations" ]]; then
mkdir -p "$cap/app-configuration/translations"
fi
if [[ "$seed_required" == "true" && -f "$cap/app-configuration/translations/en-US.json" ]]; then
while IFS= read -r locale || [[ -n "$locale" ]]; do
locale="${locale%$'\r'}"
[[ -z "$locale" ]] && continue
if [[ ! -f "$cap/app-configuration/translations/$locale.json" ]]; then
cp "$cap/app-configuration/translations/en-US.json" \
"$cap/app-configuration/translations/$locale.json"
fi
done < "$REQUIRED_LOCALES"
fi
echo "$cap"
}

Expand Down Expand Up @@ -107,7 +133,7 @@ TASKS_LIST_OK='[{"taskKey":"setup_account","name":"Setup","description":"d","tas

assert_passes "missing translations dir is OK (optional)" no-translations

assert_passes "en-US only" "tasks:en-US.json:$EN_BASIC"
assert_passes "all required locales seeded from en-US" "tasks:en-US.json:$EN_BASIC"

assert_passes "en-US + matching de" \
"tasks:en-US.json:$EN_BASIC" \
Expand All @@ -122,11 +148,20 @@ assert_passes "adminComponents pair coverage and parity" \
"tasks:de.json:{\"tasks\":{\"setup_account\":{\"name\":\"S\",\"description\":\"d\"}},\"adminComponents\":{\"component_visibility\":{\"attributes\":{\"sfcc.checkout.shippingAddress.after\":{\"label\":\"Beim Checkout\"}}}}}" \
"adminComponents:{\"configuration\":[{\"componentKey\":\"component_visibility\",\"type\":\"storefrontComponentVisibility\",\"attributes\":[{\"id\":\"sfcc.checkout.shippingAddress.after\",\"label\":\"Show on Checkout\",\"defaultValue\":true}]}]}"

assert_passes "additional valid BCP-47 locale is accepted" \
"tasks:en-US.json:$EN_BASIC" \
"tasks:fr-CA.json:$EN_BASIC"

# --- Rejecting shapes ------------------------------------------------------

assert_rejects "translations dir without en-US.json is rejected" \
"en-US.json is missing" \
"tasks:de.json:$DE_BASIC"
"missing required locale file(s): en-US.json" \
seed-required-without-en-US

assert_rejects "translations dir missing required defaults is rejected" \
"missing required locale file(s)" \
no-seed-required \
"tasks:en-US.json:$EN_BASIC"

assert_rejects "invalid JSON locale is rejected" \
"not valid JSON" \
Expand All @@ -140,10 +175,10 @@ assert_rejects "empty name in en-US is rejected" \
"missing/invalid name or description" \
'tasks:en-US.json:{"tasks":{"setup_account":{"name":"","description":"d"}}}'

assert_rejects "unsupported locale filename is rejected" \
"Unsupported locale file" \
assert_rejects "misformatted locale filename is rejected" \
"misformatted locale file" \
"tasks:en-US.json:$EN_BASIC" \
"tasks:xx-YY.json:$EN_BASIC"
"tasks:zh_CN.json:$EN_BASIC"

assert_rejects "tasksList taskKey missing from en-US is rejected" \
"not present in translations/en-US.json" \
Expand Down
70 changes: 70 additions & 0 deletions .github/scripts/validate-locale-files.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#!/usr/bin/env bash
# Validate required and BM-supported BCP-47 locale filenames in a translations directory.
#
# Usage: validate-locale-files.sh <translations-dir> [label]
#
# Exit codes:
# 0 - all required locale files are present and filenames are valid
# 1 - required locale files are missing or filenames are misformatted
# 2 - usage or required-locale configuration error

set -euo pipefail

if [[ $# -lt 1 || $# -gt 2 ]]; then
echo "Usage: $(basename "$0") <translations-dir> [label]" >&2
exit 2
fi

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
required_locales_file="${REQUIRED_BM_LOCALES_FILE:-$SCRIPT_DIR/../config/required-bm-locales.txt}"
translations_dir="$1"
label="${2:-Translations directory}"

if [[ ! -d "$translations_dir" ]]; then
echo "$label not found: $translations_dir" >&2
exit 2
fi

if [[ ! -f "$required_locales_file" ]]; then
echo "Required BM locale configuration not found: $required_locales_file" >&2
exit 2
fi

missing_locales=()
while IFS= read -r locale || [[ -n "$locale" ]]; do
locale="${locale%$'\r'}"
[[ -z "$locale" ]] && continue
if [[ ! "$locale" =~ ^[a-z]{2}(-[A-Z]{2})?$ ]]; then
echo "Required BM locale configuration contains misformatted locale: $locale" >&2
exit 2
fi
if [[ ! -f "$translations_dir/$locale.json" ]]; then
missing_locales+=("$locale.json")
fi
done < "$required_locales_file"

misformatted_locales=()
while IFS= read -r locale_file; do
filename="$(basename "$locale_file")"
if [[ ! "$filename" =~ ^[a-z]{2}(-[A-Z]{2})?\.json$ ]]; then
misformatted_locales+=("$filename")
fi
done < <(find "$translations_dir" -mindepth 1 -maxdepth 1 -type f -name '*.json' | sort)

validation_failed=false
if [[ ${#missing_locales[@]} -gt 0 ]]; then
echo "$label is missing required locale file(s): ${missing_locales[*]}" >&2
validation_failed=true
fi

if [[ ${#misformatted_locales[@]} -gt 0 ]]; then
echo "$label contains misformatted locale file(s): ${misformatted_locales[*]} (expected BM-supported BCP-47 filename such as de.json or en-US.json)" >&2
validation_failed=true
fi

if [[ "$validation_failed" == "true" ]]; then
exit 1
fi

locale_count="$(find "$translations_dir" -mindepth 1 -maxdepth 1 -type f -name '*.json' | wc -l | tr -d ' ')"
echo "$label locale files are valid ($locale_count file(s))"
Loading
Loading