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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 61 additions & 2 deletions .github/workflows/CI.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,26 @@ on:
pull_request:
branches:
- master
workflow_dispatch:

concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
flutter:
name: Flutter -- verify, test
runs-on: macOS-latest
# Allow push so Generate locales can commit and push ARB changes to the PR branch.
permissions:
contents: write
steps:
- name: Checkout main repo
id: checkout-main-repo
uses: actions/checkout@v3
with:
path: main-repo
fetch-depth: 0
- name: Checkout the target 'master' branch of 'flutter-app-secrets'
id: checkout-secrets
uses: actions/checkout@v3
Expand Down Expand Up @@ -69,12 +78,62 @@ jobs:
run: |
cd main-repo
melos run 4mat
- name: Set l10n baseline ref (last successful run or origin/base)
id: l10n-baseline
if: github.event_name == 'pull_request'
run: |
# For PRs: use last successful CI run's commit as baseline when available; else origin/base_ref.
BRANCH="${{ github.head_ref }}"
RUN_ID="${{ github.run_id }}"
BASE_DEFAULT="origin/${{ github.base_ref }}"
if [ -z "$BRANCH" ]; then
echo "base_ref=$BASE_DEFAULT" >> $GITHUB_OUTPUT
exit 0
fi
LAST_SHA=$(curl -s -H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
"https://api.github.com/repos/${{ github.repository }}/actions/workflows/CI.yaml/runs?branch=${BRANCH}&status=success&per_page=10" \
| jq -r --arg run_id "$RUN_ID" '.workflow_runs[] | select(.id != ($run_id | tonumber)) | .head_sha' | head -1)
if [ -n "$LAST_SHA" ] && [ "$LAST_SHA" != "null" ]; then
echo "base_ref=$LAST_SHA" >> $GITHUB_OUTPUT
else
echo "base_ref=$BASE_DEFAULT" >> $GITHUB_OUTPUT
fi
- name: Checkout PR branch (for locale commit/push)
if: github.event_name == 'pull_request'
run: |
cd main-repo
git fetch origin ${{ github.head_ref }}
git checkout ${{ github.head_ref }}
- name: Set git committer from PR HEAD
if: github.event_name == 'pull_request'
run: |
cd main-repo
git config --local user.name "$(git log -1 --format='%cn')"
git config --local user.email "$(git log -1 --format='%ce')"
- name: Generate locales
id: generate-locales
env:
BASE_REF: ${{ steps.l10n-baseline.outputs.base_ref }}
run: |
cd main-repo
if [ "${{ github.event_name }}" = "pull_request" ]; then
bash ./scripts/auto_translate_locales.sh --commit
else
bash ./scripts/auto_translate_locales.sh
fi
- name: Re-trigger CI for new commit (so status is reported on pushed commit)
if: github.event_name == 'pull_request'
run: |
cd main-repo
# Run locale generation with verification
./scripts/generate_locales.sh
HEAD_SHA=$(git rev-parse HEAD)
if [ "$HEAD_SHA" != "${{ github.sha }}" ]; then
echo "Locales were pushed; triggering CI for new commit so status is reported on HEAD."
curl -s -X POST \
-H "Authorization: token ${{ secrets.GITHUB_TOKEN }}" \
-H "Accept: application/vnd.github.v3+json" \
"https://api.github.com/repos/${{ github.repository }}/actions/workflows/CI.yaml/dispatches" \
-d "{\"ref\":\"refs/heads/${{ github.head_ref }}\"}"
fi
- name: Generate code
id: generate-code
run: |
Expand Down
68 changes: 68 additions & 0 deletions docs/auto-translate-missing-strings-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Auto-translate l10n strings (missing + changed English)

This repo auto-fills ARB translations using a Dart script + a small shell wrapper.

## Files

- **Source locale**: `lib/l10n/app_en.arb`
- **Target locales**: `lib/l10n/app_<locale>.arb` (all non-`en` locales present)
- **Missing-keys input**: `untranslated_messages.txt` (generated by `flutter gen-l10n`, JSON map `{"it":["key1",...], ...}`)
- **Translator**: `tools/translate_missing.dart`
- **Wrapper** (shared by CI + pre-push): `scripts/auto_translate_locales.sh`
- **Verifier**: `scripts/generate_locales.sh`

