yoastseo-dep
Version:
Yoast clientside page analysis
151 lines (134 loc) • 4.67 kB
JavaScript
/** @module analyses/calculateFleschReading */
import stripNumbers from "../helpers/sanitize/stripNumbers.js";
import countSentences from "../helpers/sentence/countSentences.js";
import countWords from "../helpers/word/countWords.js";
import countSyllables from "../helpers/syllables/countSyllables.js";
import { clamp, inRange } from "lodash-es";
/**
* Calculates an average from a total and an amount
*
* @param {number} total The total.
* @param {number} amount The amount.
* @returns {number} The average from the total and the amount.
*/
const getAverage = function( total, amount ) {
return total / amount;
};
/**
* The Flesch reading ease difficulty.
* @readonly
* @enum {number}
*/
export const DIFFICULTY = {
NO_DATA: -1,
VERY_EASY: 0,
EASY: 1,
FAIRLY_EASY: 2,
OKAY: 3,
FAIRLY_DIFFICULT: 4,
DIFFICULT: 5,
VERY_DIFFICULT: 6,
};
/**
* Returns the Flesch reading ease difficulty based on the boundaries
* defined in the score configuration.
*
* @param {number} score The Flesch reading ease score.
* @param {Object} scoreConfiguration The score configuration.
*
* @returns {DIFFICULTY} The Flesch reading ease difficulty.
*/
function getDifficulty( score, scoreConfiguration ) {
if ( score >= scoreConfiguration.borders.veryEasy ) {
return DIFFICULTY.VERY_EASY;
} else if ( inRange( score, scoreConfiguration.borders.easy, scoreConfiguration.borders.veryEasy ) ) {
return DIFFICULTY.EASY;
} else if ( inRange( score, scoreConfiguration.borders.fairlyEasy, scoreConfiguration.borders.easy ) ) {
return DIFFICULTY.FAIRLY_EASY;
} else if ( inRange( score, scoreConfiguration.borders.okay, scoreConfiguration.borders.fairlyEasy ) ) {
return DIFFICULTY.OKAY;
} else if ( inRange( score, scoreConfiguration.borders.fairlyDifficult, scoreConfiguration.borders.okay ) ) {
return DIFFICULTY.FAIRLY_DIFFICULT;
} else if ( inRange( score, scoreConfiguration.borders.difficult, scoreConfiguration.borders.fairlyDifficult ) ) {
return DIFFICULTY.DIFFICULT;
}
return DIFFICULTY.VERY_DIFFICULT;
}
/**
* Retrieves the scoring configuration defining the boundaries to use to
* determine the Flesch reading ease difficulty.
*
* @param {Researcher} researcher The researcher.
*
* @returns {Object} The language specific scoring configuration, or the default configuration if not available.
*/
function getConfiguration( researcher ) {
const languageSpecificConfig = researcher.getConfig( "fleschReadingEaseScores" );
const defaultConfig = {
borders: {
veryEasy: 90,
easy: 80,
fairlyEasy: 70,
okay: 60,
fairlyDifficult: 50,
difficult: 30,
veryDifficult: 0,
},
scores: {
veryEasy: 9,
easy: 9,
fairlyEasy: 9,
okay: 9,
fairlyDifficult: 6,
difficult: 3,
veryDifficult: 3,
},
};
return languageSpecificConfig ? languageSpecificConfig : defaultConfig;
}
/**
* This calculates the Flesch reading score for a given text.
*
* @param {Paper} paper The paper containing the text.
* @param {Researcher} researcher The researcher.
*
* @returns {{ score: number, difficulty: DIFFICULTY }} The Flesch reading score.
*/
export default function( paper, researcher ) {
const syllables = researcher.getConfig( "syllables" );
const memoizedTokenizer = researcher.getHelper( "memoizedTokenizer" );
const languageSpecificConfiguration = getConfiguration( researcher );
let text = paper.getText();
if ( text === "" ) {
// A score of -1 signals to the code down the line that no valid FRE was calculated.
return {
score: -1,
difficulty: DIFFICULTY.NO_DATA,
};
}
text = stripNumbers( text );
const numberOfSentences = countSentences( text, memoizedTokenizer );
const numberOfWords = countWords( text );
// Do not show the Flesch reading ease when there is not enough data for the FRE to make sense. Also used to prevent division by zero errors.
// A score of -1 signals to the code down the line that no valid FRE was calculated.
if ( numberOfSentences < 1 || numberOfWords <= 10 ) {
return {
score: -1,
difficulty: DIFFICULTY.NO_DATA,
};
}
const numberOfSyllables = countSyllables( text, syllables );
const averageWordsPerSentence = getAverage( numberOfWords, numberOfSentences );
const syllablesPer100Words = numberOfSyllables * ( 100 / numberOfWords );
const statistics = {
numberOfSentences,
numberOfWords,
numberOfSyllables,
averageWordsPerSentence,
syllablesPer100Words,
};
const getScore = researcher.getHelper( "fleschReadingScore" );
const score = clamp( getScore( statistics ), 0, 100 );
const difficulty = getDifficulty( score, languageSpecificConfiguration );
return { score, difficulty };
}