Skip to content

updated migration guide #66

updated migration guide

updated migration guide #66

# ------------------------------------------------------------------------------
# Frenglish Translation GitHub Action
#
# Workflow summary
# - PRs - Internal: always translate the diff to the PR base.
# • Example 1: Open PR => feature_1 → main
# • Example 2: Open PR => feature_1_fix → feature_1
#
# - PRs - External: Only translate when external PR is merged
# • Example: merge PR => fork → main (from a contributor fork)
#
# - Default‑branch pushes: translate unless
# a) author is github‑actions[bot], or
# b) the push includes a commit with “chore(i18n): update translations”
# • Example: Hotfix push → main
#
# - Diff logic: compares to fork-point for PRs, or previous commit on main for direct pushes.
# - If changes are found: handles file renames/deletes, runs translation and formatting,
# then commits and pushes a single update from the bot.
# ------------------------------------------------------------------------------
name: Frenglish Translation
on:
# Run once per pull‑request (feature → any target)
pull_request:
types: [opened, synchronize, reopened]
# Run again only when commits land on the default branch (e.g. master/main)
push:
branches:
- '**' # We filter below
permissions:
contents: read
jobs:
translate_and_format:
# Run if (a) it’s a PR OR (b) it’s a push *and* the ref equals the repo’s default branch
if: >-
github.event_name == 'pull_request' ||
(
github.event_name == 'push' &&
github.ref == format('refs/heads/{0}', github.event.repository.default_branch) &&
!contains(github.event.head_commit.author.name, 'github-actions[bot]') &&
!startsWith(github.event.head_commit.message, 'Merge ')
)
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
# We check if commit message include `chore(i18n): update translations` and assume it's been fully translated if so
- name: Detect translation commit in push range
id: detect
if: github.event_name == 'push' # PRs always run
run: |
echo "Looking for 'chore(i18n): update translations' between ${{ github.event.before }}..${{ github.sha }}"
if git log --format=%B ${{ github.event.before }}..${{ github.sha }} | grep -qF 'chore(i18n): update translations'; then
echo "skip=true" >> "$GITHUB_OUTPUT"
else
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- name: Checkout code
if: steps.detect.outputs.skip != 'true'
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0
- name: Setup Node.js
if: steps.detect.outputs.skip != 'true'
uses: actions/setup-node@v3
with:
node-version: '18' # Or your preferred Node.js version >= 16
- name: Install dependencies
if: steps.detect.outputs.skip != 'true'
run: |
# Ensure you have a package.json and package-lock.json
# Add @frenglish/sdk to your package.json: npm install @frenglish/sdk --save
npm install
- name: Setup Git User
if: steps.detect.outputs.skip != 'true'
run: |
git config --global user.email "github-actions[bot]@users.noreply.github.com"
git config --global user.name "github-actions[bot]"
- name: Get Language Configuration
if: steps.detect.outputs.skip != 'true'
id: get_lang_config
run: node .github/scripts/fetch-frenglish-configuration.js
env:
FRENGLISH_API_KEY: ${{ secrets.FRENGLISH_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# --- Step to Handle Renamed/Deleted Files ---
- name: Handle Renamed and Deleted Source Files
if: steps.detect.outputs.skip != 'true'
id: handle_changes
run: |
set -e # Exit immediately if a command exits with a non-zero status.
# Get target languages and commit SHAs
# SOURCE_DIR_RAW from get_lang_config is now IGNORED for path determination
TARGET_DIRS_STRING="${{ steps.get_lang_config.outputs.target_langs }}"
BEFORE_SHA="${{ github.event.before }}"
CURRENT_SHA="${{ github.sha }}"
# --- Define the source path CONSISTENTLY with translate.js ---
# ORIGIN_LANGUAGE_DIR in translate.js is path.resolve('.'), so we use '.' here.
EFFECTIVE_SOURCE_PATH="."
echo "Source file location for rename/delete check: Root Directory (.)"
# --- Validate Target Languages ---
if [ -z "$TARGET_DIRS_STRING" ]; then
echo "::warning::No target languages determined. Rename/delete actions for target directories will be skipped."
echo "processed_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
read -r -a TARGET_DIRS <<< "$TARGET_DIRS_STRING"
if [ ${#TARGET_DIRS[@]} -eq 0 ]; then
echo "::warning::No target languages parsed. Rename/delete actions for target directories will be skipped."
echo "processed_changes=false" >> $GITHUB_OUTPUT
exit 0
fi
# --- List of top-level files/dirs to EXCLUDE from rename/delete handling ---
# Add any other known non-locale files/folders residing in your root directory
# Use trailing slash for directories to avoid matching files starting with the same name
EXCLUDED_PATTERNS=(
'package.json'
'package-lock.json'
'node_modules/'
'frenglish.config.json'
'.github/'
'.git/'
'.gitignore'
'README.md'
# Add other files/dirs like 'vite.config.js', 'tsconfig.json', etc. if they exist in root
)
echo "Excluding patterns: ${EXCLUDED_PATTERNS[*]}"
# --- Check for Renamed/Deleted Files in the Root Directory ---
echo "Checking for renamed/deleted files in '$EFFECTIVE_SOURCE_PATH' between $BEFORE_SHA and $CURRENT_SHA..."
processed_any_change=false
# Use NUL delimiters, check within the root directory (.)
git diff --name-status --find-renames -z $BEFORE_SHA $CURRENT_SHA -- "$EFFECTIVE_SOURCE_PATH" | while IFS= read -r -d $'\0' status && IFS= read -r -d $'\0' old_path && IFS= read -r -d $'\0' new_path; do
# Handle cases where new_path might not be present (for deletions)
if [ -z "$new_path" ]; then
new_path=$old_path
fi
# --- Calculate relative paths (already relative to root) ---
relative_old_path="$old_path"
relative_new_path="$new_path"
# --- Filter out EXCLUDED top-level files/directories ---
is_excluded=false
for pattern in "${EXCLUDED_PATTERNS[@]}"; do
# Check if old_path starts with or exactly matches the pattern
if [[ "$old_path" == "$pattern"* ]]; then
is_excluded=true
echo "Skipping excluded file/path based on pattern '$pattern': $old_path"
break # Exit inner loop once matched
fi
done
if [ "$is_excluded" = true ]; then
continue # Skip to the next file in the diff
fi
# --- End of exclusion filter ---
# Proceed only if the file wasn't excluded
echo "Detected potentially relevant change: Status=$status, Old Path=$old_path, New Path=$new_path"
for TARGET_DIR in "${TARGET_DIRS[@]}"; do # Iterate over array elements correctly
# Ensure target *directory* exists (e.g., 'ja', 'es')
if [ ! -d "$TARGET_DIR" ]; then
echo "::warning::Target directory '$TARGET_DIR' not found. Skipping for this language."
continue
fi
# Construct target paths using the relative path from root
target_old_path="$TARGET_DIR/$relative_old_path"
if [[ "$status" == D* ]]; then
# Delete corresponding file in target dir IF it exists
if [ -f "$target_old_path" ]; then
echo "Deleting corresponding file: $target_old_path"
git rm "$target_old_path"
processed_any_change=true
else
# It's okay if the target file doesn't exist, don't warn loudly.
echo "Corresponding file for deletion not found (or already deleted): $target_old_path"
fi
elif [[ "$status" == R* ]]; then
# Rename corresponding file in target dir IF it exists
target_new_path="$TARGET_DIR/$relative_new_path"
target_new_path_dir=$(dirname "$target_new_path")
if [ -f "$target_old_path" ]; then
# Create parent directory for target if needed
if [ ! -d "$target_new_path_dir" ]; then
echo "Creating directory for renamed file: $target_new_path_dir"
mkdir -p "$target_new_path_dir"
fi
echo "Renaming corresponding file: $target_old_path -> $target_new_path"
git mv "$target_old_path" "$target_new_path"
processed_any_change=true
else
# It's okay if the target file doesn't exist, don't warn loudly.
echo "Corresponding file for rename not found: $target_old_path"
fi
fi # End status check (D or R)
done # end loop target dirs
done # end loop git diff
# Output based on the flag
echo "processed_changes=$processed_any_change" >> $GITHUB_OUTPUT
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Run translation script (Writes/Updates files)
if: steps.detect.outputs.skip != 'true'
env:
FRENGLISH_API_KEY: ${{ secrets.FRENGLISH_API_KEY }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: node .github/scripts/translate.js
- name: Stage ALL changes (new, modified, deleted, renamed)
if: steps.detect.outputs.skip != 'true'
run: |
echo "Staging all tracked changes (adds, modifications, deletes, renames)..."
git add . # This stages all changes in the working directory
- name: Commit changes
if: steps.detect.outputs.skip != 'true'
id: commit
run: |
# Check index status after all operations (add, rm, mv)
# Use --cached to check staged changes specifically
if git diff --cached --quiet; then
echo "No changes staged for commit."
echo "changes_committed=false" >> $GITHUB_OUTPUT
else
echo "Committing translation updates, formatting, renames, and deletions..."
# Use the dynamically fetched source language in the commit message
COMMIT_SOURCE_LANG="${{ steps.get_lang_config.outputs.source_lang }}" # Capture output first
git commit -m "chore(i18n): update translations [${COMMIT_SOURCE_LANG:-unknown}]" \
-m "Sync file structure, format locales. Branch: ${{ github.ref_name }}"
echo "changes_committed=true" >> $GITHUB_OUTPUT
git show --stat # Show commit details
fi
- name: Push changes
# run only when we actually committed something
if: steps.detect.outputs.skip != 'true' && steps.commit.outputs.changes_committed == 'true'
env:
# use head branch for PRs, ref_name for normal pushes
TARGET_REF: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || github.ref_name }}
run: |
echo "Pushing changes to origin/${TARGET_REF}..."
git push origin HEAD:${TARGET_REF}