## What gets translated

`tools/translate_missing.dart` translates the union of:

- **Missing keys** per locale (from `untranslated_messages.txt`)
- **Keys whose English changed** in `app_en.arb` vs a base Git ref (`BASE_REF` / `--base-ref`)

### Overwrite rules

- **Modified English keys**: re-translated for all locales (overwrites existing values).
- **Newly added English keys**: translated **only if** that locale does not already contain the key (so manually-added translations in the same PR are preserved).

## Common flow (CI + pre-push)

The shared wrapper is `scripts/auto_translate_locales.sh`:

1. Run `flutter gen-l10n` (creates/refreshes `untranslated_messages.txt`)
2. Decide if translation is needed:
- `untranslated_messages.txt` is non-empty, **or**
- `lib/l10n/app_en.arb` differs vs `BASE_REF` (or auto base ref)
3. Run `dart run tools/translate_missing.dart` (optionally `--commit`)
4. Re-run `flutter gen-l10n`
5. Run `./scripts/generate_locales.sh` to verify completeness

## Usage

### Local (pre-push)

`scripts/pre_push.sh` calls:

- `./scripts/auto_translate_locales.sh --fail-if-generated --base-ref=auto`

Meaning:

- If translations were generated, the push is blocked so you can **review + commit** the ARB changes.

### CI (PRs)

CI runs (with `BASE_REF` set to last successful run’s commit or origin/base):

- `./scripts/auto_translate_locales.sh --commit`

Meaning:

- Missing and/or changed-English translations are generated, committed, and pushed **only when allowed by `tools/translate_missing.dart` branch guards**.

When the job pushes a commit, GitHub does not start a new run for that commit, so the PR can show “Expected — Waiting for status” on the latest commit. After Generate locales, the workflow **re-triggers itself** via `workflow_dispatch` on the same branch when it pushed. That second run (verify-only, no `--commit`) reports status to the new commit so the required “Flutter -- verify, test” check passes.

## Configuration

- **OpenAI key**: `OPENAI_API_KEY` (required for actual translation; `--dry-run` does not require it)
- **Base ref for “changed English” detection**:
- env: `BASE_REF` (preferred in CI), or
- wrapper: `--base-ref=<ref>` / `--base-ref=auto`

