From fee799c96fdddb3bed214e0aae753bc00fb63053 Mon Sep 17 00:00:00 2001 From: shauryemahajanSF <143645277+shauryemahajanSF@users.noreply.github.com> Date: Fri, 14 Aug 2026 14:18:51 +0000 Subject: [PATCH] CI: promote app artifacts from release/26.8 to release/26.9 --- .github/ISSUE_TEMPLATE/new_app.yml | 2 +- .github/config/required-bm-locales.txt | 13 +++ .github/scripts/test-validate-locale-files.sh | 99 +++++++++++++++++++ .../test-validate-registry-translations.sh | 84 ++++++++++++++++ .github/scripts/test-validate-translations.sh | 47 +++++++-- .github/scripts/validate-locale-files.sh | 70 +++++++++++++ .../scripts/validate-registry-translations.sh | 29 ++++++ .github/scripts/validate-translations.sh | 35 ++----- CONTRIBUTING.md | 44 ++++----- 9 files changed, 362 insertions(+), 61 deletions(-) create mode 100644 .github/config/required-bm-locales.txt create mode 100755 .github/scripts/test-validate-locale-files.sh create mode 100755 .github/scripts/test-validate-registry-translations.sh create mode 100755 .github/scripts/validate-locale-files.sh create mode 100755 .github/scripts/validate-registry-translations.sh diff --git a/.github/ISSUE_TEMPLATE/new_app.yml b/.github/ISSUE_TEMPLATE/new_app.yml index 5bc3908..b56c7ec 100644 --- a/.github/ISSUE_TEMPLATE/new_app.yml +++ b/.github/ISSUE_TEMPLATE/new_app.yml @@ -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 diff --git a/.github/config/required-bm-locales.txt b/.github/config/required-bm-locales.txt new file mode 100644 index 0000000..ed997fc --- /dev/null +++ b/.github/config/required-bm-locales.txt @@ -0,0 +1,13 @@ +ar-MA +de +en-US +es +fr +it +ja +ko +nl +pl +pt +zh-CN +zh-TW diff --git a/.github/scripts/test-validate-locale-files.sh b/.github/scripts/test-validate-locale-files.sh new file mode 100755 index 0000000..bc74b0f --- /dev/null +++ b/.github/scripts/test-validate-locale-files.sh @@ -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 ]] diff --git a/.github/scripts/test-validate-registry-translations.sh b/.github/scripts/test-validate-registry-translations.sh new file mode 100755 index 0000000..2a7c02f --- /dev/null +++ b/.github/scripts/test-validate-registry-translations.sh @@ -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 ]] diff --git a/.github/scripts/test-validate-translations.sh b/.github/scripts/test-validate-translations.sh index ddf5b97..c6df4dd 100755 --- a/.github/scripts/test-validate-translations.sh +++ b/.github/scripts/test-validate-translations.sh @@ -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 @@ -19,14 +20,29 @@ LAST_RC=0 # tasksList: (writes app-configuration/tasksList.json) # adminComponents: (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%%:*}" @@ -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" } @@ -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" \ @@ -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" \ @@ -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" \ diff --git a/.github/scripts/validate-locale-files.sh b/.github/scripts/validate-locale-files.sh new file mode 100755 index 0000000..0d6d2eb --- /dev/null +++ b/.github/scripts/validate-locale-files.sh @@ -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 [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") [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))" diff --git a/.github/scripts/validate-registry-translations.sh b/.github/scripts/validate-registry-translations.sh new file mode 100755 index 0000000..e9c109b --- /dev/null +++ b/.github/scripts/validate-registry-translations.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash +# Validate locale files in commerce-apps-manifest/translations/. +# +# Usage: validate-registry-translations.sh +# +# Exit codes: +# 0 - locale files are valid +# 1 - required locale files are missing or filenames are misformatted +# 2 - usage error (bad args or missing directory) + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + +if [[ $# -ne 1 ]]; then + echo "Usage: $(basename "$0") " >&2 + exit 2 +fi + +translations_dir="$1" + +if [[ ! -d "$translations_dir" ]]; then + echo "Registry translations directory not found: $translations_dir" >&2 + exit 2 +fi + +bash "$SCRIPT_DIR/validate-locale-files.sh" \ + "$translations_dir" \ + "Registry translations directory" diff --git a/.github/scripts/validate-translations.sh b/.github/scripts/validate-translations.sh index 6416329..0c1560a 100755 --- a/.github/scripts/validate-translations.sh +++ b/.github/scripts/validate-translations.sh @@ -9,9 +9,8 @@ # 2 - usage error (bad args or unreadable input) # # Schema: -# - Locale filenames must be in the supported set (kept in sync with the -# "Supported locales" table in CONTRIBUTING.md). -# - en-US.json is required when the directory exists. +# - Every required default BM locale file must be present when the directory +# exists, and additional locale filenames must use the BM-supported BCP-47 format. # - Each locale file must be a JSON object with a "tasks" object whose # entries each have non-empty "name" and "description" strings. # Optional "adminComponents" object holds per-component "attributes" @@ -24,6 +23,8 @@ set -euo pipefail +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" + if [[ $# -ne 1 ]]; then echo "Usage: $(basename "$0") " >&2 exit 2 @@ -36,10 +37,6 @@ if [[ ! -d "$cap_root" ]]; then exit 2 fi -# Set of supported BM locales — must match the table in CONTRIBUTING.md -# under "Localizing app-shipped strings > Supported locales". -SUPPORTED_LOCALES=("ar-MA" "de" "en-US" "es" "fr" "it" "ja" "ko" "nl" "pl" "pt" "zh-CN" "zh-TW") - translations_dir="$cap_root/app-configuration/translations" if [[ ! -d "$translations_dir" ]]; then @@ -47,11 +44,11 @@ if [[ ! -d "$translations_dir" ]]; then exit 0 fi +bash "$SCRIPT_DIR/validate-locale-files.sh" \ + "$translations_dir" \ + "app-configuration/translations/" + default_locale="$translations_dir/en-US.json" -if [[ ! -f "$default_locale" ]]; then - echo "app-configuration/translations/ exists but en-US.json is missing" >&2 - exit 1 -fi # Validate per-file shape. shape_errors="" @@ -117,22 +114,6 @@ if [[ -n "$shape_errors" ]]; then exit 1 fi -# Locale filenames must be in the supported set. -unsupported_locales=() -while IFS= read -r locale_file; do - fname="$(basename "$locale_file" .json)" - ok=false - for supported in "${SUPPORTED_LOCALES[@]}"; do - [[ "$fname" == "$supported" ]] && ok=true && break - done - [[ "$ok" == "false" ]] && unsupported_locales+=("$fname") -done < <(find "$translations_dir" -mindepth 1 -maxdepth 1 -type f -name '*.json') - -if [[ ${#unsupported_locales[@]} -gt 0 ]]; then - echo "Unsupported locale file(s) in app-configuration/translations/: ${unsupported_locales[*]} (supported: ${SUPPORTED_LOCALES[*]})" >&2 - exit 1 -fi - # tasksList.json taskKey coverage in en-US.json. tasks_list="$cap_root/app-configuration/tasksList.json" if [[ -f "$tasks_list" ]]; then diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 3cb042c..6503766 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -440,7 +440,7 @@ commerce-{appName}-app-v{version}/ ├── tasksList.json ├── adminComponents.json (optional) └── translations/ - ├── en-US.json # required if translations/ exists + ├── en-US.json # canonical locale; all defaults are required ├── de.json ├── ja.json └── ... @@ -471,8 +471,9 @@ Rules: - The top-level `tasks` and `adminComponents` keys are reserved by the registry. Do not introduce sibling keys without coordinating with the registry team — future namespaces will follow the same reserved pattern. - Translatable fields per task: `name`, `description` (both required, non-empty, in every locale file that lists the task). - Translatable fields per admin component: `attributes..label` (required, non-empty, for every (componentKey, attribute id) pair listed in the locale file). -- Locale filenames must use the BCP-47 tag of a [supported BM locale](#supported-locales) (`en-US.json`, `de.json`, `fr.json`, …). -- `en-US.json` is **required** when `translations/` exists. It defines the canonical key set every other locale file must match exactly — no extra keys, no missing keys, in either namespace. +- Every [required default BM locale](#supported-locales) must be present when `translations/` exists. +- Additional locales are accepted when their filenames use the BM-supported BCP-47 format `^[a-z]{2}(-[A-Z]{2})?\.json$` (`en-US.json`, `de.json`, `fr-CA.json`, …). +- `en-US.json` defines the canonical key set every other locale file must match exactly — no extra keys, no missing keys, in either namespace. - The English text in `en-US.json` and the literal English in `tasksList.json` / `adminComponents.json` must stay in sync. If a string changes, update both. ### Fallback chain @@ -485,35 +486,23 @@ At render time the BM client requests a locale; the registry resolves each trans - tasks → `name` / `description` on the task itself in `tasksList.json` - admin component attribute labels → `label` on the attribute in `adminComponents.json` -Result: an app that omits `translations/` entirely keeps rendering literal English in every locale (existing behavior). An app that ships only `en-US.json` renders the same strings in every locale, but is wired up to add more locales later without further code changes. +Result: an app that omits `translations/` entirely keeps rendering literal English in every locale (existing behavior). Once an app adds `translations/`, it must provide every required default BM locale. ### Locale rollout -`en-US` is required at submission when `translations/` is present. Additional locales are optional and can be added incrementally as your localization team produces them — there is no requirement to ship all supported locales at once. +All required default BM locales are required at submission when `translations/` is present. Additional correctly named locales can be added as your localization team produces them. -> **Heads up:** translation files are bundled inside the CAP zip and merged into the persisted task list at install time. Adding or updating translations after the fact requires shipping a new app version and having merchants upgrade — there is no out-of-band path for the registry to push translation updates to an already-installed app. Plan to include `en-US.json` (at minimum) in any version that introduces translatable strings, even if other locales come later. +> **Heads up:** translation files are bundled inside the CAP zip and merged into the persisted task list at install time. Adding or updating translations after the fact requires shipping a new app version and having merchants upgrade — there is no out-of-band path for the registry to push translation updates to an already-installed app. Plan to include the complete required default locale set in any version that introduces translatable strings. ### Supported locales -Locale filenames are validated against the set of locales supported by Business Manager. The currently supported set is: - -| Locale | Filename | -|:--|:--| -| Arabic (Morocco) | `ar-MA.json` | -| German | `de.json` | -| English (United States) | `en-US.json` | -| Spanish | `es.json` | -| French | `fr.json` | -| Italian | `it.json` | -| Japanese | `ja.json` | -| Korean | `ko.json` | -| Dutch | `nl.json` | -| Polish | `pl.json` | -| Portuguese | `pt.json` | -| Chinese (Simplified) | `zh-CN.json` | -| Chinese (Traditional) | `zh-TW.json` | - -CI will reject locale files whose filenames are outside this set. +The authoritative required default locale set is +[`required-bm-locales.txt`](.github/config/required-bm-locales.txt). It applies +to both `commerce-apps-manifest/translations/` and any packaged +`app-configuration/translations/` directory. + +Both directories may include additional locale files only when each filename +matches the BM-supported BCP-47 format `^[a-z]{2}(-[A-Z]{2})?\.json$`. --- @@ -655,9 +644,10 @@ Before submitting your PR, please verify: ### Localization - [ ] Every entry in `tasksList.json` declares a unique non-empty `taskKey` matching `^[a-z][a-z0-9_]*$` - [ ] If `adminComponents.json` is shipped, every entry declares a unique non-empty `componentKey` matching `^[a-z][a-z0-9_]*$` -- [ ] If `app-configuration/translations/` exists, `en-US.json` is present and lists every `taskKey` from `tasksList.json` and every (componentKey, attribute id) pair from `adminComponents.json` +- [ ] `commerce-apps-manifest/translations/` and, when present, `app-configuration/translations/` contain every required default BM locale +- [ ] If `app-configuration/translations/` exists, `en-US.json` lists every `taskKey` from `tasksList.json` and every (componentKey, attribute id) pair from `adminComponents.json` - [ ] Every non-default locale file lists exactly the same keys as `en-US.json` (no extras, no missing) in both `tasks` and `adminComponents` namespaces -- [ ] All locale filenames are in the [supported locale set](#supported-locales) and use BCP-47 tags +- [ ] Additional locale filenames match the BM-supported BCP-47 format `^[a-z]{2}(-[A-Z]{2})?\.json$` - [ ] English text in `en-US.json` matches the literal English in `tasksList.json` and `adminComponents.json` ### Domain and Naming