From 4a506f6e063a3b755630b0fca8d9abd5750ede33 Mon Sep 17 00:00:00 2001 From: Tom <20648924+moT01@users.noreply.github.com> Date: Tue, 9 Jul 2024 09:21:38 -0500 Subject: [PATCH] feat(curriculum): add i18n-curriculum submodule (#55341) Co-authored-by: Oliver Eyton-Williams --- .gitmodules | 4 +++ client/utils/build-challenges.js | 9 ++++-- curriculum/get-challenges.js | 52 +++++++++++++++++++++++-------- curriculum/get-challenges.test.js | 10 +++--- curriculum/i18n-curriculum | 1 + sample.env | 3 ++ 6 files changed, 60 insertions(+), 19 deletions(-) create mode 100644 .gitmodules create mode 160000 curriculum/i18n-curriculum diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000000..513eae4a9a7 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "curriculum/i18n-curriculum"] + path = curriculum/i18n-curriculum + url = https://github.com/freeCodeCamp/i18n-curriculum.git + ignore = dirty diff --git a/client/utils/build-challenges.js b/client/utils/build-challenges.js index 44f122f4c34..6df479c2047 100644 --- a/client/utils/build-challenges.js +++ b/client/utils/build-challenges.js @@ -6,8 +6,9 @@ const envData = require('../config/env.json'); const { getChallengesForLang, generateChallengeCreator, - CHALLENGES_DIR, + ENGLISH_CHALLENGES_DIR, META_DIR, + CHALLENGES_DIR, getChallengesDirForLang } = require('../../curriculum/get-challenges'); @@ -24,7 +25,11 @@ exports.replaceChallengeNode = () => { const metaPath = path.resolve(META_DIR, `${blockName}/meta.json`); delete require.cache[require.resolve(metaPath)]; const meta = require(metaPath); - const englishPath = path.resolve(CHALLENGES_DIR, 'english', filePath); + const englishPath = path.resolve( + ENGLISH_CHALLENGES_DIR, + 'english', + filePath + ); const i18nPath = path.resolve(CHALLENGES_DIR, curriculumLocale, filePath); // TODO: reimplement hot-reloading of certifications const createChallenge = generateChallengeCreator( diff --git a/curriculum/get-challenges.js b/curriculum/get-challenges.js index 08585fe875c..3a39ab939f4 100644 --- a/curriculum/get-challenges.js +++ b/curriculum/get-challenges.js @@ -21,17 +21,34 @@ const { metaSchemaValidator } = require('./schema/meta-schema'); const access = util.promisify(fs.access); -const CHALLENGES_DIR = path.resolve(__dirname, 'challenges'); -const META_DIR = path.resolve(CHALLENGES_DIR, '_meta'); -exports.CHALLENGES_DIR = CHALLENGES_DIR; -exports.META_DIR = META_DIR; +const ENGLISH_CHALLENGES_DIR = path.resolve(__dirname, 'challenges'); +const ENGLISH_DICTIONARIES_DIR = path.resolve(__dirname, 'dictionaries'); +const META_DIR = path.resolve(ENGLISH_CHALLENGES_DIR, '_meta'); -const COMMENT_TRANSLATIONS = createCommentMap( - path.resolve(__dirname, 'dictionaries') +const CURRICULUM_DIR = path.resolve( + __dirname, + process.env.BUILD_WITH_SUBMODULE === 'true' + ? 'i18n-curriculum/curriculum' + : '.' ); -function createCommentMap(dictionariesDir) { - // get all the languages for which there are dictionaries. +const CHALLENGES_DIR = path.resolve(CURRICULUM_DIR, 'challenges'); +const DICTIONARIES_DIR = path.resolve(CURRICULUM_DIR, 'dictionaries'); + +exports.ENGLISH_CHALLENGES_DIR = ENGLISH_CHALLENGES_DIR; +exports.META_DIR = META_DIR; +exports.CHALLENGES_DIR = CHALLENGES_DIR; + +const COMMENT_TRANSLATIONS = createCommentMap( + DICTIONARIES_DIR, + ENGLISH_DICTIONARIES_DIR +); + +function createCommentMap(dictionariesDir, englishDictionariesDir) { + // get all the languages for which there are dictionaries. Note: this has to + // include the english dictionaries since translateCommentsInChallenge treats + // all languages equally and will simply remove comments if there is no entry + // in the comment map. const languages = fs.readdirSync(dictionariesDir); // get all their dictionaries @@ -45,11 +62,15 @@ function createCommentMap(dictionariesDir) { // get the english dicts const COMMENTS_TO_TRANSLATE = require( - path.resolve(dictionariesDir, 'english', 'comments.json') + path.resolve(englishDictionariesDir, 'english', 'comments.json') ); const COMMENTS_TO_NOT_TRANSLATE = require( - path.resolve(dictionariesDir, 'english', 'comments-to-not-translate') + path.resolve( + englishDictionariesDir, + 'english', + 'comments-to-not-translate.json' + ) ); // map from english comment text to translations @@ -98,7 +119,11 @@ function getTranslationEntry(dicts, { engId, text }) { } function getChallengesDirForLang(lang) { - return path.resolve(CHALLENGES_DIR, `${lang}`); + if (lang === 'english') { + return path.resolve(ENGLISH_CHALLENGES_DIR, `${lang}`); + } else { + return path.resolve(CHALLENGES_DIR, `${lang}`); + } } function getMetaForBlock(block) { @@ -221,7 +246,7 @@ async function buildChallenges({ path: filePath }, curriculum, lang) { const isCert = path.extname(filePath) === '.yml'; const englishPath = path.resolve( __dirname, - CHALLENGES_DIR, + ENGLISH_CHALLENGES_DIR, 'english', filePath ); @@ -336,7 +361,8 @@ function challengeFilesToPolys(files) { async function assertHasEnglishSource(filePath, lang, englishPath) { const missingEnglish = - lang !== 'english' && !(await hasEnglishSource(CHALLENGES_DIR, filePath)); + lang !== 'english' && + !(await hasEnglishSource(ENGLISH_CHALLENGES_DIR, filePath)); if (missingEnglish) throw Error(`Missing English challenge for ${filePath} diff --git a/curriculum/get-challenges.test.js b/curriculum/get-challenges.test.js index 9066fdddfbb..5d35db2c0fc 100644 --- a/curriculum/get-challenges.test.js +++ b/curriculum/get-challenges.test.js @@ -56,12 +56,14 @@ describe('create non-English challenge', () => { ); it('returns an object', () => { - expect(typeof createCommentMap(dictionaryDir)).toBe('object'); + expect(typeof createCommentMap(dictionaryDir, dictionaryDir)).toBe( + 'object' + ); }); it('fallback to the untranslated string', () => { expect.assertions(2); - const commentMap = createCommentMap(incompleteDictDir); + const commentMap = createCommentMap(incompleteDictDir, incompleteDictDir); expect(commentMap['To be translated one'].spanish).toEqual( 'Spanish translation one' ); @@ -78,7 +80,7 @@ describe('create non-English challenge', () => { 'Not translated one', 'Not translated two' ]; - const map = createCommentMap(dictionaryDir); + const map = createCommentMap(dictionaryDir, dictionaryDir); expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds)); const mapValue = map['To be translated one']; @@ -98,7 +100,7 @@ describe('create non-English challenge', () => { 'Not translated one', 'Not translated two' ]; - const map = createCommentMap(dictionaryDir); + const map = createCommentMap(dictionaryDir, dictionaryDir); expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds)); const translatedOne = map['To be translated one']; diff --git a/curriculum/i18n-curriculum b/curriculum/i18n-curriculum new file mode 160000 index 00000000000..531d189f02b --- /dev/null +++ b/curriculum/i18n-curriculum @@ -0,0 +1 @@ +Subproject commit 531d189f02b4b13e81f18a4b3558bb4482048473 diff --git a/sample.env b/sample.env index 092811e8cd9..b455708a190 100644 --- a/sample.env +++ b/sample.env @@ -51,6 +51,9 @@ FREECODECAMP_NODE_ENV=development CLIENT_LOCALE=english CURRICULUM_LOCALE=english +# Build using submodule - remove this once the workflow has been entirely moved to use the submodule +BUILD_WITH_SUBMODULE=false + # Show or hide WIP in progress challenges SHOW_UPCOMING_CHANGES=false SHOW_NEW_CURRICULUM=true