mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2025-12-19 10:07:46 -05:00
feat(curriculum): add i18n-curriculum submodule (#55341)
Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com>
This commit is contained in:
4
.gitmodules
vendored
Normal file
4
.gitmodules
vendored
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
[submodule "curriculum/i18n-curriculum"]
|
||||||
|
path = curriculum/i18n-curriculum
|
||||||
|
url = https://github.com/freeCodeCamp/i18n-curriculum.git
|
||||||
|
ignore = dirty
|
||||||
@@ -6,8 +6,9 @@ const envData = require('../config/env.json');
|
|||||||
const {
|
const {
|
||||||
getChallengesForLang,
|
getChallengesForLang,
|
||||||
generateChallengeCreator,
|
generateChallengeCreator,
|
||||||
CHALLENGES_DIR,
|
ENGLISH_CHALLENGES_DIR,
|
||||||
META_DIR,
|
META_DIR,
|
||||||
|
CHALLENGES_DIR,
|
||||||
getChallengesDirForLang
|
getChallengesDirForLang
|
||||||
} = require('../../curriculum/get-challenges');
|
} = require('../../curriculum/get-challenges');
|
||||||
|
|
||||||
@@ -24,7 +25,11 @@ exports.replaceChallengeNode = () => {
|
|||||||
const metaPath = path.resolve(META_DIR, `${blockName}/meta.json`);
|
const metaPath = path.resolve(META_DIR, `${blockName}/meta.json`);
|
||||||
delete require.cache[require.resolve(metaPath)];
|
delete require.cache[require.resolve(metaPath)];
|
||||||
const meta = require(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);
|
const i18nPath = path.resolve(CHALLENGES_DIR, curriculumLocale, filePath);
|
||||||
// TODO: reimplement hot-reloading of certifications
|
// TODO: reimplement hot-reloading of certifications
|
||||||
const createChallenge = generateChallengeCreator(
|
const createChallenge = generateChallengeCreator(
|
||||||
|
|||||||
@@ -21,17 +21,34 @@ const { metaSchemaValidator } = require('./schema/meta-schema');
|
|||||||
|
|
||||||
const access = util.promisify(fs.access);
|
const access = util.promisify(fs.access);
|
||||||
|
|
||||||
const CHALLENGES_DIR = path.resolve(__dirname, 'challenges');
|
const ENGLISH_CHALLENGES_DIR = path.resolve(__dirname, 'challenges');
|
||||||
const META_DIR = path.resolve(CHALLENGES_DIR, '_meta');
|
const ENGLISH_DICTIONARIES_DIR = path.resolve(__dirname, 'dictionaries');
|
||||||
exports.CHALLENGES_DIR = CHALLENGES_DIR;
|
const META_DIR = path.resolve(ENGLISH_CHALLENGES_DIR, '_meta');
|
||||||
exports.META_DIR = META_DIR;
|
|
||||||
|
|
||||||
const COMMENT_TRANSLATIONS = createCommentMap(
|
const CURRICULUM_DIR = path.resolve(
|
||||||
path.resolve(__dirname, 'dictionaries')
|
__dirname,
|
||||||
|
process.env.BUILD_WITH_SUBMODULE === 'true'
|
||||||
|
? 'i18n-curriculum/curriculum'
|
||||||
|
: '.'
|
||||||
);
|
);
|
||||||
|
|
||||||
function createCommentMap(dictionariesDir) {
|
const CHALLENGES_DIR = path.resolve(CURRICULUM_DIR, 'challenges');
|
||||||
// get all the languages for which there are dictionaries.
|
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);
|
const languages = fs.readdirSync(dictionariesDir);
|
||||||
|
|
||||||
// get all their dictionaries
|
// get all their dictionaries
|
||||||
@@ -45,11 +62,15 @@ function createCommentMap(dictionariesDir) {
|
|||||||
|
|
||||||
// get the english dicts
|
// get the english dicts
|
||||||
const COMMENTS_TO_TRANSLATE = require(
|
const COMMENTS_TO_TRANSLATE = require(
|
||||||
path.resolve(dictionariesDir, 'english', 'comments.json')
|
path.resolve(englishDictionariesDir, 'english', 'comments.json')
|
||||||
);
|
);
|
||||||
|
|
||||||
const COMMENTS_TO_NOT_TRANSLATE = require(
|
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
|
// map from english comment text to translations
|
||||||
@@ -98,8 +119,12 @@ function getTranslationEntry(dicts, { engId, text }) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function getChallengesDirForLang(lang) {
|
function getChallengesDirForLang(lang) {
|
||||||
|
if (lang === 'english') {
|
||||||
|
return path.resolve(ENGLISH_CHALLENGES_DIR, `${lang}`);
|
||||||
|
} else {
|
||||||
return path.resolve(CHALLENGES_DIR, `${lang}`);
|
return path.resolve(CHALLENGES_DIR, `${lang}`);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function getMetaForBlock(block) {
|
function getMetaForBlock(block) {
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
@@ -221,7 +246,7 @@ async function buildChallenges({ path: filePath }, curriculum, lang) {
|
|||||||
const isCert = path.extname(filePath) === '.yml';
|
const isCert = path.extname(filePath) === '.yml';
|
||||||
const englishPath = path.resolve(
|
const englishPath = path.resolve(
|
||||||
__dirname,
|
__dirname,
|
||||||
CHALLENGES_DIR,
|
ENGLISH_CHALLENGES_DIR,
|
||||||
'english',
|
'english',
|
||||||
filePath
|
filePath
|
||||||
);
|
);
|
||||||
@@ -336,7 +361,8 @@ function challengeFilesToPolys(files) {
|
|||||||
|
|
||||||
async function assertHasEnglishSource(filePath, lang, englishPath) {
|
async function assertHasEnglishSource(filePath, lang, englishPath) {
|
||||||
const missingEnglish =
|
const missingEnglish =
|
||||||
lang !== 'english' && !(await hasEnglishSource(CHALLENGES_DIR, filePath));
|
lang !== 'english' &&
|
||||||
|
!(await hasEnglishSource(ENGLISH_CHALLENGES_DIR, filePath));
|
||||||
if (missingEnglish)
|
if (missingEnglish)
|
||||||
throw Error(`Missing English challenge for
|
throw Error(`Missing English challenge for
|
||||||
${filePath}
|
${filePath}
|
||||||
|
|||||||
@@ -56,12 +56,14 @@ describe('create non-English challenge', () => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
it('returns an object', () => {
|
it('returns an object', () => {
|
||||||
expect(typeof createCommentMap(dictionaryDir)).toBe('object');
|
expect(typeof createCommentMap(dictionaryDir, dictionaryDir)).toBe(
|
||||||
|
'object'
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('fallback to the untranslated string', () => {
|
it('fallback to the untranslated string', () => {
|
||||||
expect.assertions(2);
|
expect.assertions(2);
|
||||||
const commentMap = createCommentMap(incompleteDictDir);
|
const commentMap = createCommentMap(incompleteDictDir, incompleteDictDir);
|
||||||
expect(commentMap['To be translated one'].spanish).toEqual(
|
expect(commentMap['To be translated one'].spanish).toEqual(
|
||||||
'Spanish translation one'
|
'Spanish translation one'
|
||||||
);
|
);
|
||||||
@@ -78,7 +80,7 @@ describe('create non-English challenge', () => {
|
|||||||
'Not translated one',
|
'Not translated one',
|
||||||
'Not translated two'
|
'Not translated two'
|
||||||
];
|
];
|
||||||
const map = createCommentMap(dictionaryDir);
|
const map = createCommentMap(dictionaryDir, dictionaryDir);
|
||||||
expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds));
|
expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds));
|
||||||
|
|
||||||
const mapValue = map['To be translated one'];
|
const mapValue = map['To be translated one'];
|
||||||
@@ -98,7 +100,7 @@ describe('create non-English challenge', () => {
|
|||||||
'Not translated one',
|
'Not translated one',
|
||||||
'Not translated two'
|
'Not translated two'
|
||||||
];
|
];
|
||||||
const map = createCommentMap(dictionaryDir);
|
const map = createCommentMap(dictionaryDir, dictionaryDir);
|
||||||
expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds));
|
expect(Object.keys(map)).toEqual(expect.arrayContaining(expectedIds));
|
||||||
|
|
||||||
const translatedOne = map['To be translated one'];
|
const translatedOne = map['To be translated one'];
|
||||||
|
|||||||
1
curriculum/i18n-curriculum
Submodule
1
curriculum/i18n-curriculum
Submodule
Submodule curriculum/i18n-curriculum added at 531d189f02
@@ -51,6 +51,9 @@ FREECODECAMP_NODE_ENV=development
|
|||||||
CLIENT_LOCALE=english
|
CLIENT_LOCALE=english
|
||||||
CURRICULUM_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 or hide WIP in progress challenges
|
||||||
SHOW_UPCOMING_CHANGES=false
|
SHOW_UPCOMING_CHANGES=false
|
||||||
SHOW_NEW_CURRICULUM=true
|
SHOW_NEW_CURRICULUM=true
|
||||||
|
|||||||
Reference in New Issue
Block a user