Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Sync strings from poeditor with github workflow #1897

Merged
merged 3 commits into from
Mar 10, 2025

Conversation

Cihatata
Copy link
Collaborator

POEditor Translation Synchronization

This GitHub Actions workflow automatically downloads translation files from POEditor and integrates them into the project.

How It Works

The workflow can be triggered in two ways:

  1. Manual Trigger: You can manually run the "POEditor Translation Synchronization" workflow from the "Actions" tab in the GitHub interface.
  2. Automatic Trigger: The workflow runs automatically every day at midnight (UTC).

Required Settings

For this workflow to function, you need to define the following secrets in your GitHub repository:

  1. POEDITOR_API_TOKEN: Your POEditor API token
  2. POEDITOR_PROJECT_ID: Your POEditor project ID

You can add these secrets in the "Settings > Secrets and variables > Actions" section of your GitHub repository.

Manual Execution

When running the workflow manually, you can specify which languages to download. Languages should be entered as comma-separated values (e.g., tr,gb,es).

If you don't specify any languages, the default languages tr and en will be downloaded.

Output

When the workflow completes successfully:

  1. Translation files for the specified languages are downloaded from POEditor
  2. These files are copied to the src/locales/ directory
  3. Changes are automatically committed and pushed to the main branch

Troubleshooting

If the workflow fails:

  1. Check the GitHub Actions logs
  2. Make sure your POEditor API token and project ID are correct
  3. Ensure that the languages you specified exist in your POEditor project

@Cihatata Cihatata changed the title feat: Sync strings from poeditor with github workfliw feat: Sync strings from poeditor with github workflow Mar 10, 2025
Copy link

@llamapreview llamapreview bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Auto Pull Request Review from LlamaPReview

1. Overview

1.1 Core Changes

  • Primary purpose and scope: This PR introduces a GitHub Actions workflow to automate the synchronization of translation strings from POEditor to the Checkmate project. This automation streamlines the localization process, reduces manual effort, and ensures translations are kept up-to-date with the source of truth in POEditor.
  • Key components modified:
    • .github/scripts/download-translations.js: A new JavaScript script to download translations from POEditor.
    • .github/workflows/README.md: Documentation for the GitHub Actions workflow.
    • .github/workflows/poeditor-sync.yml: The GitHub Actions workflow definition.
  • Cross-component impacts: The workflow interacts with the POEditor API, GitHub Secrets, and the file system to download, verify, and commit translation files.
  • Business value alignment: This feature enhances the localization workflow, making it more efficient and less error-prone, thereby improving the overall user experience for multilingual users.

1.2 Technical Architecture

  • System design modifications: The introduction of a GitHub Actions workflow to automate the translation synchronization process.
  • Component interaction changes: The workflow orchestrates interactions between GitHub Actions, Node.js, POEditor API, and the Git repository.
  • Integration points impact: The workflow integrates with POEditor for translation data and uses GitHub Secrets for secure API token management.
  • Dependency changes and implications: The workflow introduces dependencies on axios and fs-extra for API calls and file operations, respectively. These dependencies are standard and well-maintained.

2. Critical Findings

2.1 Must Fix (P0🔴)

[List critical issues one by one that must be addressed before merging]
Issue: None

2.2 Should Fix (P1🟡)

[List important improvements one by one that should be implemented]
Issue: Inconsistent Default Languages

  • Impact: The script defaults to tr,en, while the workflow's env section uses tr,gb. The README mentions tr,gb as default in manual execution section but tr,en in the output section. This inconsistency can be confusing.
  • Suggested Solution: Standardize the default languages to a single set (e.g., tr,en) and ensure consistency across the script, workflow definition, and documentation.

2.3 Consider (P2🟢)

[List minor suggestions worth considering]
Area: Input Validation and Logging

  • Improvement Opportunity: Add more robust input validation to the workflow and enhance logging in the script to improve debugging and observability.

2.4 Summary of Action Items

  • P1: Address the inconsistency in default languages by standardizing to tr,en across the script, workflow, and documentation.
  • P2: Consider adding more robust input validation to the workflow and enhancing logging in the script for better debugging and observability.

3. Technical Analysis

3.1 Code Logic Analysis