33 changes: 11 additions & 22 deletions lib/l10n/app_ar.arb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"@@locale": "ar",
"add_new_key_for_auto_translate": "نص جديد، يجب ترجمته تلقائيًا",
"add_new_key_for_auto_translate2": "نص جديد، يجب ترجمته تلقائيًا 2",
"all_networks_item": "كل الشبكات",
"app_language_description": "سيتم عرض التطبيق باللغة المختارة",
"app_language_title": "لغة التطبيق",
"article_menu_report_article": "الإبلاغ عن المقالة",
"app_language_title": "تحديث لغة التطبيق 1",
"article_menu_report_article": "تقرير تحديث المقال 2",
"article_page_from_author": "من {name}",
"article_page_in_topic": "في {name}",
"article_preview_title": "معاينة المقالة",
"auth_identity_io": "Identity.io",
"article_preview_title": "تحديث دفع معاينة المقالة 3",
"auth_identity_io": "تحديث Identity.io 4 دفع متزامن",
"auth_link_new_device_description": "لمتابعة استخدام التطبيق، يرجى ربط هذا الجهاز بحسابك",
"auth_link_new_device_link_button": "ربط الجهاز",
"auth_link_new_device_title": "تسجيل الدخول من جهاز جديد",
Expand Down Expand Up @@ -58,10 +60,10 @@
"bsc_required_dialog_action_button": "ابدأ",
"bsc_required_dialog_description": "حول محتواك إلى قيمة حقيقية من خلال المجتمعات المميزة بالرموز المميزة وإيرادات الإعلانات والمقايضات. متاح لجميع المستخدمين.",
"bsc_required_dialog_title": "تحقيق الربح من المحتوى متاح الآن",
"button_add": "إضافة",
"button_apply": "تطبيق",
"button_approve": "موافقة",
"button_approve_all": "موافقة على الكل",
"button_add": "أضف تغيير 5",
"button_apply": "تطبيق التغيير 6",
"button_approve": "وافق على تغيير 7 دفع",
"button_approve_all": "وافق على جميع التغييرات 8 متزامنة",
"button_back": "رجوع",
"button_back_to_security": "العودة إلى الأمان",
"button_block": "حظر",
Expand Down Expand Up @@ -204,7 +206,6 @@
"chat_money_sent_title": "أرسلت",
"chat_new_message_button": "رسالة جديدة",
"chat_privacy_cant_send_message": "لا يمكنك إرسال رسالة لهذا المستخدم بسبب إعدادات الخصوصية الخاصة به.",
"chat_profile_share_button": "اكتب رسالة",
"chat_profile_share_modal_title": "شارك الملف الشخصي",
"chat_profile_view_button": "عرض الملف الشخصي",
"chat_read": "مقروء",
Expand Down Expand Up @@ -292,10 +293,6 @@
"contact_wallet_is_private_title": "محفظة خاصة",
"contact_wallet_not_found_description": "لا يملك محفظة في شبكة {network}",
"contact_wallet_not_found_title": "لم يتم العثور على المحفظة",
"contacts_allow_pop_up_action": "السماح بالوصول إلى جهات الاتصال",
"contacts_allow_pop_up_desc": "زامن جهات الاتصال الخاصة بك، شاهد من هم موجودون بالفعل على Ice، وأرسل واستقبل مدفوعات Ice من أي من جهات اتصالك.",
"contacts_allow_pop_up_title": "Ice أفضل مع الأصدقاء",
"contacts_select_title": "اختر جهة اتصال",
"content_language_description": "سيتم عرض المحتوى باللغة المختارة",
"content_language_title": "لغات المحتوى",
"core_all": "الكل",
Expand All @@ -318,7 +315,6 @@
"create_post_external_content": "إنشاء منشور",
"create_post_modal_placeholder": "ما الذي يحدث؟",
"create_post_modal_title": "منشور جديد",
"create_video_edit_cover": "تعديل الغلاف",
"create_video_input_placeholder": "أضف وصفاً (اختياري)",
"create_video_new_video": "فيديو جديد",
"creator_token_is_live": "توكن المنشئ نشط",
Expand Down Expand Up @@ -581,8 +577,6 @@
"identity_key_name_description": "اعتبر اسم مفتاح هويتك كمعرف فريد لحسابك. ستحتاجه لتسجيل الدخول واستعادة حسابك، لذا احفظه جيداً ولا تنساه",
"settings_identity_key_name_description": "اسم مفتاح الهوية هو معرف فريد لحسابك ومطلوب لتسجيل الدخول واستعادة الحساب",
"identity_key_name_usage": "استخدمه لتسجيل الدخول على أي تطبيق مؤمن بواسطة",
"identity_not_found_desc": "لم نجد هذا المفتاح في قاعدة بياناتنا. تحقق من صحة البيانات المدخلة",
"identity_not_found_title": "اسم مفتاح الهوية غير موجود",
"invite_friends_button": "ادعُ صديقًا",
"invite_friends_earnings_title": "الأرباح",
"invite_friends_page_subtitle": "لكل شخص تدعوه، ستحصل على نسبة من الإيرادات التي يحققها",
Expand Down Expand Up @@ -610,8 +604,6 @@
"nickname_not_owned_suffix": "(! غير مملوك)",
"no_connection_page_description": "يرجى التحقق من اتصالك بالإنترنت والمحاولة مرة أخرى",
"no_connection_page_title": "لا يوجد اتصال",
"no_local_passkey_creds_desc": "تحقق من ملكية هذا الحساب بمسح رمز الاستجابة السريعة في الخطوة التالية باستخدام الجهاز الذي تم حفظ مفتاحك عليه.",
"no_local_passkey_creds_title": "يتطلب التحقق من مفتاح الهوية",
"notification_article_loading": "تحميل المقالة...",
"notification_article_published": "نشر المقالة",
"notification_delete_loading": "حذف...",
Expand Down Expand Up @@ -747,8 +739,6 @@
"profile_articles_empty_list": "{username} ليس لديه أي مقالات",
"profile_articles_empty_list_current_user": "ليس لديك أي مقالات",
"profile_bio": "نبذة",
"profile_bookmarks": "الإشارات المرجعية",
"profile_bookmarks_desc": "منشوراتك المحفوظة",
"profile_create_new_account": "إضافة حساب",
"profile_log_in_existing_account": "تسجيل الدخول إلى حساب موجود",
"profile_create_a_new_account": "إنشاء حساب جديد",
Expand Down Expand Up @@ -911,7 +901,6 @@
"sign_up_passkey_advantage_2_title": "يعمل على جميع أجهزتك",
"sign_up_passkey_advantage_3_description": "توفر مفاتيح المرور حماية متقدمة ضد التصيد الاحتيالي",
"sign_up_passkey_advantage_3_title": "حماية أقوى للحساب",
"sign_up_passkey_identity_key_name_taken": "اسم مفتاح الهوية محجوز",
"sign_up_passkey_title": "التسجيل باستخدام مفتاح المرور",
"sign_up_password_description": "اختر كلمة مرور قوية لإنشاء حساب",
"sign_up_password_title": "تسجيل",
Expand Down Expand Up @@ -1210,8 +1199,8 @@
"who_can_reply_settings_title_post": "من يمكنه الرد على هذا المنشور",
"who_can_reply_settings_title_video": "من يمكنه الرد على هذا الفيديو",
"who_can_reply_settings_verified_accounts": "الحسابات المُوثقة",
"write_a_message": "اكتب رسالة...",
"token_unable_to_fetch_market_data_title": "تعذّر جلب بيانات السوق",
"token_unable_to_fetch_market_data_description": "نقوم بجلب معلومات السوق لهذا الرمز، يرجى المحاولة مرة أخرى لاحقاً",
"write_a_message": "اكتب رسالة...",
"your_position_card_title": "أيتمكش"
}
20 changes: 11 additions & 9 deletions lib/l10n/app_bg.arb
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"@@locale": "bg",
"add_new_key_for_auto_translate": "Нов текст, който трябва да бъде автоматично преведен",
"add_new_key_for_auto_translate2": "Нов текст, който трябва да бъде автоматично преведен 2",
"all_networks_item": "Всички мрежи",
"app_language_description": "Приложението ще се показва на избрания език",
"app_language_title": "Език на приложението",
"article_menu_report_article": "Докладване на статия",
"app_language_title": "Актуализация на езика на приложението 1",
"article_menu_report_article": "Докладвай актуализация на статия 2",
"article_page_from_author": "от {name}",
"article_page_in_topic": "в {name}",
"article_preview_title": "Преглед на статията",
"auth_identity_io": "Identity.io",
"article_preview_title": "Актуализация на предварителния преглед на статия 3 пусни",
"auth_identity_io": "Identity.io актуализация 4 едновременни известия",
"auth_link_new_device_description": "За да продължите да използвате приложението, моля свържете това устройство с вашия акаунт",
"auth_link_new_device_link_button": "Свързване на устройство",
"auth_link_new_device_title": "Вход от ново устройство",
Expand Down Expand Up @@ -58,10 +60,10 @@
"bsc_required_dialog_action_button": "Започнете",
"bsc_required_dialog_description": "Превърнете съдържанието си в реална стойност чрез токенизирани общности, приходи от реклами и суапове. Отворено за всички потребители.",
"bsc_required_dialog_title": "Монетизацията на създателите е НА ЖИВО",
"button_add": "Добави",
"button_apply": "Приложи",
"button_approve": "Одобри",
"button_approve_all": "Одобри всички",
"button_add": "Добави промяна 5",
"button_apply": "Приложи промяна 6",
"button_approve": "Одобрете промяна 7 push",
"button_approve_all": "Одобрете всички промени 8 едновременни",
"button_back": "Назад",
"button_back_to_security": "Обратно към сигурността",
"button_block": "Блокирай",
Expand Down Expand Up @@ -1197,8 +1199,8 @@
"who_can_reply_settings_title_post": "Кой може да отговаря на тази публикация",
"who_can_reply_settings_title_video": "Кой може да отговаря на това видео",
"who_can_reply_settings_verified_accounts": "Верифицирани акаунти",
"write_a_message": "Напишете съобщение...",
"token_unable_to_fetch_market_data_title": "Не могат да се извлекат пазарни данни",
"token_unable_to_fetch_market_data_description": "Извличаме пазарна информация за този токен, моля опитайте отново по-късно",
"write_a_message": "Напишете съобщение...",
"your_position_card_title": "Вашата позиция"
}
Loading
Loading