Skip to content

Commit 1bc0c84

Browse files
Add script and workflow to detect missing locales
Co-authored-by: ArtyomVancyan <44609997+ArtyomVancyan@users.noreply.github.com>
1 parent 08756c2 commit 1bc0c84

File tree

3 files changed

+358
-0
lines changed

3 files changed

+358
-0
lines changed
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
name: Check Missing Locales
2+
3+
on:
4+
schedule:
5+
# Run every Sunday at midnight UTC (similar to validation patterns workflow)
6+
- cron: "0 0 * * 0"
7+
workflow_dispatch: # Allow manual triggering
8+
9+
jobs:
10+
check-locales:
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v3
16+
17+
- name: Setup Python
18+
uses: actions/setup-python@v4
19+
with:
20+
python-version: '3.x'
21+
22+
- name: Check for missing locales
23+
id: check
24+
continue-on-error: true
25+
run: |
26+
python scripts/check-missing-locales
27+
28+
- name: Read issue body
29+
if: steps.check.outcome == 'failure'
30+
id: issue_body
31+
run: |
32+
if [ -f /tmp/missing_locales_issue.md ]; then
33+
echo "issue_body<<EOF" >> $GITHUB_OUTPUT
34+
cat /tmp/missing_locales_issue.md >> $GITHUB_OUTPUT
35+
echo "EOF" >> $GITHUB_OUTPUT
36+
fi
37+
38+
- name: Check if issue already exists
39+
if: steps.check.outcome == 'failure'
40+
id: check_issue
41+
env:
42+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
43+
run: |
44+
# Check for open issues with the label "missing-locales"
45+
ISSUE_COUNT=$(gh issue list --label "missing-locales" --state open --json number --jq 'length')
46+
echo "existing_issues=$ISSUE_COUNT" >> $GITHUB_OUTPUT
47+
48+
- name: Create GitHub Issue
49+
if: steps.check.outcome == 'failure' && steps.check_issue.outputs.existing_issues == '0'
50+
uses: actions/github-script@v7
51+
with:
52+
github-token: ${{ secrets.GITHUB_TOKEN }}
53+
script: |
54+
const issueBody = `${{ steps.issue_body.outputs.issue_body }}`;
55+
56+
await github.rest.issues.create({
57+
owner: context.repo.owner,
58+
repo: context.repo.repo,
59+
title: 'Missing locale translations detected',
60+
body: issueBody,
61+
labels: ['missing-locales', 'translation', 'enhancement']
62+
});
63+
64+
- name: Update existing issue
65+
if: steps.check.outcome == 'failure' && steps.check_issue.outputs.existing_issues != '0'
66+
env:
67+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
68+
run: |
69+
# Get the issue number
70+
ISSUE_NUMBER=$(gh issue list --label "missing-locales" --state open --json number --jq '.[0].number')
71+
72+
# Add a comment to the existing issue
73+
gh issue comment $ISSUE_NUMBER --body "🔄 **Updated Check Results**
74+
75+
The automated locale check has been run again. Please see the latest findings above or re-run the workflow for current status."
76+
77+
- name: All locales present
78+
if: steps.check.outcome == 'success'
79+
run: |
80+
echo "✅ All locales are present! No missing locales detected."
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
# Check Missing Locales Script
2+
3+
This script detects missing locale files in `react-phone-hooks` by comparing against locales available in dependent packages (antd and MUI).
4+
5+
## Purpose
6+
7+
The `react-phone-hooks` library provides locale translations for phone input components. Since the library is used by packages like `antd-phone-input` and `mui-phone-input`, it should ideally support all locales that these packages support.
8+
9+
This script helps maintain locale coverage by:
10+
1. Checking all locale keys used by antd (via `development/src/ant-phone/locale.ts`)
11+
2. Checking all locale keys supported by MUI Material
12+
3. Comparing them against existing locales in `react-phone-hooks` (`src/locale.ts`)
13+
4. Identifying any missing locales
14+
15+
## Usage
16+
17+
### Manual Execution
18+
19+
Run the script manually from the project root:
20+
21+
```bash
22+
python scripts/check-missing-locales
23+
```
24+
25+
### Automated Execution
26+
27+
The script runs automatically via GitHub Actions:
28+
- **Schedule**: Every Sunday at midnight UTC
29+
- **Workflow**: `.github/workflows/check-missing-locales.yml`
30+
31+
When missing locales are detected, the workflow will:
32+
1. Create a GitHub issue with the list of missing locales
33+
2. Tag it with labels: `missing-locales`, `translation`, `enhancement`
34+
3. Update existing open issues if they already exist
35+
36+
## Output
37+
38+
The script outputs:
39+
- Console log showing the check results
40+
- Exit code 1 if missing locales are found
41+
- Exit code 0 if all locales are present
42+
- Issue body saved to `/tmp/missing_locales_issue.md` for GitHub Actions
43+
44+
## How It Works
45+
46+
1. **Extract existing locales**: Parses `src/locale.ts` to find all exported locale constants
47+
2. **Get antd locales**: Reads locale imports from `development/src/ant-phone/locale.ts`
48+
3. **Get MUI locales**: Uses a predefined list of MUI Material supported locales
49+
4. **Compare**: Identifies locales present in antd/MUI but missing from react-phone-hooks
50+
5. **Report**: Generates a formatted issue body with missing locale details
51+
52+
## Adding Missing Locales
53+
54+
When locales are identified as missing, they should be added to `src/locale.ts` following this structure:
55+
56+
```typescript
57+
export const enUS = {
58+
searchNotFound: "Country not found",
59+
searchPlaceholder: "Search country",
60+
countries: {
61+
"Gibraltar": "Gibraltar",
62+
"Gambia": "Gambia",
63+
// ... all other countries
64+
},
65+
}
66+
```
67+
68+
Each locale requires:
69+
- `searchNotFound`: Translation for "Country not found"
70+
- `searchPlaceholder`: Translation for "Search country"
71+
- `countries`: Object mapping English country names to their translations in the target language
Lines changed: 207 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,207 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Script to detect missing locale files in react-phone-hooks by comparing
4+
against locales available in antd-phone-input and mui-phone-input dependencies.
5+
"""
6+
7+
import json
8+
import os
9+
import re
10+
import subprocess
11+
import sys
12+
from pathlib import Path
13+
from typing import Set, List, Dict
14+
from urllib.request import urlopen
15+
16+
17+
def get_existing_locales() -> Set[str]:
18+
"""
19+
Extract locale keys from the existing locale.ts file in react-phone-hooks.
20+
"""
21+
project_root = Path(__file__).parent.parent.parent
22+
locale_file = project_root / "src" / "locale.ts"
23+
24+
if not locale_file.exists():
25+
print(f"Error: locale.ts not found at {locale_file}")
26+
sys.exit(1)
27+
28+
with open(locale_file, 'r', encoding='utf-8') as f:
29+
content = f.read()
30+
31+
# Extract all exported locale constants (e.g., "export const enUS = {")
32+
locale_pattern = r'^export const (\w+) = \{'
33+
locales = set(re.findall(locale_pattern, content, re.MULTILINE))
34+
35+
return locales
36+
37+
38+
def get_antd_locales() -> Set[str]:
39+
"""
40+
Fetch available locale keys from antd package by checking the locale imports
41+
in the development ant-phone locale.ts file.
42+
"""
43+
project_root = Path(__file__).parent.parent.parent
44+
ant_locale_file = project_root / "development" / "src" / "ant-phone" / "locale.ts"
45+
46+
if not ant_locale_file.exists():
47+
print(f"Warning: ant-phone locale.ts not found at {ant_locale_file}")
48+
return set()
49+
50+
with open(ant_locale_file, 'r', encoding='utf-8') as f:
51+
content = f.read()
52+
53+
# Extract antd locale imports (e.g., "import enUS from "antd/es/locale/en_US";")
54+
import_pattern = r'^import (\w+) from "antd/es/locale/'
55+
locales = set(re.findall(import_pattern, content, re.MULTILINE))
56+
57+
return locales
58+
59+
60+
def get_mui_locales_from_package() -> Set[str]:
61+
"""
62+
Fetch available locale keys from @mui/material package by checking
63+
the mui-phone locale.ts structure and inferring from MUI's locale module.
64+
65+
Since MUI locales are accessed dynamically via "@mui/material/locale",
66+
we need to check what locales are available in the package.
67+
"""
68+
# MUI Material has built-in locales that can be imported
69+
# Common MUI locales based on their documentation
70+
mui_locales = {
71+
'arEG', 'arSA', 'arSD', 'azAZ', 'bgBG', 'bnBD', 'caES', 'csCZ',
72+
'daDK', 'deDE', 'elGR', 'enUS', 'esES', 'etEE', 'faIR', 'fiFI',
73+
'frFR', 'heIL', 'hiIN', 'hrHR', 'huHU', 'hyAM', 'idID', 'isIS',
74+
'itIT', 'jaJP', 'khKH', 'kkKZ', 'koKR', 'ltLT', 'lvLV', 'mkMK',
75+
'mnMN', 'nbNO', 'nlNL', 'plPL', 'ptBR', 'ptPT', 'roRO', 'ruRU',
76+
'siLK', 'skSK', 'srRS', 'svSE', 'thTH', 'trTR', 'ukUA', 'urPK',
77+
'viVN', 'zhCN', 'zhHK', 'zhTW'
78+
}
79+
80+
return mui_locales
81+
82+
83+
def format_missing_locales_issue(missing_locales: Dict[str, Set[str]]) -> str:
84+
"""
85+
Format the missing locales into a GitHub issue body.
86+
"""
87+
if not any(missing_locales.values()):
88+
return ""
89+
90+
issue_body = """## Missing Locale Translations
91+
92+
This automated check has detected missing locale files in `react-phone-hooks` that are available in dependent packages.
93+
94+
### Summary
95+
96+
"""
97+
98+
total_missing = sum(len(locales) for locales in missing_locales.values())
99+
issue_body += f"**Total missing locales: {total_missing}**\n\n"
100+
101+
for source, locales in missing_locales.items():
102+
if locales:
103+
issue_body += f"### Missing from {source}\n\n"
104+
for locale in sorted(locales):
105+
issue_body += f"- `{locale}`\n"
106+
issue_body += "\n"
107+
108+
issue_body += """### Action Required
109+
110+
Please add translation entries for the missing locales listed above. Each locale should include:
111+
- `searchNotFound`: Translation for "Country not found"
112+
- `searchPlaceholder`: Translation for "Search country"
113+
- `countries`: Object mapping English country names to translations
114+
115+
### Example Structure
116+
117+
```typescript
118+
export const enUS = {
119+
searchNotFound: "Country not found",
120+
searchPlaceholder: "Search country",
121+
countries: {
122+
"United States": "United States",
123+
"United Kingdom": "United Kingdom",
124+
// ... other countries
125+
},
126+
}
127+
```
128+
129+
---
130+
*This issue was automatically generated by the locale detection script.*
131+
"""
132+
133+
return issue_body
134+
135+
136+
def main():
137+
"""
138+
Main function to detect missing locales and output results.
139+
"""
140+
print("Checking for missing locales in react-phone-hooks...")
141+
142+
# Get existing locales
143+
existing_locales = get_existing_locales()
144+
print(f"Found {len(existing_locales)} existing locales in react-phone-hooks")
145+
146+
# Get locales from dependent packages
147+
antd_locales = get_antd_locales()
148+
print(f"Found {len(antd_locales)} locales in antd")
149+
150+
mui_locales = get_mui_locales_from_package()
151+
print(f"Found {len(mui_locales)} locales in MUI")
152+
153+
# Find missing locales
154+
missing_from_antd = antd_locales - existing_locales
155+
missing_from_mui = mui_locales - existing_locales
156+
157+
# Combine and deduplicate
158+
all_missing = missing_from_antd | missing_from_mui
159+
160+
missing_locales = {}
161+
if missing_from_antd:
162+
missing_locales['antd'] = missing_from_antd
163+
if missing_from_mui:
164+
missing_locales['mui'] = missing_from_mui
165+
166+
# Output results
167+
if all_missing:
168+
print(f"\n❌ Found {len(all_missing)} missing locale(s):")
169+
for locale in sorted(all_missing):
170+
sources = []
171+
if locale in missing_from_antd:
172+
sources.append("antd")
173+
if locale in missing_from_mui:
174+
sources.append("MUI")
175+
print(f" - {locale} (from: {', '.join(sources)})")
176+
177+
# Generate issue body
178+
issue_body = format_missing_locales_issue(missing_locales)
179+
180+
# Save to file for GitHub Actions
181+
output_file = Path("/tmp/missing_locales_issue.md")
182+
with open(output_file, 'w', encoding='utf-8') as f:
183+
f.write(issue_body)
184+
185+
print(f"\n📝 Issue body saved to {output_file}")
186+
187+
# Set output for GitHub Actions
188+
if os.getenv('GITHUB_OUTPUT'):
189+
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
190+
f.write(f"has_missing=true\n")
191+
f.write(f"count={len(all_missing)}\n")
192+
193+
sys.exit(1) # Exit with error to indicate missing locales
194+
else:
195+
print("\n✅ No missing locales found!")
196+
197+
# Set output for GitHub Actions
198+
if os.getenv('GITHUB_OUTPUT'):
199+
with open(os.getenv('GITHUB_OUTPUT'), 'a') as f:
200+
f.write(f"has_missing=false\n")
201+
f.write(f"count=0\n")
202+
203+
sys.exit(0)
204+
205+
206+
if __name__ == "__main__":
207+
main()

0 commit comments

Comments
 (0)