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
133 changes: 133 additions & 0 deletions bin/syncTranslations.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
#!/usr/bin/env node

/**
* Sync translation files from the code-dot-org monorepo.
*
* Usage:
* node bin/syncTranslations.js # from GitHub API (default branch)
* node bin/syncTranslations.js --local ~/code/code-dot-org # from local checkout
*
* Downloads all non-English locale JSON files from apps/i18n/fish/ and saves
* them to i18n/locales/ in this repo.
*/

const fs = require('fs');
const path = require('path');
const https = require('https');

const LOCALES_DIR = path.resolve(__dirname, '..', 'i18n', 'locales');
const GITHUB_API =
'https://api.github.com/repos/code-dot-org/code-dot-org/contents/apps/i18n/fish';

function parseArgs() {
const args = process.argv.slice(2);
const localIdx = args.indexOf('--local');
if (localIdx !== -1) {
const localPath = args[localIdx + 1];
if (!localPath) {
console.error('Error: --local requires a path argument');
process.exit(1);
}
return {mode: 'local', repoPath: localPath};
}
return {mode: 'github'};
}

function ensureDir(dir) {
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir, {recursive: true});
}
}

function syncFromLocal(repoPath) {
const fishDir = path.join(repoPath, 'apps', 'i18n', 'fish');
if (!fs.existsSync(fishDir)) {
console.error(`Error: ${fishDir} does not exist`);
process.exit(1);
}

ensureDir(LOCALES_DIR);

const files = fs.readdirSync(fishDir).filter(
f => f.endsWith('.json') && f !== 'en_us.json'
);

for (const file of files) {
fs.copyFileSync(path.join(fishDir, file), path.join(LOCALES_DIR, file));
}

console.log(`Synced ${files.length} locale files from ${fishDir}`);
}

function fetchJSON(url) {
return new Promise((resolve, reject) => {
https.get(url, {headers: {'User-Agent': 'ml-activities-sync'}}, res => {
let data = '';
res.on('data', chunk => (data += chunk));
res.on('end', () => {
if (res.statusCode !== 200) {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
return;
}
resolve(JSON.parse(data));
});
res.on('error', reject);
});
});
}

function fetchRaw(url) {
return new Promise((resolve, reject) => {
https.get(url, {headers: {'User-Agent': 'ml-activities-sync'}}, res => {
let data = '';
res.on('data', chunk => (data += chunk));
res.on('end', () => {
if (res.statusCode !== 200) {
reject(new Error(`HTTP ${res.statusCode}: ${data}`));
return;
}
resolve(data);
});
res.on('error', reject);
});
});
}

async function syncFromGitHub() {
ensureDir(LOCALES_DIR);

console.log('Fetching file list from GitHub...');
const contents = await fetchJSON(GITHUB_API);

const localeFiles = contents.filter(
f => f.name.endsWith('.json') && f.name !== 'en_us.json'
);

console.log(`Found ${localeFiles.length} locale files. Downloading...`);

let downloaded = 0;
for (const file of localeFiles) {
const raw = await fetchRaw(file.download_url);
fs.writeFileSync(path.join(LOCALES_DIR, file.name), raw);
downloaded++;
if (downloaded % 10 === 0) {
console.log(` ${downloaded}/${localeFiles.length}...`);
}
}

console.log(`Synced ${downloaded} locale files from GitHub`);
}

async function main() {
const opts = parseArgs();
if (opts.mode === 'local') {
syncFromLocal(opts.repoPath);
} else {
await syncFromGitHub();
}
}

