UNPKG

yoastseo-dep

Version:

Yoast clientside page analysis

135 lines (118 loc) 4.45 kB
import { sprintf } from "@wordpress/i18n"; import { isString } from "lodash-es"; import AssessmentResult from "../../../values/AssessmentResult"; import Mark from "../../../values/Mark"; import addMark from "../../../markers/addMark"; import { createAnchorOpeningTag } from "../../../helpers/shortlinker"; import { getWords } from "../../../languageProcessing"; import { includesConsecutiveWords } from "./helpers/includesConsecutiveWords"; /** * An inclusive language assessment. * * Based on the configuration given to it in the constructor, it assesses * whether a paper's text contains potentially non-inclusive phrases and * suggests a potentially more inclusive alternative. */ export default class InclusiveLanguageAssessment { /** * Creates a new inclusive language assessment. * * @param {object} config The assessment configuration. * * @param {string} config.identifier The identifier of this assessment. * @param {string[]} config.nonInclusivePhrases The non-inclusive phrases. * @param {string|array} config.inclusiveAlternatives The suggested alternative, more inclusive, phrase(s). * @param {number} config.score The score to give if the non-inclusive phrase is recognized in the text. * @param {string} config.feedbackFormat The feedback format string, * should include a `%1$s` placeholder for the non-inclusive phrase * and `%2$s` (and potentially further replacements) for the suggested alternative(s). * @param {string} config.learnMoreUrl The URL to an article explaining more about this specific assessment. * @param {function} [config.rule] A potential additional rule for targeting the non-inclusive phrases. * @param {boolean} [config.caseSensitive=false] If the inclusive phrase is case-sensitive, defaults to `false`. * * @returns {void} */ constructor( { identifier, nonInclusivePhrases, inclusiveAlternatives, score, feedbackFormat, learnMoreUrl, rule, caseSensitive, category } ) { this.identifier = identifier; this.nonInclusivePhrases = nonInclusivePhrases; this.inclusiveAlternatives = inclusiveAlternatives; if ( isString( this.inclusiveAlternatives ) ) { this.inclusiveAlternatives = [ this.inclusiveAlternatives ]; } this.score = score; this.feedbackFormat = feedbackFormat; this.learnMoreUrl = createAnchorOpeningTag( learnMoreUrl ); this.rule = rule || includesConsecutiveWords; this.caseSensitive = caseSensitive || false; this.category = category; } /** * Checks whether the assessment is applicable for the given paper. * * @param {Paper} paper The paper to check. * @param {Researcher} researcher The researcher. * * @returns {boolean} Whether the assessment is applicable for the given paper. */ isApplicable( paper, researcher ) { const sentences = researcher.getResearch( "sentences" ); // Also include the text title in the analysis as a separate sentence. const textTitle = paper.getTextTitle(); sentences.push( textTitle ); this.foundPhrases = []; sentences.forEach( sentence => { let words = getWords( sentence, false ); if ( ! this.caseSensitive ) { words = words.map( word => word.toLocaleLowerCase() ); } const foundPhrase = this.nonInclusivePhrases.find( phrase => this.rule( words, getWords( phrase, false ) ).length >= 1 ); if ( foundPhrase ) { this.foundPhrases.push( { sentence: sentence, phrase: foundPhrase, } ); } } ); return this.foundPhrases.length >= 1; } /** * Execute the Assessment and return a result. * * @returns {AssessmentResult} The result of the assessment, containing both a score and a descriptive text. */ getResult() { const link = sprintf( "%1$sLearn more.%2$s", this.learnMoreUrl, "</a>" ); const text = sprintf( this.feedbackFormat, this.foundPhrases[ 0 ].phrase, ...this.inclusiveAlternatives ); const result = new AssessmentResult( { score: this.score, text: `${ text } ${ link }`, } ); result.setIdentifier( this.identifier ); result.setHasMarks( true ); return result; } /** * Marks text for the inclusive language assessment. * * @returns {Array<Mark>} A list of marks that should be applied. */ getMarks() { if ( ! this.foundPhrases ) { return []; } return this.foundPhrases.map( foundPhrase => new Mark( { original: foundPhrase.sentence, marked: addMark( foundPhrase.sentence ), } ) ); } }