📁 .github/scripts/download-translations.js - downloadTranslations function

  • Submitted PR Code:
    async function downloadTranslations() {
      try {
        console.log('Downloading translations from POEditor...');
        console.log(`Using export format: ${EXPORT_FORMAT}`);

        for (const language of LANGUAGES) {
          console.log(`Downloading translations for ${language} language...`);

          // Get export URL from POEditor
          const exportResponse = await axios.post(`${API_URL}/projects/export`,
            new URLSearchParams({
              api_token: API_TOKEN,
              id: PROJECT_ID,
              language: language,
              type: EXPORT_FORMAT
            })
          );

          if (exportResponse.data.response.status !== 'success') {
            throw new Error(`Failed to get export URL for ${language} language: ${JSON.stringify(exportResponse.data)}`);
          }

          const fileUrl = exportResponse.data.result.url;
          console.log(`Export URL obtained for ${language}`);

          // Download translation file
          const downloadResponse = await axios.get(fileUrl, { responseType: 'json' });
          const translations = downloadResponse.data;
          console.log(`Downloaded translations for ${language}`);

          // POEditor'den dönen veri formatını kontrol et ve gerekirse dönüştür
          let formattedTranslations = translations;

          // Eğer dizi formatında geldiyse, key-value formatına dönüştür
          if (Array.isArray(translations)) {
            console.log(`Converting array format to key-value format for ${language}`);
            formattedTranslations = {};
            translations.forEach(item => {
              if (item.term && item.definition) {
                formattedTranslations[item.term] = item.definition;
              }
            });
          }

          // Save file
          const outputPath = path.join(process.cwd(), 'temp', `${language}.json`);
          await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 });

          console.log(`Translations for ${language} language successfully downloaded and saved: ${outputPath}`);
        }

        console.log('All translations successfully downloaded!');
      } catch (error) {
        console.error('An error occurred while downloading translations:', error);
        process.exit(1);
      }
    }
  • Analysis:
    • Current logic and potential issues: The script correctly handles the download and conversion of translations from POEditor. The use of axios for API calls and fs-extra for file operations is appropriate. The conversion from array format to key-value format is a good handling of potential POEditor API response variations. Error handling with try-catch is present.
    • Edge cases and error handling: The script includes try-catch blocks to handle errors during API calls and file operations. It checks exportResponse.data.response.status for API success. It also includes process.exit(1) on errors, which will fail the GitHub Action step.
    • Cross-component impact : The script interacts with the POEditor API, the file system, and the GitHub Actions environment.
    • Business logic considerations : The script aligns with the typical workflow for localization management, ensuring translations are kept up-to-date.
  • LlamaPReview Suggested Improvements:
    async function downloadTranslations() {
      try {
        console.log('Downloading translations from POEditor...');
        console.log(`Using export format: ${EXPORT_FORMAT}`);

        for (const language of LANGUAGES) {
          console.log(`Downloading translations for ${language} language...`);

          // Get export URL from POEditor
          const exportResponse = await axios.post(`${API_URL}/projects/export`,
            new URLSearchParams({
              api_token: API_TOKEN,
              id: PROJECT_ID,
              language: language,
              type: EXPORT_FORMAT
            })
          );

          if (exportResponse.data.response.status !== 'success') {
            throw new Error(`Failed to get export URL for ${language} language: ${JSON.stringify(exportResponse.data)}`);
          }

          const fileUrl = exportResponse.data.result.url;
          console.log(`Export URL obtained for ${language}`);

          // Download translation file
          const downloadResponse = await axios.get(fileUrl, { responseType: 'json' });
          const translations = downloadResponse.data;
          console.log(`Downloaded translations for ${language}`);

          // POEditor'den dönen veri formatını kontrol et ve gerekirse dönüştür
          let formattedTranslations = translations;

          // Eğer dizi formatında geldiyse, key-value formatına dönüştür
          if (Array.isArray(translations)) {
            console.log(`POEditor response is in array format, converting to key-value for language: ${language}`); // Added log for array format conversion
            console.log(`Converting array format to key-value format for ${language}`);
            formattedTranslations = {};
            translations.forEach(item => {
              if (item.term && item.definition) {
                formattedTranslations[item.term] = item.definition;
              }
            });
          }

          // Save file
          const outputPath = path.join(process.cwd(), 'temp', `${language}.json`);
          await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 });

          console.log(`Translations for ${language} language successfully downloaded and saved: ${outputPath}`);
        }

        console.log('All translations successfully downloaded!');
      } catch (error) {
        console.error('An error occurred while downloading translations:', error);
        process.exit(1);
      }
    }
  • Improvement rationale:
    • Technical benefits: Improves debugging and observability of the script's execution.
    • Business value: Makes it easier to troubleshoot issues related to translation format changes.
    • Risk assessment: Minimal risk, as it only adds logging for better traceability.