main().catch(err => {
console.error('Error:', err.message);
process.exit(1);
});
106 changes: 106 additions & 0 deletions i18n/locales/ar_sa.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
{
"angry": "غاضب",
"areYouSure": "هل أنت متأكد؟",
"awesome": "رائع",
"blue": "أرزق",
"bodies": "جسم",
"cancel": "إلغاء",
"circular": "دائري",
"clickIndividualFish": "انقر على السمكة المفردة لرؤية معلوماتها.",
"colors": "ألوان",
"continue": "الاستمرار",
"creaturesvtrash-pond-init1": "اعتماداً على التدريب الذي قدمته، إليك بعض الأشياء التي تعرف عليها الروبوت A.I كـ\"كائنات تعيش في المياه\".كيف فعل الروبوت A.I. هذا؟",
"creaturesvtrash-predicting-init1": "هل تعتقد بأن الروبوت A.I. سيؤدي الآن عملاً أفضل في التعرف على الأشياء التي يجب أن تكون موجودة في المياه؟ لِنشاهد.",
"creaturesvtrash-training-init1": "لِنعلّم الروبوت A.I ما الأشياء التي يجب أن توجد في الماء.",
"creaturesvtrash-training-init2": "في المحيط، قد تؤدي النفايات البلاستيكية إلى تضرر الأسماك والطيور البحرية والثدييات البحرية. تعد حماية الطيور البحرية والحياة البحرية واحدة من أسباب عديدة تدفع للحفاظ على المحيط نظيفاً.",
"creaturesvtrash-training-init3": "كلما قدمت المزيد من بيانات التدريب، كلما زاد تعلم الروبوت A.I. واصل التدريب.",
"creaturesvtrash-training-init4": "يؤثر تلوث المياه بالمواد البلاستيكية على ما لا يقل عن 267 فصيلة بحرية في أنحاء العالم بما في ذلك 86% من كل فصائل السلاحف البحرية.",
"creaturesvtrash-training-init5": "استمر بالتدريب لتعليم الروبوت A.I. الأشياء التي توجد في الماء.",
"creaturesvtrash-training-init6": "عمل رائع! كلما قدمت المزيد من المعلومات إلى الروبوت A.I، كلما أصبح عمله أفضل. حافظ على تدريب الروبوت A.I.، أو استمر عندما تكون مستعداً.",
"creaturesvtrashdemo-predicting-init1": "حتى الآن، نحن دربنا الروبوت A.I لتحديد الأشياء على أنها \"أسماك\"، أو \"ليست أسماك\".",
"creaturesvtrashdemo-predicting-init2": "ماذا لو أن بيانات التدريب هذه استخدمت لتحديد الشي الموجود في الماء؟",
"creaturesvtrashdemo-predicting-init3": "ما الذي سيحدث للمخلوقات البحرية الأخرى؟ هل نهج التدريب الذي نقوم به يتسبب بحدوث عواقب ونتائج غير مقصودة؟",
"creaturesvtrashdemo-predicting-init4": "لِنرى.",
"creaturesvtrashdemo-predicting-pause1": "هل تعرّف الروبوت A.I على بعض المخلوقات البحرية باعتبارها \"ليست سمكة\"؟",
"creaturesvtrashdemo-predicting-pause2": "على الرغم من أن هذه المخلوقات البحرية ليست أسماكاً، لكنها تعيش في الماء.",
"creaturesvtrashdemo-predicting-pause3": "تعلم الروبوت A.I ما علّمناه اياه.",
"creaturesvtrashdemo-predicting-pause4": "لِندرّب الروبوت A.I مرة أخرى.",
"delicious": "مبهج",
"didYouKnow": "هل تعلم؟",
"doesThisBelongInWater": "هل هذا الشيء يعيش في الماء؟",
"dorsalFins": "زعانف ظهرية",
"endangered": "معرض للخطر",
"erase": "مسح",
"eraseWarning": "سيؤدي مسح بيانات A.I. إلى حذف كل التدريب بشكل نهائي. هل هذا ما تريد فعله؟",
"eyes": "عيون",
"fast": "سريع",
"fierce": "عنيف",
"finish": "إنهاء",
"fish": "أسماك",
"fishlong-pond-init1": "اعتماداً على التدريب الذي قدمته إلى A.I.، فقد قام الروبوت A.I. بتعريف {n, plural,one {هذه} other {تلك}} السمكة باعتبارها “{word}”. كيف فعل الروبوت A.I. ذلك؟",
"fishlong-pond-init2": "يمكنك تقديم المزيد من التدريب إذا كنت ترغب في تحسين النتائج.",
"fishlong-pond-init3": "أو، يمكنك تعليم A.I. كلمة جديدة باختيار (كلمة جديدة).",
"fishlong-predicting-init1": "باستخدام بيانات التدريب الخاصة بك، أنت قمت ببرمجة الروبوت A.I. لكي يتعرف على {word} سمكة.",
"fishlong-predicting-init2": "دعونا نرى الروبوت A.I. وهو يقدم تعريفا لـ“{word}” سمكة.",
"fishlong-training-many": "عجباً. يوجد الكثير من الأسماك!",
"fishlong-training-pause1": "هل من الانصاف استخدام الذكاء الاصطناعي للحكم على أن الشيء يمثل سمكة بحسب الشكل؟ في حين أن الذكاء الاصطناعي قد يبدو منصفاً ومحايداً، إلا أن تحليلاته تصدر بالاعتماد على التدريب الذي نقدمه إليه. ما الذي قد يسبب هذا التحيز العفوي غير المقصود؟",
"fishlong-training-pause2": "تقديم المزيد من التدريب إلى الروبوت A.I. سيساعده على تعلم الكلمة الخاصة بك. واصل التدريب.",
"fishlong-training-pause3": "كل اختيار تقوم به قد يساعد على تعلم A.I.. واصل التدريب.",
"fishlong-training-pause4": "هل تعتقد بأن الروبوت A.I لديه بيانات تدريبية كافية؟ سيقدم A.I. عملاً أفضل مع تلقية الكثير من البيانات. استمر حتى تعتقد بأن A.I. أصبح جاهزاً.",
"fishlong-words-init1": "لِنرى إذا كان الروبوت A.I. يمكنه تعلم كلمة أقل وضوحاً.",
"fishlong-words-init2": "لِنعلّم الروبوت A.I. كلمة تعتمد على وجهة نظرك. لك الحرية في انتقاء كلمة السمكة التي تتطابق مع الكلمة الخاصة بك.",
"fishshort-pond-init1": "اعتماداً على التدريب الذي قدمته إلى A.I.، فقد قام الروبوت A.I. بتعريف {n, plural,one {هذه} other {تلك}} السمكة باعتبارها “{word}”. كيف فعل الروبوت؟ يمكنك تقديم المزيد من التدريب إذا كنت ترغب في تحسين النتائج. A.I. هذا؟",
"fishshort-pond-init2": "انقر على زر معلومات لمشاهدة ما تعمله الروبوت A.I.",
"fishshort-predicting-init1": "باستخدام بيانات التدريب الخاصة بك، أنت قمت ببرمجة الروبوت A.I. لكي يتعرف على {word} سمكة.",
"fishshort-predicting-init2": "دعونا نرى الروبوت A.I. وهو يتعرف على {word} سمكة.",
"fishshort-words-init1": "يمكن استخدام الذكاء الاصطناعي والتعلم الآلي في تعلم الحاسوب أنماطاً جديدة.",
"fishshort-words-init2": "دعونا نعلم الروبوت A.I. كلمة جديدة من خلال عرض أمثلة على ذلك النوع من الأسماك.",
"fishshort-words-training-pause1": "هل تعتقد بأن الروبوت A.I. لديه بيانات تدريبية كافية؟ يمكنك النقر فوق متابعة لمعرفة المزيد.",
"fishshort-words-training-pause2": "عمل رائع! حافظ على تدريب الروبوت A.I.، أو استمر عندما تكون مستعداً.",
"fishvtrash-pond-init1": "بناءً على تدريبك، إليك بعض الأشياء التي تعرف عليها الروبوت A.I كـ\"سمكة\".كيف فعل الروبوت A.I هذا؟",
"fishvtrash-pond-init2": "يمكنك تدريب الروبوت A.I أكثر من ذلك...",
"fishvtrash-pond-init3": "...أو المتابعة.",
"fishvtrash-pond-recall": "انقر هنا للتبديل بين الأشياء التي تم تحديدها على أنها \"سمكة\"، أو \"ليست سمكة\".",
"fishvtrash-predicting-init1": "دعونا نشاهد الآن إذا كان الروبوت A.I. يعرف كيف يبدو شكل \"السمكة\" أم لا.",
"fishvtrash-predicting-init2": "سيعمل الروبوت A.I على تحليل مجموعة من الأشياء وتصنيفها بناءً على تدريبك.",
"fishvtrash-predicting-init3": "هيا بنا!",
"fishvtrash-training-init1": "تضر القمامة الملقاه في الماء بالحياة البحرية.في هذا النشاط ستبرمج او تتدرب على الذكاء الاصطناعي للتمييز بين السمكة وسلة المهملات .هيا بنا ننظف المحيط .انقر على اي مكان بالشاشة للمتابعة.",
"fishvtrash-training-init2": "دعونا نقابل الروبوت A.I.",
"fishvtrash-training-init3": "لا يعلم الروبوت A.I. إذا كان هذا الشيء سمكة أو نفايات، إلا أنه يمكنه معالجة الصور والتعرف على الأنماط.",
"fishvtrash-training-init4": "لبرمجة الروبوت A.I.، استخدم الأزرار لتسمية الصورة بوصفها \"سمكة\"، أو أنها \"ليست سمكة\". التدريب الذي تقدمه سيعلم الروبوت A.I. التعرف على الأنماط لوحده. دعونا نبدأ!",
"fishvtrash-training-pause1": "17 مليار رطل من بلاستيك تدخل المحيط كل عام.",
"fishvtrash-training-pause2": "أنت تقوم ببرمجة أو تدريب الروبوت A.I.مع كل نقرة.واصل العمل.",
"fishvtrash-training-pause3": "80% من تلوث المحيط يأتي من النفايات الأرضية، التي تقدّر خسارتها بـ 13 مليار دولار في السنة.",
"fishvtrash-training-pause4": "يتعلم الروبوت A.I من الخيارات التي لديه. إذا قمت بإجراء اختيارات \"خاطئة\"، فأن الروبوت A.I سيكرر أخطائك. واصل التدريب.",
"fishvtrash-training-pause5": "عمل رائع! كلما قدمت المزيد من البيانات إلى الروبوت A.I، كلما تعلم أشياء أكثر. حافظ على تدريب الروبوت A.I، أو استمر عندما تكون مستعداً.",
"fun": "متعة",
"glitchy": "تشويش",
"green": "أخضر",
"happy": "سعيد",
"hungry": "جائع",
"isThisAFish": "هل هذه سمكة؟",
"isThisFish": "هل هذه سمكة {word}؟",
"mostImportantParts": "كانت هذه الأجزاء الأكثر أهمية في السمكة:",
"mostImportantPartsDescription": "كانت هذه هي الأجزاء الأكثر أهمية في السمكة لتحديد نوعية هذه السمكة “*{word}*” أو“**{notWord}**”.",
"mouths": "فم",
"newWord": "كلمة جديدة",
"no": "لا",
"notWord": "ليس {word}",
"pectoralFinsFront": "زعانف صدرية",
"playful": "محب للعب",
"rectangular": "مستطيل",
"red": "أحمر",
"run": "تشغيل",
"scales": "قشور",
"scary": "مُخيف",
"silly": "ٍسخيف",
"spooky": "عصبي",
"tails": "ذيل",
"trainMore": "تدريب أكثر",
"triangular": "مثلث",
"waterCreature": "مخلوق بحري",
"wild": "بري",
"wordQuestionLong": "اختر كلمة جديدة كي يتعلمها A.I.",
"wordQuestionShort": "ما نوع السمكة التي ترغب أن يتدرب A.I. على كشفها؟",
"yes": "نعم"
}
Loading
Loading