UNPKG

yoastseo-dep

Version:

Yoast clientside page analysis

207 lines (185 loc) 7.36 kB
import { __, sprintf } from "@wordpress/i18n"; import { map, merge } from "lodash-es"; import formatNumber from "../../../helpers/formatNumber"; import { inRangeStartInclusive as inRange } from "../../helpers/assessments/inRange"; import { createAnchorOpeningTag } from "../../../helpers/shortlinker"; import { stripIncompleteTags as stripTags } from "../../../languageProcessing/helpers/sanitize/stripHTMLTags"; import AssessmentResult from "../../../values/AssessmentResult"; import Mark from "../../../values/Mark.js"; import marker from "../../../markers/addMark.js"; import Assessment from "../assessment"; import removeHtmlBlocks from "../../../languageProcessing/helpers/html/htmlParser"; import getWords from "../../../languageProcessing/helpers/word/getWords"; import { filterShortcodesFromHTML } from "../../../languageProcessing/helpers"; /** * Represents the assessment that checks whether there are enough transition words in the text. */ export default class TransitionWordsAssessment extends Assessment { /** * Sets the identifier and the config. * * @param {object} config The configuration to use. * * @returns {void} */ constructor( config = {} ) { super(); const defaultConfig = { urlTitle: createAnchorOpeningTag( "https://yoa.st/34z" ), urlCallToAction: createAnchorOpeningTag( "https://yoa.st/35a" ), applicableIfTextLongerThan: 200, }; this.identifier = "textTransitionWords"; this._config = merge( defaultConfig, config ); } /** * Calculates the actual percentage of transition words in the sentences. * * @param {object} sentences The object containing the total number of sentences and the number of sentences containing * a transition word. * * @returns {number} The percentage of sentences containing a transition word. */ calculateTransitionWordPercentage( sentences ) { if ( sentences.transitionWordSentences === 0 || sentences.totalSentences === 0 ) { return 0; } return formatNumber( ( sentences.transitionWordSentences / sentences.totalSentences ) * 100 ); } /** * Calculates the score for the assessment based on the percentage of sentences containing transition words. * * @param {number} percentage The percentage of sentences containing transition words. * * @returns {number} The score. */ calculateScoreFromPercentage( percentage ) { if ( percentage < 20 ) { // Red indicator. return 3; } if ( inRange( percentage, 20, 30 ) ) { // Orange indicator. return 6; } // Green indicator. return 9; } /** * Calculates transition word result. * * @param {object} transitionWordSentences The object containing the total number of sentences and the number of sentences containing * a transition word. * * @returns {object} Object containing score and text. */ calculateTransitionWordResult( transitionWordSentences ) { const percentage = this.calculateTransitionWordPercentage( transitionWordSentences ); const score = this.calculateScoreFromPercentage( percentage ); const hasMarks = ( percentage > 0 ); if ( score < 7 && percentage === 0 ) { return { score: formatNumber( score ), hasMarks: hasMarks, text: sprintf( /* translators: %1$s and %3$s expand to a link to yoast.com, %2$s expands to the anchor end tag */ __( "%1$sTransition words%2$s: None of the sentences contain transition words. %3$sUse some%2$s.", "wordpress-seo" ), this._config.urlTitle, "</a>", this._config.urlCallToAction ), }; } if ( score < 7 ) { return { score: formatNumber( score ), hasMarks: hasMarks, text: sprintf( /* translators: %1$s and %4$s expand to a link to yoast.com, %2$s expands to the anchor end tag, %3$s expands to the percentage of sentences containing transition words */ __( // eslint-disable-next-line max-len "%1$sTransition words%2$s: Only %3$s of the sentences contain transition words, which is not enough. %4$sUse more of them%2$s.", "wordpress-seo" ), this._config.urlTitle, "</a>", percentage + "%", this._config.urlCallToAction ), }; } return { score: formatNumber( score ), hasMarks: hasMarks, text: sprintf( /* translators: %1$s expands to a link on yoast.com, %3$s expands to the anchor end tag. */ __( "%1$sTransition words%2$s: Well done!", "wordpress-seo" ), this._config.urlTitle, "</a>" ), }; } /** * Scores the percentage of sentences including one or more transition words. * * @param {object} paper The paper to use for the assessment. * @param {object} researcher The researcher used for calling research. * * @returns {object} The Assessment result. */ getResult( paper, researcher ) { const transitionWordSentences = researcher.getResearch( "findTransitionWords" ); const transitionWordResult = this.calculateTransitionWordResult( transitionWordSentences ); const assessmentResult = new AssessmentResult(); assessmentResult.setScore( transitionWordResult.score ); assessmentResult.setText( transitionWordResult.text ); assessmentResult.setHasMarks( transitionWordResult.hasMarks ); return assessmentResult; } /** * Marks text for the transition words assessment. * * @param {Paper} paper The paper to use for the marking. * @param {Researcher} researcher The researcher containing the necessary research. * * @returns {Array<Mark>} A list of marks that should be applied. */ getMarks( paper, researcher ) { const transitionWordSentences = researcher.getResearch( "findTransitionWords" ); return map( transitionWordSentences.sentenceResults, function( sentenceResult ) { let sentence = sentenceResult.sentence; sentence = stripTags( sentence ); return new Mark( { original: sentence, marked: marker( sentence ), } ); } ); } /** * Checks if the transition words assessment is applicable to the paper. Language-specific length requirements and methods of counting text length * may apply (e.g. for Japanese, the text should be counted in characters instead of words, which also makes the minimum required length higher). * * @param {Paper} paper The paper to check. * @param {Researcher} researcher The researcher object. * * @returns {boolean} Returns true if the language is available, the paper is not empty and the text is longer than the minimum required length. */ isApplicable( paper, researcher ) { const customCountLength = researcher.getHelper( "customCountLength" ); const customApplicabilityConfig = researcher.getConfig( "assessmentApplicability" ).transitionWords; if ( customApplicabilityConfig ) { this._config.applicableIfTextLongerThan = customApplicabilityConfig; } let text = paper.getText(); text = removeHtmlBlocks( text ); text = filterShortcodesFromHTML( text, paper._attributes && paper._attributes.shortcodes ); const textLength = customCountLength ? customCountLength( text ) : getWords( text ).length; // Do not use hasEnoughContent in this assessment as it is mostly redundant with `textLength >= this._config.applicableIfTextLongerThan` return textLength >= this._config.applicableIfTextLongerThan && researcher.hasResearch( "findTransitionWords" ); } }