📁 .github/workflows/poeditor-sync.yml - sync-translations job

  • Submitted PR Code:
    jobs:
      sync-translations:
        runs-on: ubuntu-latest

        steps:
          - name: Checkout code
            uses: actions/checkout@v3
            with:
              token: ${{ secrets.GITHUB_TOKEN }}

          - name: Setup Node.js
            uses: actions/setup-node@v3
            with:
              node-version: "18"

          - name: Create package.json for scripts
            run: |
              mkdir -p .github/scripts
              cat > .github/scripts/package.json << EOF
              {
                "name": "poeditor-scripts",
                "version": "1.0.0",
                "type": "module",
                "private": true,
                "dependencies": {
                  "axios": "^1.6.0",
                  "fs-extra": "^11.1.1"
                }
              }
              EOF

          - name: Install dependencies
            run: |
              cd .github/scripts
              npm install

          - name: Download translations from POEditor
            env:
              POEDITOR_API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
              POEDITOR_PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
              LANGUAGES: ${{ github.event.inputs.languages || 'tr,gb' }}
              EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }}
            run: |
              mkdir -p temp
              node .github/scripts/download-translations.js

          - name: Verify translation files
            run: |
              echo "Verifying translation files..."
              for file in temp/*.json; do
                echo "Checking $file"
                if [ ! -s "$file" ]; then
                  echo "Error: $file is empty or does not exist"
                  exit 1
                fi
                # Validate JSON format
                cat "$file" | jq . > /dev/null || { echo "Error: $file is not valid JSON"; exit 1; }
              done
              echo "All translation files are valid"

          - name: Copy translations to project
            run: |
              mkdir -p src/locales
              cp -r temp/* src/locales/
              echo "Translation files copied to src/locales/"

          - name: Commit changes
            run: |
              git config --local user.email "github-actions[bot]@users.noreply.github.com"
              git config --local user.name "github-actions[bot]"
              git add src/locales/*.json
              git diff --staged --quiet || git commit -m "Translations updated from POEditor"
              git push
  • Analysis:
    • Current logic and potential issues: The workflow is well-structured and uses standard GitHub Actions steps. The use of environment variables and workflow inputs is appropriate. The verification step ensures the integrity of the downloaded translation files.
    • Edge cases and error handling: The workflow relies on GitHub Actions' built-in error handling. If any step fails, the workflow will fail.
    • Cross-component impact : The workflow interacts with the POEditor API, GitHub Secrets, and the Git repository.
    • Business logic considerations : The workflow aligns with the typical workflow for localization management, ensuring translations are kept up-to-date.
  • LlamaPReview Suggested Improvements:
    jobs:
      sync-translations:
        runs-on: ubuntu-latest

        steps:
          - name: Checkout code
            uses: actions/checkout@v3
            with:
              token: ${{ secrets.GITHUB_TOKEN }}

          - name: Setup Node.js
            uses: actions/setup-node@v3
            with:
              node-version: "18"

          - name: Create package.json for scripts
            run: |
              mkdir -p .github/scripts
              cat > .github/scripts/package.json << EOF
              {
                "name": "poeditor-scripts",
                "version": "1.0.0",
                "type": "module",
                "private": true,
                "dependencies": {
                  "axios": "^1.6.0",
                  "fs-extra": "^11.1.1"
                }
              }
              EOF

          - name: Install dependencies
            run: |
              cd .github/scripts
              npm install

          - name: Download translations from POEditor
            env:
              POEDITOR_API_TOKEN: ${{ secrets.POEDITOR_API_TOKEN }}
              POEDITOR_PROJECT_ID: ${{ secrets.POEDITOR_PROJECT_ID }}
              LANGUAGES: ${{ github.event.inputs.languages || 'tr,en' }} # Consistent default languages: tr,en
              EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }}
            run: |
              mkdir -p temp
              # Basic input validation for languages (example - can be expanded)
              if [[ -n "${{ github.event.inputs.languages }}" ]]; then
                if ! [[ "${{ github.event.inputs.languages }}" =~ ^[a-z]{2}(,[a-z]{2})*$ ]]; then
                  echo "Error: Invalid languages input format. Must be comma-separated 2-letter language codes (e.g., tr,en)."
                  exit 1
                fi
              fi
              node .github/scripts/download-translations.js

          - name: Verify translation files
            run: |
              echo "Verifying translation files..."
              for file in temp/*.json; do
                echo "Checking $file"
                if [ ! -s "$file" ]; then
                  echo "Error: $file is empty or does not exist"
                  exit 1
                fi
                # Validate JSON format
                cat "$file" | jq . > /dev/null || { echo "Error: $file is not valid JSON"; exit 1; }
              done
              echo "All translation files are valid"

          - name: Copy translations to project
            run: |
              mkdir -p src/locales
              cp -r temp/* src/locales/
              echo "Translation files copied to src/locales/"

          - name: Commit changes
            run: |
              git config --local user.email "github-actions[bot]@users.noreply.github.com"
              git config --local user.name "github-actions[bot]"
              git add src/locales/*.json
              git diff --staged --quiet || git commit -m "Translations updated from POEditor"
              git push
  • Improvement rationale:
    • Technical benefits: Improves consistency and reduces potential confusion for users.
    • Business value: Ensures the expected default languages are downloaded, aligning with project localization needs.
    • Risk assessment: Minimal risk, as it only standardizes default languages.

3.2 Key Quality Aspects

[Only include aspects not covered in Critical Findings]

  • System scalability considerations: The workflow should scale reasonably well with the number of languages. The for...of loop iterates through languages, and API calls are made for each language. Performance might degrade with a very large number of languages, but it should be acceptable for most projects.
  • Performance bottlenecks and optimizations: Network latency and POEditor API rate limits could be potential performance bottlenecks, although unlikely to be significant for typical translation synchronization. Resource utilization is expected to be low. Performance is unlikely to be a major concern for this workflow.
  • Testing strategy and coverage: There are no automated tests included in this PR for the workflow or the JavaScript script. The PR description outlines manual execution steps, implying manual testing is expected. Automated tests (e.g., unit tests for the script, integration tests for the workflow) would significantly improve the robustness and reliability of this feature.
  • Documentation needs: The README.md file in the workflow directory provides good documentation on how the workflow works, required settings, manual execution, output, and troubleshooting. The PR description itself is also well-written and explains the purpose and functionality of the workflow.

4. Overall Evaluation

  • Technical assessment: The PR is well-executed and introduces a valuable feature for automating translation synchronization. The code is reasonably robust, maintainable, and well-documented.
  • Business impact: This feature enhances the localization workflow, making it more efficient and less error-prone, thereby improving the overall user experience for multilingual users.
  • Risk evaluation: The primary risk is the inconsistency in default languages. Addressing this inconsistency will improve the overall quality and reliability of the workflow.
  • Notable positive aspects and good practices: The workflow is well-structured, uses standard GitHub Actions steps, and includes comprehensive documentation. The script is well-structured and implements the core logic for downloading translations.
  • Implementation quality: The implementation quality is good. The code is easy to understand and modify, and the documentation is comprehensive and helpful.
  • Final recommendation: Approve and Merge after addressing the P1 issue (Suggestion 1) regarding default language consistency. The P2 suggestions are valuable improvements but can be considered for future iterations if time is a constraint for merging. Consider adding automated tests in a follow-up PR to further enhance robustness.

💡 LlamaPReview Community
Have feedback on this AI Code review tool? Join our GitHub Discussions to share your thoughts and help shape the future of LlamaPReview.

Copy link

coderabbitai bot commented Mar 10, 2025

Walkthrough

This pull request introduces a new JavaScript script that downloads and processes translations from the POEditor API. It also adds GitHub Actions workflows to automate the translation synchronization process by running the script, verifying the downloaded files, and committing changes to the repository. Both manual triggers and scheduled runs are supported. Error handling and environment-based configurations have been implemented.

Changes

File(s) Change Summary
.github/scripts/download-translations.js New Node.js script that retrieves configuration from environment variables, sends POST/GET requests to the POEditor API to download translation files, formats the data, cleans the temporary directory, and saves the results as JSON.
.github/workflows/README.md
.github/workflows/poeditor-sync.yml
New GitHub Actions workflows added for POEditor translation synchronization. They support manual triggering and scheduled runs, handle dependency setup, execute the download script, verify translation files, copy them to src/locales, and commit any changes to the repository.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Workflow
    participant NS as download-translations.js
    participant PE as POEditor API
    participant FS as File System/Repository

    GH->>GH: Trigger (manually or scheduled)
    GH->>NS: Start translation download script
    NS->>PE: Send POST request for export URL (per language)
    PE-->>NS: Return export URL or error
    NS->>PE: Send GET request to download translation file
    PE-->>NS: Return translation data
    NS->>NS: Format data (convert array to key-value if needed)
    NS->>FS: Save JSON file to temporary directory
    NS->>NS: Clean temporary files before next run
    GH->>FS: Copy verified translation files to src/locales
    FS->>GH: Confirm file status
    GH->>FS: Stage and commit changes if modifications exist
Loading

🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

‼️ IMPORTANT
Auto-reply has been disabled for this repository in the CodeRabbit settings. The CodeRabbit bot will not respond to your replies unless it is explicitly tagged.

  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (6)
.github/workflows/README.md (1)

16-17: Consider adding a section on how to obtain these credentials.

The README mentions the required secrets but doesn't explain how to obtain a POEditor API token or find the project ID. This might be challenging for new team members.

You could add something like:

1. `POEDITOR_API_TOKEN`: Your POEditor API token
2. `POEDITOR_PROJECT_ID`: Your POEditor project ID
+
+### How to obtain these credentials
+1. To get your POEditor API token, log in to your POEditor account, go to your user dashboard, and find the API Access section.
+2. For the Project ID, navigate to your project in POEditor. The ID is visible in the URL or in the project settings.
🧰 Tools
🪛 LanguageTool

[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...Hub repository: 1. POEDITOR_API_TOKEN: Your POEditor API token 2. `POEDITOR_PR...

(UNLIKELY_OPENING_PUNCTUATION)

.github/scripts/download-translations.js (3)

45-45: Enhance error handling for the download request.

The script doesn't handle HTTP errors during the download. If the server returns a non-200 status code, the script might fail without a meaningful error message.

- const downloadResponse = await axios.get(fileUrl, { responseType: 'json' });
+ try {
+   const downloadResponse = await axios.get(fileUrl, { responseType: 'json' });
+   const translations = downloadResponse.data;
+   console.log(`Downloaded translations for ${language}`);
+ } catch (error) {
+   throw new Error(`Failed to download translations for ${language}: ${error.message}`);
+ }

49-61: Non-English comments should be translated for consistency.

There are comments in a non-English language (Turkish) which might make maintenance difficult for international contributors. Consider translating these to maintain consistency with the rest of the codebase.

- // POEditor'den dönen veri formatını kontrol et ve gerekirse dönüştür
+ // Check the data format returned from POEditor and convert if necessary
  let formattedTranslations = translations;

- // Eğer dizi formatında geldiyse, key-value formatına dönüştür
+ // If the data is in array format, convert it to key-value format
  if (Array.isArray(translations)) {

64-66: Directory creation before file writing could vomit errors.

You're writing JSON to a path without explicitly ensuring the directory structure exists, even though you created the temp dir at the main level. This could result in errors when writing to nested paths.

- const outputPath = path.join(process.cwd(), 'temp', `${language}.json`);
- await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 });
+ const outputDir = path.join(process.cwd(), 'temp');
+ const outputPath = path.join(outputDir, `${language}.json`);
+ // Ensure directory exists
+ await fs.ensureDir(outputDir);
+ await fs.writeJson(outputPath, formattedTranslations, { spaces: 2 });
.github/workflows/poeditor-sync.yml (2)

86-92: Improve git commit process with more specific error handling.

The commit step doesn't handle potential git errors, and there's no feedback if pushing fails. Mom's spaghetti all over my keyboard thinking about those silent failures.

- git config --local user.email "github-actions[bot]@users.noreply.github.com"
- git config --local user.name "github-actions[bot]"
- git add src/locales/*.json
- git diff --staged --quiet || git commit -m "Translations updated from POEditor"
- git push
+ # Set git configuration
+ git config --local user.email "github-actions[bot]@users.noreply.github.com"
+ git config --local user.name "github-actions[bot]"
+ 
+ # Add translation files
+ git add src/locales/*.json
+ 
+ # Commit and push changes if any
+ if ! git diff --staged --quiet; then
+   echo "Changes detected, committing..."
+   git commit -m "Translations updated from POEditor [skip ci]"
+   
+   echo "Pushing changes..."
+   if git push; then
+     echo "Successfully pushed changes!"
+   else
+     echo "Failed to push changes!"
+     exit 1
+   fi
+ else
+   echo "No changes to commit"
+ fi

66-79: Add additional validation for translation content.

The verification step checks if files exist and are valid JSON, but doesn't validate that they actually contain translations. Mom's spaghetti will be all over the floor if empty objects or malformed translations make it to production.

  for file in temp/*.json; do
    echo "Checking $file"
    if [ ! -s "$file" ]; then
      echo "Error: $file is empty or does not exist"
      exit 1
    fi
    # Validate JSON format
    cat "$file" | jq . > /dev/null || { echo "Error: $file is not valid JSON"; exit 1; }
+   
+   # Check if JSON is not just an empty object
+   if [ "$(cat "$file" | jq 'length')" -eq "0" ]; then
+     echo "Error: $file contains an empty object with no translations"
+     exit 1
+   fi
+   
+   # Validate at least some keys exist (adjust the minimum expected count as needed)
+   KEY_COUNT=$(cat "$file" | jq 'length')
+   if [ "$KEY_COUNT" -lt 5 ]; then
+     echo "Warning: $file contains only $KEY_COUNT translation keys. This seems unusually low."
+   fi
  done
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7c369e8 and 29e03a6.

📒 Files selected for processing (3)
  • .github/scripts/download-translations.js (1 hunks)
  • .github/workflows/README.md (1 hunks)
  • .github/workflows/poeditor-sync.yml (1 hunks)
🧰 Additional context used
🪛 LanguageTool
.github/workflows/README.md

[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...Hub repository: 1. POEDITOR_API_TOKEN: Your POEditor API token 2. `POEDITOR_PR...

(UNLIKELY_OPENING_PUNCTUATION)

🪛 actionlint (1.7.4)
.github/workflows/poeditor-sync.yml

26-26: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


31-31: the runner of "actions/setup-node@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🔇 Additional comments (3)
.github/workflows/README.md (1)

1-42: Comprehensive documentation that thoroughly explains the POEditor integration workflow.

The README provides clear instructions on how to use the workflow, including required settings, manual execution options, and troubleshooting steps. This will help users understand how to properly configure and utilize the translation synchronization.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...Hub repository: 1. POEDITOR_API_TOKEN: Your POEditor API token 2. `POEDITOR_PR...

(UNLIKELY_OPENING_PUNCTUATION)

.github/workflows/poeditor-sync.yml (2)

31-33:

✅ Verification successful

Update actions/setup-node to the latest version.

Similar to the checkout action, "actions/setup-node@v3" is flagged as too old. Knees weak, arms heavy thinking about potential compatibility issues.

- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4

🌐 Web query:

What is the latest version of GitHub's setup-node action?

💡 Result:

The latest version of GitHub's setup-node action is v4, as referenced across multiple sources[1][2][4][8][10][11]. This version provides features like Node.js version management, dependency caching, and authentication configuration for package registries. Key aspects include:

Usage in workflows:

- uses: actions/setup-node@v4
  with:
    node-version: '20.x'  # Example using Node.js 20
    cache: 'npm'          # Enable dependency caching

Key capabilities in v4:

  • Support for Node.js versions up to 20.x LTS and nightly/canary builds[1][6][8]
  • Matrix testing across multiple Node.js versions[4]:
    strategy:
      matrix:
        node-version: ['18.x', '20.x', 'latest']
  • Authentication handling for npm/GitHub Package Registry[4][10]
  • Architecture specification (x86/x64/ARM)[2][5]
  • Rate limit management with personal access tokens[1][12]

The action automatically handles Node.js installation from GitHub's actions/node-versions repository[12] and supports both .nvmrc/.node-version files and direct version specifications[2][4]. For enterprise environments, v4 requires proper token configuration to avoid API rate limits[1][12].

Citations:


Action Required: Upgrade to actions/setup-node v4

The web query confirms that the latest version is indeed v4. Please update the workflow to use v4 instead of v3 to take advantage of improved node version management, dependency caching, and compatibility features.

- uses: actions/setup-node@v3
+ uses: actions/setup-node@v4

Knees weak, arms heavy – this upgrade is the right move!

🧰 Tools
🪛 actionlint (1.7.4)

31-31: the runner of "actions/setup-node@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


26-28:

✅ Verification successful

Update actions/checkout to the latest version.

The static analysis indicates that "actions/checkout@v3" is too old. Mom's spaghetti already on the GitHub Actions runner, but v4 has been out for a while now with performance improvements.

- uses: actions/checkout@v3
+ uses: actions/checkout@v4

🌐 Web query:

What is the latest version of GitHub's checkout action?

💡 Result:

The latest version of GitHub's actions/checkout action is v4, as confirmed by multiple sources from 2024 to 2025[1][2][4][6]. This version addresses deprecated Node.js 16 dependencies and is widely adopted in workflows for its stability and compatibility with modern GitHub Actions runners.

Key details about actions/checkout@v4:

  • Cloning Repositories: Automatically clones the current repository into the runner’s $GITHUB_WORKSPACE directory[2].
  • Configuration Options: Supports parameters like ref (branch/tag/SHA), repository (for external repos), and path (for multi-repo workflows)[2].
  • Authentication: Handles GitHub authentication seamlessly, including private repositories[1].
  • Version Pinning: Recommended to pin the version using @v4 or a commit hash for stability[1][4].

Example usage:

steps:
  - uses: actions/checkout@v4
  # Additional steps...

For projects requiring Node.js 20 compatibility or running on older systems, alternatives like taiki-e/checkout-action@v1 exist but lack full feature parity[3].

Citations:


Action Required: Update Checkout Action to v4

In the file .github/workflows/poeditor-sync.yml (lines 26-28), please replace actions/checkout@v3 with actions/checkout@v4. This update leverages performance improvements and addresses deprecated dependencies, ensuring optimal compatibility with modern GitHub Actions runners.

- uses: actions/checkout@v3
+ uses: actions/checkout@v4
🧰 Tools
🪛 actionlint (1.7.4)

26-26: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


When running the workflow manually, you can specify which languages to download. Languages should be entered as comma-separated values (e.g., `tr,gb,es`).

If you don't specify any languages, the default languages `tr` and `gb` will be downloaded.
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistency between README and workflow default languages.

The README states the default languages are tr and gb, but in the workflow file, line 61 specifies tr,gb while line 10 specifies tr,en. This inconsistency could confuse users.

- If you don't specify any languages, the default languages `tr` and `gb` will be downloaded.
+ If you don't specify any languages, the default languages `tr` and `en` will be downloaded.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
If you don't specify any languages, the default languages `tr` and `gb` will be downloaded.
If you don't specify any languages, the default languages `tr` and `en` will be downloaded.

Comment on lines +1 to +5
import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Mom's spaghetti, but where's the error handler for axios?

I'm sweatin' over here noticing you're importing axios but not setting up a global error interceptor. Network requests might fail with various status codes, and knees weak, arms heavy when we can't distinguish between different types of errors.

import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';

+// Configure axios error handling
+axios.interceptors.response.use(
+  response => response,
+  error => {
+    if (error.response) {
+      // The request was made and the server responded with a status code
+      // that falls out of the range of 2xx
+      console.error(`Response error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
+    } else if (error.request) {
+      // The request was made but no response was received
+      console.error('No response received from server');
+    } else {
+      // Something happened in setting up the request that triggered an Error
+      console.error('Error setting up request:', error.message);
+    }
+    return Promise.reject(error);
+  }
+);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';
import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';
// Configure axios error handling
axios.interceptors.response.use(
response => response,
error => {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.error(`Response error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
} else if (error.request) {
// The request was made but no response was received
console.error('No response received from server');
} else {
// Something happened in setting up the request that triggered an Error
console.error('Error setting up request:', error.message);
}
return Promise.reject(error);
}
);

Comment on lines 10 to 13
const API_TOKEN = process.env.POEDITOR_API_TOKEN;
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(',');
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Environment variables should have fallbacks or validation.

The script relies on environment variables that might not be set, potentially causing runtime errors. While you have fallbacks for LANGUAGES and EXPORT_FORMAT, you're missing validation for the critical API_TOKEN and PROJECT_ID variables.

// POEditor API information
const API_TOKEN = process.env.POEDITOR_API_TOKEN;
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(',');
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json';

+// Validate required environment variables
+if (!API_TOKEN || !PROJECT_ID) {
+  console.error('Error: POEDITOR_API_TOKEN and POEDITOR_PROJECT_ID environment variables must be set');
+  process.exit(1);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const API_TOKEN = process.env.POEDITOR_API_TOKEN;
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(',');
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json';
const API_TOKEN = process.env.POEDITOR_API_TOKEN;
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(',');
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json';
// Validate required environment variables
if (!API_TOKEN || !PROJECT_ID) {
console.error('Error: POEDITOR_API_TOKEN and POEDITOR_PROJECT_ID environment variables must be set');
process.exit(1);
}

Comment on lines 60 to 61
LANGUAGES: ${{ github.event.inputs.languages || 'tr,gb' }}
EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Inconsistency in default language codes between workflow input and environment variable.

There's vomit on my sweater already seeing that the default input for languages is "tr,en" (line 10), but the environment variable fallback is "tr,gb" (line 60). This inconsistency could lead to confusion and errors.

- LANGUAGES: ${{ github.event.inputs.languages || 'tr,gb' }}
+ LANGUAGES: ${{ github.event.inputs.languages || 'tr,en' }}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
LANGUAGES: ${{ github.event.inputs.languages || 'tr,gb' }}
EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }}
LANGUAGES: ${{ github.event.inputs.languages || 'tr,en' }}
EXPORT_FORMAT: ${{ github.event.inputs.format || 'key_value_json' }}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (3)
.github/scripts/download-translations.js (3)

49-53: There's vomit on his sweater already - these comments need translating

The Turkish comments reduce code maintainability for international teams. Let's translate them to English for consistency.

-      // POEditor'den dönen veri formatını kontrol et ve gerekirse dönüştür
+      // Check the data format returned from POEditor and convert if necessary
       let formattedTranslations = translations;

-      // Eğer dizi formatında geldiyse, key-value formatına dönüştür
+      // If it comes in array format, convert it to key-value format
       if (Array.isArray(translations)) {

7-8: Unused variable makes my arms heavy

The __filename variable is defined but never used in the script. It should be removed to avoid confusion.

-// Get current directory
-const __filename = fileURLToPath(import.meta.url);
+

45-46: Pass timeout to axios or lose yourself in hanging requests

When making HTTP requests, it's good practice to set a timeout to prevent the script from hanging indefinitely if the server doesn't respond.

-      const downloadResponse = await axios.get(fileUrl, { responseType: 'json' });
+      const downloadResponse = await axios.get(fileUrl, { responseType: 'json', timeout: 10000 });
       const translations = downloadResponse.data;
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 29e03a6 and 7522879.

📒 Files selected for processing (2)
  • .github/scripts/download-translations.js (1 hunks)
  • .github/workflows/README.md (1 hunks)
🧰 Additional context used
🪛 LanguageTool
.github/workflows/README.md

[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...Hub repository: 1. POEDITOR_API_TOKEN: Your POEditor API token 2. `POEDITOR_PR...

(UNLIKELY_OPENING_PUNCTUATION)

🔇 Additional comments (3)
.github/workflows/README.md (1)

1-42: Documentation looks solid, ready to drop!

The workflow documentation is clear and comprehensive, covering all the necessary details for users to successfully implement and troubleshoot the POEditor translation synchronization process.

🧰 Tools
🪛 LanguageTool

[uncategorized] ~16-~16: Loose punctuation mark.
Context: ...Hub repository: 1. POEDITOR_API_TOKEN: Your POEditor API token 2. `POEDITOR_PR...

(UNLIKELY_OPENING_PUNCTUATION)

.github/scripts/download-translations.js (2)

10-13: Environment variables need validation or my palms get sweaty

The script relies on environment variables that might not be set, potentially causing runtime errors. While you have fallbacks for LANGUAGES and EXPORT_FORMAT, you're missing validation for the critical API_TOKEN and PROJECT_ID variables.

const API_TOKEN = process.env.POEDITOR_API_TOKEN;
const PROJECT_ID = process.env.POEDITOR_PROJECT_ID;
const LANGUAGES = (process.env.LANGUAGES || 'tr,en').split(',');
const EXPORT_FORMAT = process.env.EXPORT_FORMAT || 'key_value_json';

+// Validate required environment variables
+if (!API_TOKEN || !PROJECT_ID) {
+  console.error('Error: POEDITOR_API_TOKEN and POEDITOR_PROJECT_ID environment variables must be set');
+  process.exit(1);
+}

1-5: Mom's spaghetti, but where's the error handler for axios?

I'm sweatin' over here noticing you're importing axios but not setting up a global error interceptor. Network requests might fail with various status codes, and knees weak, arms heavy when we can't distinguish between different types of errors.

import axios from 'axios';
import fs from 'fs-extra';
import path from 'path';
import { fileURLToPath } from 'url';

+// Configure axios error handling
+axios.interceptors.response.use(
+  response => response,
+  error => {
+    if (error.response) {
+      // The request was made and the server responded with a status code
+      // that falls out of the range of 2xx
+      console.error(`Response error: ${error.response.status} - ${JSON.stringify(error.response.data)}`);
+    } else if (error.request) {
+      // The request was made but no response was received
+      console.error('No response received from server');
+    } else {
+      // Something happened in setting up the request that triggered an Error
+      console.error('Error setting up request:', error.message);
+    }
+    return Promise.reject(error);
+  }
+);

const __filename = fileURLToPath(import.meta.url);

// POEditor API information
const API_TOKEN = process.env.POEDITOR_API;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Knees weak! Environment variable name mismatch will break everything!

There's a critical mismatch between your code and the documentation. Your README mentions POEDITOR_API_TOKEN but the code is looking for POEDITOR_API. This will cause the script to fail when users follow the documentation.

-const API_TOKEN = process.env.POEDITOR_API;
+const API_TOKEN = process.env.POEDITOR_API_TOKEN;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const API_TOKEN = process.env.POEDITOR_API;
-const API_TOKEN = process.env.POEDITOR_API;
+const API_TOKEN = process.env.POEDITOR_API_TOKEN;

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
.github/workflows/poeditor-sync.yml (2)

65-78: Translation File Verification is Robust
The verification loop checks for file existence, non-emptiness, and valid JSON using jq. As a small nitpick, consider using:

jq . "$file" > /dev/null

instead of piping cat "$file" | jq . for slightly better efficiency.


79-84: Copying Files to Project Directory is Clear
The step to create the src/locales directory and copy the JSON translation files from temp/ is straightforward. Just ensure that only the intended JSON files are present in the temp/ directory to avoid accidental copies.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7522879 and c50b45f.

📒 Files selected for processing (2)
  • .github/scripts/download-translations.js (1 hunks)
  • .github/workflows/poeditor-sync.yml (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • .github/scripts/download-translations.js
🧰 Additional context used
🪛 actionlint (1.7.4)
.github/workflows/poeditor-sync.yml

26-26: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


31-31: the runner of "actions/setup-node@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

🔇 Additional comments (9)
.github/workflows/poeditor-sync.yml (9)

1-1: Workflow Naming is Spot-On!
The workflow is named "POEditor Translation Synchronization," which is clear and descriptive. Nice and clean—just what we need to stay in sync, eh?


3-15: Trigger and Input Configuration
The manual trigger via workflow_dispatch along with the scheduled run at midnight are set up perfectly. The inputs for languages (defaulting to "tr,en") and format (defaulting to "key_value_json") align seamlessly with the PR objectives. Great job ensuring consistency here!


20-23: Job Configuration Looks Solid
The job named sync-translations with the ubuntu-latest runner is standard and meets the functional goals.


35-49: Automated Package.json Creation is Neat
The step creating a package.json file in .github/scripts using a heredoc is well-executed. The dependencies (axios and fs-extra) are correctly specified, making the script setup clean and automated.


50-54: Dependency Installation is Straightforward
Changing directory into .github/scripts and running npm install is clear and effective. No issues here—everything’s installing like a charm!


55-64: Translation Download Step is on Point
The environment variables are neatly injected (using POEDITOR_API_TOKEN, POEDITOR_PROJECT_ID, LANGUAGES, and EXPORT_FORMAT), and the script execution via Node.js is spot-on for downloading translations from POEditor.


85-92: Automated Commit and Push Logic is Solid
The Git configuration and conditional commit (only proceeding if there are changes) is well implemented. This ensures that only meaningful translation updates result in a commit. Keep up the great work!


24-29:

✅ Verification successful

Checkout Action Version Update Recommended
The checkout step currently uses actions/checkout@v3, and static analysis flags this version as outdated. Consider bumping it up to a newer version (e.g., v4) to ensure optimal performance.

-        uses: actions/checkout@v3
+        uses: actions/checkout@v4

Checkout Action Version Update – Upgrade to v4
After reviewing the static analysis feedback and confirming that the current configuration in .github/workflows/poeditor-sync.yml uses actions/checkout@v3, it’s clear that updating to actions/checkout@v4 is recommended for enhanced performance and compliance with the latest standards.

  • File: .github/workflows/poeditor-sync.yml (lines 24-29)
  • Action: Update the checkout step from version v3 to v4
    -        uses: actions/checkout@v3
    +        uses: actions/checkout@v4

This change should ensure optimal performance and further align with the static analysis recommendations.

🧰 Tools
🪛 actionlint (1.7.4)

26-26: the runner of "actions/checkout@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)


30-34:

❓ Verification inconclusive

Setup-Node Action Version Update Recommended
Similarly, the setup step uses actions/setup-node@v3, which might be outdated. Upgrading to the latest stable version can help avoid potential issues.

-        uses: actions/setup-node@v3
+        uses: actions/setup-node@v4


ACTION REQUIRED: Double-check Setup-Node Version

After running a search through the workflow file, it’s clear that the file currently calls:

uses: actions/setup-node@v3

At this time, there isn’t conclusive evidence from our review that a stable v4 is available or recommended. The documentation still favours staying with v3 unless official release notes confirm a major upgrade. Please confirm from the official release notes of the Setup-Node action if v4 is indeed stable and brings essential benefits. If it is, the suggested diff can be applied as follows:

-        uses: actions/setup-node@v3
+        uses: actions/setup-node@v4

His palms are sweaty, knees weak, arms are heavy – let’s not rush into changes unless we’re sure they won’t throw our pipeline into a spaghetti mess!


🏁 Script executed:

#!/bin/bash
echo "Searching for setup-node version usage in .github/workflows/poeditor-sync.yml..."
rg 'uses: actions/setup-node@' .github/workflows/poeditor-sync.yml

Length of output: 275


Upgrade Setup-Node Action to v4 – Verify Stability

The workflow file (.github/workflows/poeditor-sync.yml) is currently using actions/setup-node@v3. If official release notes confirm that v4 is stable and provides essential improvements, then please proceed with the upgrade as shown below:

-        uses: actions/setup-node@v3
+        uses: actions/setup-node@v4

Before merging, kindly verify that the v4 update won’t break any dependencies in our CI workflow. His palms are sweaty, knees weak, arms are heavy – let’s be extra sure this upgrade doesn’t turn our pipeline into a spaghetti mess!

🧰 Tools
🪛 actionlint (1.7.4)

31-31: the runner of "actions/setup-node@v3" action is too old to run on GitHub Actions. update the action's version to fix this issue

(action)

@ajhollid ajhollid merged commit 480056a into bluewave-labs:develop Mar 10, 2025
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants