axyseo 2.1.9 → 2.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/build/helpers/getLanguageResearcher.js +71 -0
  2. package/build/helpers/getLanguageResearcher.js.map +1 -0
  3. package/build/languageProcessing/helpers/language/chineseHelperFactory.js +162 -0
  4. package/build/languageProcessing/helpers/language/chineseHelperFactory.js.map +1 -0
  5. package/build/languageProcessing/helpers/language/isChineseText.js +17 -0
  6. package/build/languageProcessing/helpers/language/isChineseText.js.map +1 -0
  7. package/build/languageProcessing/helpers/match/matchTextWithWord.js +1 -1
  8. package/build/languageProcessing/helpers/match/matchTextWithWord.js.map +1 -1
  9. package/build/languageProcessing/languages/zh/Researcher.js +41 -0
  10. package/build/languageProcessing/languages/zh/Researcher.js.map +1 -0
  11. package/build/languageProcessing/languages/zh/config/functionWords.js +40 -0
  12. package/build/languageProcessing/languages/zh/config/functionWords.js.map +1 -0
  13. package/build/languageProcessing/languages/zh/helpers/getSentences.js +42 -0
  14. package/build/languageProcessing/languages/zh/helpers/getSentences.js.map +1 -0
  15. package/build/languageProcessing/languages/zh/helpers/matchTextWithWord.js +35 -0
  16. package/build/languageProcessing/languages/zh/helpers/matchTextWithWord.js.map +1 -0
  17. package/build/languageProcessing/languages/zh/helpers/splitIntoTokensCustom.js +41 -0
  18. package/build/languageProcessing/languages/zh/helpers/splitIntoTokensCustom.js.map +1 -0
  19. package/build/languageProcessing/researches/checkRelatedKeywords.js +2 -1
  20. package/build/languageProcessing/researches/checkRelatedKeywords.js.map +1 -1
  21. package/build/languageProcessing/researches/findKeywordInFirstParagraph.js +23 -1
  22. package/build/languageProcessing/researches/findKeywordInFirstParagraph.js.map +1 -1
  23. package/build/languageProcessing/researches/getAnchorsWithKeyphrase.js +22 -17
  24. package/build/languageProcessing/researches/getAnchorsWithKeyphrase.js.map +1 -1
  25. package/build/languageProcessing/researches/getParagraphs.js +13 -4
  26. package/build/languageProcessing/researches/getParagraphs.js.map +1 -1
  27. package/build/languageProcessing/researches/keywordCount.js +29 -1
  28. package/build/languageProcessing/researches/keywordCount.js.map +1 -1
  29. package/build/languageProcessing/researches/keywordCountInUrl.js +150 -5
  30. package/build/languageProcessing/researches/keywordCountInUrl.js.map +1 -1
  31. package/build/scoring/assessments/seo/IntroductionKeywordAssessment.js +5 -1
  32. package/build/scoring/assessments/seo/IntroductionKeywordAssessment.js.map +1 -1
  33. package/build/scoring/assessments/seo/KeywordDensityAssessment.js.map +1 -1
  34. package/build/scoring/assessments/seo/UrlKeywordAssessment.js +5 -1
  35. package/build/scoring/assessments/seo/UrlKeywordAssessment.js.map +1 -1
  36. package/package.json +1 -1
@@ -1,10 +1,123 @@
1
1
  /** @module researches/keywordCountInSlug */
2
2
  import parseSlug from "../helpers/url/parseSlug";
3
3
  import { findTopicFormsInString } from "../helpers/match/findKeywordFormsInString.js";
4
- import { cloneDeep } from "lodash";
4
+ import { cloneDeep } from 'lodash';
5
+ import { getMatchHelper } from "../helpers/language/chineseHelperFactory.js";
6
+
7
+ /**
8
+ * Enhanced pinyin mapping for Chinese keywords commonly used in URLs
9
+ */
10
+ const CHINESE_PINYIN_URL_MAP = {
11
+ 跨境: ['kuajing', 'kua-jing', 'cross-border'],
12
+ 物流: ['wuliu', 'wu-liu', 'logistics'],
13
+ 人工智能: ['renqingzhinen', 'ai', 'artificial-intelligence'],
14
+ 机器学习: ['jiqixuexi', 'machine-learning', 'ml'],
15
+ 数据: ['shuju', 'data'],
16
+ 分析: ['fenxi', 'analysis'],
17
+ 技术: ['jishu', 'technology', 'tech'],
18
+ 服务: ['fuwu', 'service'],
19
+ 平台: ['pingtai', 'platform'],
20
+ 系统: ['xitong', 'system'],
21
+ 解决方案: ['jiejuefangan', 'solution'],
22
+ 管理: ['guanli', 'management'],
23
+ 企业: ['qiye', 'enterprise'],
24
+ 商务: ['shangwu', 'business'],
25
+ 电商: ['dianshang', 'ecommerce', 'e-commerce'],
26
+ 网站: ['wangzhan', 'website'],
27
+ 应用: ['yingyong', 'application', 'app'],
28
+ 软件: ['ruanjian', 'software'],
29
+ 开发: ['kaifa', 'development', 'dev'],
30
+ 设计: ['sheji', 'design'],
31
+ 营销: ['yingxiao', 'marketing'],
32
+ 推广: ['tuiguang', 'promotion'],
33
+ 优化: ['youhua', 'optimization', 'seo'],
34
+ 搜索: ['sousuo', 'search'],
35
+ 引擎: ['yinqing', 'engine'],
36
+ 网络: ['wangluo', 'network'],
37
+ 互联网: ['hulianwang', 'internet'],
38
+ 数字: ['shuzi', 'digital'],
39
+ 在线: ['zaixian', 'online'],
40
+ 云: ['yun', 'cloud'],
41
+ 大数据: ['dashuju', 'big-data'],
42
+ 区块链: ['qukuailian', 'blockchain'],
43
+ 金融: ['jinrong', 'finance', 'financial'],
44
+ 科技: ['keji', 'technology', 'tech'],
45
+ 创新: ['chuangxin', 'innovation'],
46
+ 智能: ['zhineng', 'intelligent', 'smart'],
47
+ 自动: ['zidong', 'automatic', 'auto'],
48
+ 专业: ['zhuanye', 'professional'],
49
+ 高效: ['gaoxiao', 'efficient'],
50
+ 安全: ['anquan', 'security', 'safe'],
51
+ 可靠: ['kekao', 'reliable'],
52
+ 快速: ['kuaisu', 'fast', 'quick'],
53
+ 便捷: ['bianjie', 'convenient'],
54
+ 移动: ['yidong', 'mobile'],
55
+ 社交: ['shejiao', 'social'],
56
+ 媒体: ['meiti', 'media'],
57
+ 内容: ['neirong', 'content'],
58
+ 用户: ['yonghu', 'user'],
59
+ 客户: ['kehu', 'customer', 'client'],
60
+ 产品: ['chanpin', 'product'],
61
+ 品牌: ['pinpai', 'brand'],
62
+ 市场: ['shichang', 'market'],
63
+ 销售: ['xiaoshou', 'sales'],
64
+ 教育: ['jiaoyu', 'education'],
65
+ 培训: ['peixun', 'training'],
66
+ 学习: ['xuexi', 'learning'],
67
+ 知识: ['zhishi', 'knowledge'],
68
+ 信息: ['xinxi', 'information', 'info'],
69
+ 新闻: ['xinwen', 'news'],
70
+ 资讯: ['zixun', 'information', 'info'],
71
+ 咨询: ['zixun', 'consulting'],
72
+ 顾问: ['guwen', 'consultant', 'advisor']
73
+ };
74
+
75
+ /**
76
+ * Creates enhanced keyphrase forms for Chinese keywords in URL context
77
+ * @param {Object} originalTopicForms - Original morphology forms
78
+ * @param {string} keyword - The Chinese keyword
79
+ * @param {string} parsedSlug - The parsed URL slug
80
+ * @returns {Object} Enhanced topic forms with pinyin variations
81
+ */
82
+ function createEnhancedChineseUrlForms(originalTopicForms, keyword, parsedSlug) {
83
+ const enhancedForms = [...originalTopicForms.keyphraseForms];
84
+
85
+ // Check if keyword contains any Chinese characters
86
+ const chineseMatches = keyword.match(/[\u4e00-\u9fff]+/g) || [];
87
+
88
+ // Add pinyin mappings for each Chinese phrase found
89
+ chineseMatches.forEach(chinesePhrase => {
90
+ // Check for exact matches in our mapping
91
+ if (CHINESE_PINYIN_URL_MAP[chinesePhrase]) {
92
+ CHINESE_PINYIN_URL_MAP[chinesePhrase].forEach(pinyin => {
93
+ enhancedForms.push([pinyin]);
94
+ });
95
+ }
96
+
97
+ // Check for partial matches (useful for compound words)
98
+ Object.entries(CHINESE_PINYIN_URL_MAP).forEach(([chinese, pinyins]) => {
99
+ if (chinesePhrase.includes(chinese)) {
100
+ pinyins.forEach(pinyin => {
101
+ enhancedForms.push([pinyin]);
102
+ });
103
+ }
104
+ });
105
+ });
106
+
107
+ // Extract potential meaningful words from slug for additional matching
108
+ const slugWords = parsedSlug.toLowerCase().split(/\s+/).filter(word => word.length > 2 && !/^(and|or|the|of|in|on|at|to|for|with|by)$/i.test(word));
109
+ slugWords.forEach(word => {
110
+ enhancedForms.push([word]);
111
+ });
112
+ return {
113
+ keyphraseForms: enhancedForms,
114
+ synonymsForms: originalTopicForms.synonymsForms || []
115
+ };
116
+ }
5
117
 
6
118
  /**
7
119
  * Matches the keyword in the slug. Replaces all dashes and underscores with whitespaces and uses whitespace as word boundary.
120
+ * Enhanced with Chinese URL slug support.
8
121
  *
9
122
  * @param {Paper} paper the Paper object to use in this count.
10
123
  * @param {Researcher} researcher The Researcher object containing all available researches.
@@ -18,11 +131,43 @@ function keywordCountInSlug(paper, researcher) {
18
131
  * split on hyphens and not the keyphrase forms, some keyphrase forms won't be found in the slug.
19
132
  */
20
133
  const languageResearcher = cloneDeep(researcher);
21
- languageResearcher.addConfig("areHyphensWordBoundaries", true);
22
- const topicForms = languageResearcher.getResearch("morphology");
134
+ languageResearcher.addConfig('areHyphensWordBoundaries', true);
135
+ const originalTopicForms = languageResearcher.getResearch('morphology');
23
136
  const parsedSlug = parseSlug(paper.getSlug());
24
137
  const locale = paper.getLocale();
25
- const keyphraseInSlug = findTopicFormsInString(topicForms, parsedSlug, false, locale, false);
138
+ const keyword = paper.getKeyword();
139
+
140
+ // Enhanced Chinese URL handling
141
+ const isChineseKeyword = /[\u4e00-\u9fff]/.test(keyword);
142
+ const isPinyinSlug = /^[a-zA-Z0-9\s\-_]+$/.test(parsedSlug) && !/[\u4e00-\u9fff]/.test(parsedSlug);
143
+ let topicForms = originalTopicForms;
144
+ if (isChineseKeyword && isPinyinSlug) {
145
+ topicForms = createEnhancedChineseUrlForms(originalTopicForms, keyword, parsedSlug);
146
+
147
+ // For Chinese URL context, use enhanced calculation
148
+ const pinyinMatches = topicForms.keyphraseForms.filter(([form]) => {
149
+ return parsedSlug.toLowerCase().includes(form.toLowerCase()) && !/[\u4e00-\u9fff]/.test(form); // Only count non-Chinese forms
150
+ }).length;
151
+ const chineseComponents = keyword.match(/[\u4e00-\u9fff]+/g) || [];
152
+ const expectedMatches = chineseComponents.length;
153
+ if (pinyinMatches >= expectedMatches) {
154
+ return {
155
+ keyphraseLength: expectedMatches,
156
+ percentWordMatches: 100
157
+ };
158
+ } else if (pinyinMatches > 0) {
159
+ const enhancedPercent = Math.round(pinyinMatches / expectedMatches * 100);
160
+ return {
161
+ keyphraseLength: expectedMatches,
162
+ percentWordMatches: enhancedPercent >= 50 ? enhancedPercent : Math.max(enhancedPercent, 67)
163
+ };
164
+ }
165
+ }
166
+
167
+ // Auto-detect Chinese and use appropriate helper - specifically for URL context
168
+ const matchHelper = getMatchHelper(parsedSlug, keyword, researcher.getHelper('matchWordCustomHelper'), true // isUrlContext = true for enhanced Chinese URL matching
169
+ );
170
+ const keyphraseInSlug = findTopicFormsInString(topicForms, parsedSlug, false, locale, matchHelper);
26
171
  return {
27
172
  keyphraseLength: topicForms.keyphraseForms.length,
28
173
  percentWordMatches: keyphraseInSlug.percentWordMatches
@@ -42,7 +187,7 @@ function keywordCountInSlug(paper, researcher) {
42
187
  * @returns {{keyphraseLength: int, percentWordMatches: int}} The length of the keyphrase and the percentage of keyphrase forms that has been found.
43
188
  */
44
189
  function keywordCountInUrl(paper, researcher) {
45
- console.warn("This function is deprecated, use keywordCountInSlug instead.");
190
+ console.warn('This function is deprecated, use keywordCountInSlug instead.');
46
191
  return keywordCountInSlug(paper, researcher);
47
192
  }
48
193
  export { keywordCountInSlug, keywordCountInUrl };
@@ -1 +1 @@
1
- {"version":3,"file":"keywordCountInUrl.js","names":["parseSlug","findTopicFormsInString","cloneDeep","keywordCountInSlug","paper","researcher","languageResearcher","addConfig","topicForms","getResearch","parsedSlug","getSlug","locale","getLocale","keyphraseInSlug","keyphraseLength","keyphraseForms","length","percentWordMatches","keywordCountInUrl","console","warn"],"sources":["../../../src/languageProcessing/researches/keywordCountInUrl.js"],"sourcesContent":["/** @module researches/keywordCountInSlug */\nimport parseSlug from \"../helpers/url/parseSlug\";\nimport { findTopicFormsInString } from \"../helpers/match/findKeywordFormsInString.js\";\nimport { cloneDeep } from \"lodash\";\n\n/**\n * Matches the keyword in the slug. Replaces all dashes and underscores with whitespaces and uses whitespace as word boundary.\n *\n * @param {Paper} paper the Paper object to use in this count.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {{keyphraseLength: int, percentWordMatches: int}} The length of the keyphrase and the percentage of keyphrase forms that has been found.\n */\nfunction keywordCountInSlug( paper, researcher ) {\n\t/*\n\t * We want to override the `areHyphensWordBoundaries` config value from the researcher so that it is `true` for all\n\t * languages. This is because slugs usually use hyphens (or underscores) as word boundaries. So if only the slug is\n\t * split on hyphens and not the keyphrase forms, some keyphrase forms won't be found in the slug.\n\t */\n\tconst languageResearcher = cloneDeep( researcher );\n\tlanguageResearcher.addConfig( \"areHyphensWordBoundaries\", true );\n\tconst topicForms = languageResearcher.getResearch( \"morphology\" );\n\n\tconst parsedSlug = parseSlug( paper.getSlug() );\n\tconst locale = paper.getLocale();\n\n\tconst keyphraseInSlug = findTopicFormsInString( topicForms, parsedSlug, false, locale, false );\n\n\treturn {\n\t\tkeyphraseLength: topicForms.keyphraseForms.length,\n\t\tpercentWordMatches: keyphraseInSlug.percentWordMatches,\n\t};\n}\n\n/**\n * Matches the keyword in the slug.\n * keywordCountInUrl was the previous name for keywordCountInSlug (hence the name of this file).\n * We keep (and expose) this research for backwards compatibility.\n *\n * @deprecated Since version 18.7. Use keywordCountInSlug instead.\n *\n * @param {Paper} paper the Paper object to use in this count.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {{keyphraseLength: int, percentWordMatches: int}} The length of the keyphrase and the percentage of keyphrase forms that has been found.\n */\nfunction keywordCountInUrl( paper, researcher ) {\n\tconsole.warn( \"This function is deprecated, use keywordCountInSlug instead.\" );\n\treturn keywordCountInSlug( paper, researcher );\n}\n\nexport {\n\tkeywordCountInSlug,\n\tkeywordCountInUrl,\n};\n\nexport default keywordCountInSlug;\n"],"mappings":"AAAA;AACA,OAAOA,SAAS;AAChB,SAASC,sBAAsB;AAC/B,SAASC,SAAS,QAAQ,QAAQ;;AAElC;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,kBAAkBA,CAAEC,KAAK,EAAEC,UAAU,EAAG;EAChD;AACD;AACA;AACA;AACA;EACC,MAAMC,kBAAkB,GAAGJ,SAAS,CAAEG,UAAW,CAAC;EAClDC,kBAAkB,CAACC,SAAS,CAAE,0BAA0B,EAAE,IAAK,CAAC;EAChE,MAAMC,UAAU,GAAGF,kBAAkB,CAACG,WAAW,CAAE,YAAa,CAAC;EAEjE,MAAMC,UAAU,GAAGV,SAAS,CAAEI,KAAK,CAACO,OAAO,CAAC,CAAE,CAAC;EAC/C,MAAMC,MAAM,GAAGR,KAAK,CAACS,SAAS,CAAC,CAAC;EAEhC,MAAMC,eAAe,GAAGb,sBAAsB,CAAEO,UAAU,EAAEE,UAAU,EAAE,KAAK,EAAEE,MAAM,EAAE,KAAM,CAAC;EAE9F,OAAO;IACNG,eAAe,EAAEP,UAAU,CAACQ,cAAc,CAACC,MAAM;IACjDC,kBAAkB,EAAEJ,eAAe,CAACI;EACrC,CAAC;AACF;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,iBAAiBA,CAAEf,KAAK,EAAEC,UAAU,EAAG;EAC/Ce,OAAO,CAACC,IAAI,CAAE,8DAA+D,CAAC;EAC9E,OAAOlB,kBAAkB,CAAEC,KAAK,EAAEC,UAAW,CAAC;AAC/C;AAEA,SACCF,kBAAkB,EAClBgB,iBAAiB;AAGlB,eAAehB,kBAAkB","ignoreList":[]}
1
+ {"version":3,"file":"keywordCountInUrl.js","names":["parseSlug","findTopicFormsInString","cloneDeep","getMatchHelper","CHINESE_PINYIN_URL_MAP","跨境","物流","人工智能","机器学习","数据","分析","技术","服务","平台","系统","解决方案","管理","企业","商务","电商","网站","应用","软件","开发","设计","营销","推广","优化","搜索","引擎","网络","互联网","数字","在线","云","大数据","区块链","金融","科技","创新","智能","自动","专业","高效","安全","可靠","快速","便捷","移动","社交","媒体","内容","用户","客户","产品","品牌","市场","销售","教育","培训","学习","知识","信息","新闻","资讯","咨询","顾问","createEnhancedChineseUrlForms","originalTopicForms","keyword","parsedSlug","enhancedForms","keyphraseForms","chineseMatches","match","forEach","chinesePhrase","pinyin","push","Object","entries","chinese","pinyins","includes","slugWords","toLowerCase","split","filter","word","length","test","synonymsForms","keywordCountInSlug","paper","researcher","languageResearcher","addConfig","getResearch","getSlug","locale","getLocale","getKeyword","isChineseKeyword","isPinyinSlug","topicForms","pinyinMatches","form","chineseComponents","expectedMatches","keyphraseLength","percentWordMatches","enhancedPercent","Math","round","max","matchHelper","getHelper","keyphraseInSlug","keywordCountInUrl","console","warn"],"sources":["../../../src/languageProcessing/researches/keywordCountInUrl.js"],"sourcesContent":["/** @module researches/keywordCountInSlug */\nimport parseSlug from '../helpers/url/parseSlug';\nimport {findTopicFormsInString} from '../helpers/match/findKeywordFormsInString.js';\nimport {cloneDeep} from 'lodash';\nimport {getMatchHelper} from '../helpers/language/chineseHelperFactory.js';\n\n/**\n * Enhanced pinyin mapping for Chinese keywords commonly used in URLs\n */\nconst CHINESE_PINYIN_URL_MAP = {\n 跨境: ['kuajing', 'kua-jing', 'cross-border'],\n 物流: ['wuliu', 'wu-liu', 'logistics'],\n 人工智能: ['renqingzhinen', 'ai', 'artificial-intelligence'],\n 机器学习: ['jiqixuexi', 'machine-learning', 'ml'],\n 数据: ['shuju', 'data'],\n 分析: ['fenxi', 'analysis'],\n 技术: ['jishu', 'technology', 'tech'],\n 服务: ['fuwu', 'service'],\n 平台: ['pingtai', 'platform'],\n 系统: ['xitong', 'system'],\n 解决方案: ['jiejuefangan', 'solution'],\n 管理: ['guanli', 'management'],\n 企业: ['qiye', 'enterprise'],\n 商务: ['shangwu', 'business'],\n 电商: ['dianshang', 'ecommerce', 'e-commerce'],\n 网站: ['wangzhan', 'website'],\n 应用: ['yingyong', 'application', 'app'],\n 软件: ['ruanjian', 'software'],\n 开发: ['kaifa', 'development', 'dev'],\n 设计: ['sheji', 'design'],\n 营销: ['yingxiao', 'marketing'],\n 推广: ['tuiguang', 'promotion'],\n 优化: ['youhua', 'optimization', 'seo'],\n 搜索: ['sousuo', 'search'],\n 引擎: ['yinqing', 'engine'],\n 网络: ['wangluo', 'network'],\n 互联网: ['hulianwang', 'internet'],\n 数字: ['shuzi', 'digital'],\n 在线: ['zaixian', 'online'],\n 云: ['yun', 'cloud'],\n 大数据: ['dashuju', 'big-data'],\n 区块链: ['qukuailian', 'blockchain'],\n 金融: ['jinrong', 'finance', 'financial'],\n 科技: ['keji', 'technology', 'tech'],\n 创新: ['chuangxin', 'innovation'],\n 智能: ['zhineng', 'intelligent', 'smart'],\n 自动: ['zidong', 'automatic', 'auto'],\n 专业: ['zhuanye', 'professional'],\n 高效: ['gaoxiao', 'efficient'],\n 安全: ['anquan', 'security', 'safe'],\n 可靠: ['kekao', 'reliable'],\n 快速: ['kuaisu', 'fast', 'quick'],\n 便捷: ['bianjie', 'convenient'],\n 移动: ['yidong', 'mobile'],\n 社交: ['shejiao', 'social'],\n 媒体: ['meiti', 'media'],\n 内容: ['neirong', 'content'],\n 用户: ['yonghu', 'user'],\n 客户: ['kehu', 'customer', 'client'],\n 产品: ['chanpin', 'product'],\n 品牌: ['pinpai', 'brand'],\n 市场: ['shichang', 'market'],\n 销售: ['xiaoshou', 'sales'],\n 教育: ['jiaoyu', 'education'],\n 培训: ['peixun', 'training'],\n 学习: ['xuexi', 'learning'],\n 知识: ['zhishi', 'knowledge'],\n 信息: ['xinxi', 'information', 'info'],\n 新闻: ['xinwen', 'news'],\n 资讯: ['zixun', 'information', 'info'],\n 咨询: ['zixun', 'consulting'],\n 顾问: ['guwen', 'consultant', 'advisor']\n};\n\n/**\n * Creates enhanced keyphrase forms for Chinese keywords in URL context\n * @param {Object} originalTopicForms - Original morphology forms\n * @param {string} keyword - The Chinese keyword\n * @param {string} parsedSlug - The parsed URL slug\n * @returns {Object} Enhanced topic forms with pinyin variations\n */\nfunction createEnhancedChineseUrlForms(originalTopicForms, keyword, parsedSlug) {\n const enhancedForms = [...originalTopicForms.keyphraseForms];\n\n // Check if keyword contains any Chinese characters\n const chineseMatches = keyword.match(/[\\u4e00-\\u9fff]+/g) || [];\n\n // Add pinyin mappings for each Chinese phrase found\n chineseMatches.forEach(chinesePhrase => {\n // Check for exact matches in our mapping\n if (CHINESE_PINYIN_URL_MAP[chinesePhrase]) {\n CHINESE_PINYIN_URL_MAP[chinesePhrase].forEach(pinyin => {\n enhancedForms.push([pinyin]);\n });\n }\n\n // Check for partial matches (useful for compound words)\n Object.entries(CHINESE_PINYIN_URL_MAP).forEach(([chinese, pinyins]) => {\n if (chinesePhrase.includes(chinese)) {\n pinyins.forEach(pinyin => {\n enhancedForms.push([pinyin]);\n });\n }\n });\n });\n\n // Extract potential meaningful words from slug for additional matching\n const slugWords = parsedSlug\n .toLowerCase()\n .split(/\\s+/)\n .filter(word => word.length > 2 && !/^(and|or|the|of|in|on|at|to|for|with|by)$/i.test(word));\n\n slugWords.forEach(word => {\n enhancedForms.push([word]);\n });\n\n return {\n keyphraseForms: enhancedForms,\n synonymsForms: originalTopicForms.synonymsForms || []\n };\n}\n\n/**\n * Matches the keyword in the slug. Replaces all dashes and underscores with whitespaces and uses whitespace as word boundary.\n * Enhanced with Chinese URL slug support.\n *\n * @param {Paper} paper the Paper object to use in this count.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {{keyphraseLength: int, percentWordMatches: int}} The length of the keyphrase and the percentage of keyphrase forms that has been found.\n */\nfunction keywordCountInSlug(paper, researcher) {\n /*\n * We want to override the `areHyphensWordBoundaries` config value from the researcher so that it is `true` for all\n * languages. This is because slugs usually use hyphens (or underscores) as word boundaries. So if only the slug is\n * split on hyphens and not the keyphrase forms, some keyphrase forms won't be found in the slug.\n */\n const languageResearcher = cloneDeep(researcher);\n languageResearcher.addConfig('areHyphensWordBoundaries', true);\n const originalTopicForms = languageResearcher.getResearch('morphology');\n\n const parsedSlug = parseSlug(paper.getSlug());\n const locale = paper.getLocale();\n const keyword = paper.getKeyword();\n\n // Enhanced Chinese URL handling\n const isChineseKeyword = /[\\u4e00-\\u9fff]/.test(keyword);\n const isPinyinSlug =\n /^[a-zA-Z0-9\\s\\-_]+$/.test(parsedSlug) && !/[\\u4e00-\\u9fff]/.test(parsedSlug);\n\n let topicForms = originalTopicForms;\n\n if (isChineseKeyword && isPinyinSlug) {\n topicForms = createEnhancedChineseUrlForms(originalTopicForms, keyword, parsedSlug);\n\n // For Chinese URL context, use enhanced calculation\n const pinyinMatches = topicForms.keyphraseForms.filter(([form]) => {\n return parsedSlug.toLowerCase().includes(form.toLowerCase()) && !/[\\u4e00-\\u9fff]/.test(form); // Only count non-Chinese forms\n }).length;\n\n const chineseComponents = keyword.match(/[\\u4e00-\\u9fff]+/g) || [];\n const expectedMatches = chineseComponents.length;\n\n if (pinyinMatches >= expectedMatches) {\n return {\n keyphraseLength: expectedMatches,\n percentWordMatches: 100\n };\n } else if (pinyinMatches > 0) {\n const enhancedPercent = Math.round((pinyinMatches / expectedMatches) * 100);\n return {\n keyphraseLength: expectedMatches,\n percentWordMatches: enhancedPercent >= 50 ? enhancedPercent : Math.max(enhancedPercent, 67)\n };\n }\n }\n\n // Auto-detect Chinese and use appropriate helper - specifically for URL context\n const matchHelper = getMatchHelper(\n parsedSlug,\n keyword,\n researcher.getHelper('matchWordCustomHelper'),\n true // isUrlContext = true for enhanced Chinese URL matching\n );\n\n const keyphraseInSlug = findTopicFormsInString(\n topicForms,\n parsedSlug,\n false,\n locale,\n matchHelper\n );\n\n return {\n keyphraseLength: topicForms.keyphraseForms.length,\n percentWordMatches: keyphraseInSlug.percentWordMatches\n };\n}\n\n/**\n * Matches the keyword in the slug.\n * keywordCountInUrl was the previous name for keywordCountInSlug (hence the name of this file).\n * We keep (and expose) this research for backwards compatibility.\n *\n * @deprecated Since version 18.7. Use keywordCountInSlug instead.\n *\n * @param {Paper} paper the Paper object to use in this count.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {{keyphraseLength: int, percentWordMatches: int}} The length of the keyphrase and the percentage of keyphrase forms that has been found.\n */\nfunction keywordCountInUrl(paper, researcher) {\n console.warn('This function is deprecated, use keywordCountInSlug instead.');\n return keywordCountInSlug(paper, researcher);\n}\n\nexport {keywordCountInSlug, keywordCountInUrl};\n\nexport default keywordCountInSlug;\n"],"mappings":"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","ignoreList":[]}
@@ -3,6 +3,7 @@ import Assessment from "../assessment";
3
3
  import AssessmentResult from "../../../values/AssessmentResult";
4
4
  import { KeyIcon } from '@shopify/polaris-icons';
5
5
  import { INTRODUCTION_KEYWORD_ID } from "../../../const/analysis";
6
+ import { enhanceResearcherForChinese } from "../../../languageProcessing/helpers/language/chineseHelperFactory.js";
6
7
 
7
8
  /**
8
9
  * Assessment to check whether the keyphrase or synonyms are encountered in the first paragraph of the article.
@@ -51,7 +52,10 @@ class IntroductionKeywordAssessment extends Assessment {
51
52
  const assessmentResult = new AssessmentResult({
52
53
  config: this._config
53
54
  });
54
- this._firstParagraphMatches = researcher.getResearch('findKeywordInFirstParagraph');
55
+
56
+ // Enhance researcher for Chinese if needed
57
+ const enhancedResearcher = enhanceResearcherForChinese(researcher, paper.getText(), paper.getKeyword());
58
+ this._firstParagraphMatches = enhancedResearcher.getResearch('findKeywordInFirstParagraph');
55
59
  const calculatedResult = this.calculateResult();
56
60
  assessmentResult.setScore(calculatedResult.score);
57
61
  assessmentResult.setStatus(calculatedResult.status);
@@ -1 +1 @@
1
- {"version":3,"file":"IntroductionKeywordAssessment.js","names":["merge","Assessment","AssessmentResult","KeyIcon","INTRODUCTION_KEYWORD_ID","IntroductionKeywordAssessment","constructor","config","defaultConfig","id","ctaType","priority","docUrl","fixPosition","icon","title","content","improve","bad","good","identifier","_config","getResult","paper","researcher","assessmentResult","_firstParagraphMatches","getResearch","calculatedResult","calculateResult","setScore","score","setStatus","status","isApplicable","hasKeyword","hasText","foundInOneSentence","foundInParagraph","getScore"],"sources":["../../../../src/scoring/assessments/seo/IntroductionKeywordAssessment.js"],"sourcesContent":["import {merge} from 'lodash';\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\nimport {KeyIcon} from '@shopify/polaris-icons';\nimport {INTRODUCTION_KEYWORD_ID} from '@axyseo/const/analysis';\n\n/**\n * Assessment to check whether the keyphrase or synonyms are encountered in the first paragraph of the article.\n */\nclass IntroductionKeywordAssessment extends Assessment {\n /**\n * Sets the identifier and the config.\n *\n * @param {Object} [config] The configuration to use.\n * @param {string} [config.url] The URL to the relevant article on Yoast.com.\n *\n * @returns {void}\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: INTRODUCTION_KEYWORD_ID,\n ctaType: 'fix',\n priority: 'low',\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#keyword-in-introduction',\n fixPosition: 'highlightFirstParagraph',\n icon: KeyIcon,\n title: 'Introduction Keyword',\n content: {\n improve: '',\n bad: 'No keywords found in the first sentence. Add one to improve keyword rank.',\n good: 'Keyword is included in the first sentence.'\n }\n };\n\n this.identifier = INTRODUCTION_KEYWORD_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Assesses the presence of keyphrase or synonyms in the first paragraph.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher used for calling research.\n *\n * @returns {AssessmentResult} The result of this assessment.\n */\n getResult({paper, researcher}) {\n const assessmentResult = new AssessmentResult({config: this._config});\n\n this._firstParagraphMatches = researcher.getResearch('findKeywordInFirstParagraph');\n const calculatedResult = this.calculateResult();\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n\n return assessmentResult;\n }\n\n /**\n * Checks if the paper has both keyword and text.\n *\n * @param {Paper} paper The paper to be analyzed.\n *\n * @returns {boolean} Whether the assessment is applicable or not.\n */\n isApplicable(paper) {\n return paper.hasKeyword() && paper.hasText();\n }\n\n /**\n *\n * @returns {{score: number, status: string}}\n */\n calculateResult() {\n let status = 'bad';\n\n if (\n this._firstParagraphMatches.foundInOneSentence ||\n this._firstParagraphMatches.foundInParagraph\n ) {\n status = 'good';\n } else {\n status = 'bad';\n }\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n score,\n status\n };\n }\n}\n\nexport default IntroductionKeywordAssessment;\n"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAQ;AAC5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AACvB,SAAQC,OAAO,QAAO,wBAAwB;AAC9C,SAAQC,uBAAuB;;AAE/B;AACA;AACA;AACA,MAAMC,6BAA6B,SAASJ,UAAU,CAAC;EACrD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEK,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEL,uBAAuB;MAC3BM,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,KAAK;MACfC,MAAM,EACJ,qGAAqG;MACvGC,WAAW,EAAE,yBAAyB;MACtCC,IAAI,EAAEX,OAAO;MACbY,KAAK,EAAE,sBAAsB;MAC7BC,OAAO,EAAE;QACPC,OAAO,EAAE,EAAE;QACXC,GAAG,EAAE,2EAA2E;QAChFC,IAAI,EAAE;MACR;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGhB,uBAAuB;IACzC,IAAI,CAACiB,OAAO,GAAGrB,KAAK,CAACQ,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEe,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B,MAAMC,gBAAgB,GAAG,IAAIvB,gBAAgB,CAAC;MAACK,MAAM,EAAE,IAAI,CAACc;IAAO,CAAC,CAAC;IAErE,IAAI,CAACK,sBAAsB,GAAGF,UAAU,CAACG,WAAW,CAAC,6BAA6B,CAAC;IACnF,MAAMC,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAAC,CAAC;IAE/CJ,gBAAgB,CAACK,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDN,gBAAgB,CAACO,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IAEnD,OAAOR,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACES,YAAYA,CAACX,KAAK,EAAE;IAClB,OAAOA,KAAK,CAACY,UAAU,CAAC,CAAC,IAAIZ,KAAK,CAACa,OAAO,CAAC,CAAC;EAC9C;;EAEA;AACF;AACA;AACA;EACEP,eAAeA,CAAA,EAAG;IAChB,IAAII,MAAM,GAAG,KAAK;IAElB,IACE,IAAI,CAACP,sBAAsB,CAACW,kBAAkB,IAC9C,IAAI,CAACX,sBAAsB,CAACY,gBAAgB,EAC5C;MACAL,MAAM,GAAG,MAAM;IACjB,CAAC,MAAM;MACLA,MAAM,GAAG,KAAK;IAChB;IAEA,MAAMF,KAAK,GAAG,IAAI,CAACQ,QAAQ,CAAC,IAAI,CAAClB,OAAO,CAACV,QAAQ,EAAEsB,MAAM,CAAC;IAE1D,OAAO;MACLF,KAAK;MACLE;IACF,CAAC;EACH;AACF;AAEA,eAAe5B,6BAA6B","ignoreList":[]}
1
+ {"version":3,"file":"IntroductionKeywordAssessment.js","names":["merge","Assessment","AssessmentResult","KeyIcon","INTRODUCTION_KEYWORD_ID","enhanceResearcherForChinese","IntroductionKeywordAssessment","constructor","config","defaultConfig","id","ctaType","priority","docUrl","fixPosition","icon","title","content","improve","bad","good","identifier","_config","getResult","paper","researcher","assessmentResult","enhancedResearcher","getText","getKeyword","_firstParagraphMatches","getResearch","calculatedResult","calculateResult","setScore","score","setStatus","status","isApplicable","hasKeyword","hasText","foundInOneSentence","foundInParagraph","getScore"],"sources":["../../../../src/scoring/assessments/seo/IntroductionKeywordAssessment.js"],"sourcesContent":["import {merge} from 'lodash';\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\nimport {KeyIcon} from '@shopify/polaris-icons';\nimport {INTRODUCTION_KEYWORD_ID} from '@axyseo/const/analysis';\nimport {enhanceResearcherForChinese} from '../../../languageProcessing/helpers/language/chineseHelperFactory.js';\n\n/**\n * Assessment to check whether the keyphrase or synonyms are encountered in the first paragraph of the article.\n */\nclass IntroductionKeywordAssessment extends Assessment {\n /**\n * Sets the identifier and the config.\n *\n * @param {Object} [config] The configuration to use.\n * @param {string} [config.url] The URL to the relevant article on Yoast.com.\n *\n * @returns {void}\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: INTRODUCTION_KEYWORD_ID,\n ctaType: 'fix',\n priority: 'low',\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#keyword-in-introduction',\n fixPosition: 'highlightFirstParagraph',\n icon: KeyIcon,\n title: 'Introduction Keyword',\n content: {\n improve: '',\n bad: 'No keywords found in the first sentence. Add one to improve keyword rank.',\n good: 'Keyword is included in the first sentence.'\n }\n };\n\n this.identifier = INTRODUCTION_KEYWORD_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Assesses the presence of keyphrase or synonyms in the first paragraph.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher used for calling research.\n *\n * @returns {AssessmentResult} The result of this assessment.\n */\n getResult({paper, researcher}) {\n const assessmentResult = new AssessmentResult({config: this._config});\n\n // Enhance researcher for Chinese if needed\n const enhancedResearcher = enhanceResearcherForChinese(\n researcher,\n paper.getText(),\n paper.getKeyword()\n );\n\n this._firstParagraphMatches = enhancedResearcher.getResearch('findKeywordInFirstParagraph');\n const calculatedResult = this.calculateResult();\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n\n return assessmentResult;\n }\n\n /**\n * Checks if the paper has both keyword and text.\n *\n * @param {Paper} paper The paper to be analyzed.\n *\n * @returns {boolean} Whether the assessment is applicable or not.\n */\n isApplicable(paper) {\n return paper.hasKeyword() && paper.hasText();\n }\n\n /**\n *\n * @returns {{score: number, status: string}}\n */\n calculateResult() {\n let status = 'bad';\n\n if (\n this._firstParagraphMatches.foundInOneSentence ||\n this._firstParagraphMatches.foundInParagraph\n ) {\n status = 'good';\n } else {\n status = 'bad';\n }\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n score,\n status\n };\n }\n}\n\nexport default IntroductionKeywordAssessment;\n"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAQ;AAC5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AACvB,SAAQC,OAAO,QAAO,wBAAwB;AAC9C,SAAQC,uBAAuB;AAC/B,SAAQC,2BAA2B;;AAEnC;AACA;AACA;AACA,MAAMC,6BAA6B,SAASL,UAAU,CAAC;EACrD;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEM,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEN,uBAAuB;MAC3BO,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,KAAK;MACfC,MAAM,EACJ,qGAAqG;MACvGC,WAAW,EAAE,yBAAyB;MACtCC,IAAI,EAAEZ,OAAO;MACba,KAAK,EAAE,sBAAsB;MAC7BC,OAAO,EAAE;QACPC,OAAO,EAAE,EAAE;QACXC,GAAG,EAAE,2EAA2E;QAChFC,IAAI,EAAE;MACR;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGjB,uBAAuB;IACzC,IAAI,CAACkB,OAAO,GAAGtB,KAAK,CAACS,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEe,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B,MAAMC,gBAAgB,GAAG,IAAIxB,gBAAgB,CAAC;MAACM,MAAM,EAAE,IAAI,CAACc;IAAO,CAAC,CAAC;;IAErE;IACA,MAAMK,kBAAkB,GAAGtB,2BAA2B,CACpDoB,UAAU,EACVD,KAAK,CAACI,OAAO,CAAC,CAAC,EACfJ,KAAK,CAACK,UAAU,CAAC,CACnB,CAAC;IAED,IAAI,CAACC,sBAAsB,GAAGH,kBAAkB,CAACI,WAAW,CAAC,6BAA6B,CAAC;IAC3F,MAAMC,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAAC,CAAC;IAE/CP,gBAAgB,CAACQ,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDT,gBAAgB,CAACU,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IAEnD,OAAOX,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;EACEY,YAAYA,CAACd,KAAK,EAAE;IAClB,OAAOA,KAAK,CAACe,UAAU,CAAC,CAAC,IAAIf,KAAK,CAACgB,OAAO,CAAC,CAAC;EAC9C;;EAEA;AACF;AACA;AACA;EACEP,eAAeA,CAAA,EAAG;IAChB,IAAII,MAAM,GAAG,KAAK;IAElB,IACE,IAAI,CAACP,sBAAsB,CAACW,kBAAkB,IAC9C,IAAI,CAACX,sBAAsB,CAACY,gBAAgB,EAC5C;MACAL,MAAM,GAAG,MAAM;IACjB,CAAC,MAAM;MACLA,MAAM,GAAG,KAAK;IAChB;IAEA,MAAMF,KAAK,GAAG,IAAI,CAACQ,QAAQ,CAAC,IAAI,CAACrB,OAAO,CAACV,QAAQ,EAAEyB,MAAM,CAAC;IAE1D,OAAO;MACLF,KAAK;MACLE;IACF,CAAC;EACH;AACF;AAEA,eAAe/B,6BAA6B","ignoreList":[]}
@@ -1 +1 @@
1
- {"version":3,"file":"KeywordDensityAssessment.js","names":["merge","Assessment","AssessmentResult","keyphraseLengthFactor","KEYPHRASE_DENSITY_ID","KeyphraseDensityAssessment","constructor","config","defaultConfig","id","fixPosition","docUrl","ctaType","priority","title","content","good","bad","improve","identifier","_config","getResult","paper","researcher","_keyphraseCount","getResearch","keyphraseLength","_keyphraseDensity","assessmentResult","calculatedResult","calculateResult","setScore","score","setStatus","status","setTitle","density","roundedDensity","parseFloat","toFixed","getScore","isApplicable","hasText","hasKeyword"],"sources":["../../../../src/scoring/assessments/seo/KeywordDensityAssessment.js"],"sourcesContent":["import {merge} from 'lodash';\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\n\nimport keyphraseLengthFactor from '../../../scoring/helpers/assessments/keyphraseLengthFactor';\nimport {KEYPHRASE_DENSITY_ID} from '@axyseo/const/analysis';\n\n/**\n * Represents the assessment that will look if the keyphrase density is within the recommended range.\n */\nclass KeyphraseDensityAssessment extends Assessment {\n /**\n *\n * @param config\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: KEYPHRASE_DENSITY_ID,\n fixPosition: 'highlightKeyword',\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#main-keyword-density',\n ctaType: 'fix',\n priority: 'high',\n title: 'Main keyword density',\n content: {\n good: 'Keyword density is optimized, between 1 - 1.5%.',\n bad: 'Keep keyword density from 1% - 1.5%.',\n improve: ''\n }\n };\n\n this.identifier = KEYPHRASE_DENSITY_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Runs the keyphrase density module, based on this returns an assessment\n * result with score.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher used for calling the research.\n *\n * @returns {AssessmentResult} The result of the assessment.\n */\n getResult({paper, researcher}) {\n this._keyphraseCount = researcher.getResearch('getKeyphraseCount');\n const keyphraseLength = this._keyphraseCount.keyphraseLength;\n\n this._keyphraseDensity = researcher.getResearch('getKeyphraseDensity');\n\n this._keyphraseDensity = this._keyphraseDensity * keyphraseLengthFactor(keyphraseLength);\n const assessmentResult = new AssessmentResult({config: this._config});\n\n const calculatedResult = this.calculateResult(paper);\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n assessmentResult.setTitle(calculatedResult.title);\n return assessmentResult;\n }\n\n /**\n *\n * @returns {{title: string, score: number, status: string}}\n */\n calculateResult() {\n const density = this._keyphraseDensity;\n const roundedDensity = parseFloat(density.toFixed(2));\n\n let status = '';\n if (roundedDensity >= 1 && roundedDensity <= 1.5) {\n status = 'good';\n } else {\n status = 'bad';\n }\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n title: (this._config.title || 'Main keyword density') + ': ' + roundedDensity + '%',\n score,\n status\n };\n }\n\n /**\n * Checks whether the paper has a text of the minimum required length and a keyphrase is set. Language-specific length requirements and methods\n * 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\n * required length higher).\n *\n * @param {Paper} \t\tpaper \t\tThe paper to use for the assessment.\n * @param {Researcher} researcher The paper to use for the assessment.\n *\n * @returns {boolean} True if applicable.\n */\n isApplicable(paper, researcher) {\n return paper.hasText() && paper.hasKeyword();\n }\n}\n\nexport default KeyphraseDensityAssessment;\n"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAQ;AAC5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AAEvB,OAAOC,qBAAqB;AAC5B,SAAQC,oBAAoB;;AAE5B;AACA;AACA;AACA,MAAMC,0BAA0B,SAASJ,UAAU,CAAC;EAClD;AACF;AACA;AACA;EACEK,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEL,oBAAoB;MACxBM,WAAW,EAAE,kBAAkB;MAC/BC,MAAM,EACJ,kGAAkG;MACpGC,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,MAAM;MAChBC,KAAK,EAAE,sBAAsB;MAC7BC,OAAO,EAAE;QACPC,IAAI,EAAE,iDAAiD;QACvDC,GAAG,EAAE,sCAAsC;QAC3CC,OAAO,EAAE;MACX;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGf,oBAAoB;IACtC,IAAI,CAACgB,OAAO,GAAGpB,KAAK,CAACQ,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEc,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B,IAAI,CAACC,eAAe,GAAGD,UAAU,CAACE,WAAW,CAAC,mBAAmB,CAAC;IAClE,MAAMC,eAAe,GAAG,IAAI,CAACF,eAAe,CAACE,eAAe;IAE5D,IAAI,CAACC,iBAAiB,GAAGJ,UAAU,CAACE,WAAW,CAAC,qBAAqB,CAAC;IAEtE,IAAI,CAACE,iBAAiB,GAAG,IAAI,CAACA,iBAAiB,GAAGxB,qBAAqB,CAACuB,eAAe,CAAC;IACxF,MAAME,gBAAgB,GAAG,IAAI1B,gBAAgB,CAAC;MAACK,MAAM,EAAE,IAAI,CAACa;IAAO,CAAC,CAAC;IAErE,MAAMS,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAACR,KAAK,CAAC;IAEpDM,gBAAgB,CAACG,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDJ,gBAAgB,CAACK,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IACnDN,gBAAgB,CAACO,QAAQ,CAACN,gBAAgB,CAACf,KAAK,CAAC;IACjD,OAAOc,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;EACEE,eAAeA,CAAA,EAAG;IAChB,MAAMM,OAAO,GAAG,IAAI,CAACT,iBAAiB;IACtC,MAAMU,cAAc,GAAGC,UAAU,CAACF,OAAO,CAACG,OAAO,CAAC,CAAC,CAAC,CAAC;IAErD,IAAIL,MAAM,GAAG,EAAE;IACf,IAAIG,cAAc,IAAI,CAAC,IAAIA,cAAc,IAAI,GAAG,EAAE;MAChDH,MAAM,GAAG,MAAM;IACjB,CAAC,MAAM;MACLA,MAAM,GAAG,KAAK;IAChB;IAEA,MAAMF,KAAK,GAAG,IAAI,CAACQ,QAAQ,CAAC,IAAI,CAACpB,OAAO,CAACP,QAAQ,EAAEqB,MAAM,CAAC;IAE1D,OAAO;MACLpB,KAAK,EAAE,CAAC,IAAI,CAACM,OAAO,CAACN,KAAK,IAAI,sBAAsB,IAAI,IAAI,GAAGuB,cAAc,GAAG,GAAG;MACnFL,KAAK;MACLE;IACF,CAAC;EACH;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,YAAYA,CAACnB,KAAK,EAAEC,UAAU,EAAE;IAC9B,OAAOD,KAAK,CAACoB,OAAO,CAAC,CAAC,IAAIpB,KAAK,CAACqB,UAAU,CAAC,CAAC;EAC9C;AACF;AAEA,eAAetC,0BAA0B","ignoreList":[]}
1
+ {"version":3,"file":"KeywordDensityAssessment.js","names":["merge","Assessment","AssessmentResult","keyphraseLengthFactor","KEYPHRASE_DENSITY_ID","KeyphraseDensityAssessment","constructor","config","defaultConfig","id","fixPosition","docUrl","ctaType","priority","title","content","good","bad","improve","identifier","_config","getResult","paper","researcher","_keyphraseCount","getResearch","keyphraseLength","_keyphraseDensity","assessmentResult","calculatedResult","calculateResult","setScore","score","setStatus","status","setTitle","density","roundedDensity","parseFloat","toFixed","getScore","isApplicable","hasText","hasKeyword"],"sources":["../../../../src/scoring/assessments/seo/KeywordDensityAssessment.js"],"sourcesContent":["import {merge} from 'lodash';\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\n\nimport keyphraseLengthFactor from '../../../scoring/helpers/assessments/keyphraseLengthFactor';\nimport {KEYPHRASE_DENSITY_ID} from '@axyseo/const/analysis';\n\n/**\n * Represents the assessment that will look if the keyphrase density is within the recommended range.\n */\nclass KeyphraseDensityAssessment extends Assessment {\n /**\n *\n * @param config\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: KEYPHRASE_DENSITY_ID,\n fixPosition: 'highlightKeyword',\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#main-keyword-density',\n ctaType: 'fix',\n priority: 'high',\n title: 'Main keyword density',\n content: {\n good: 'Keyword density is optimized, between 1 - 1.5%.',\n bad: 'Keep keyword density from 1% - 1.5%.',\n improve: ''\n }\n };\n\n this.identifier = KEYPHRASE_DENSITY_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Runs the keyphrase density module, based on this returns an assessment\n * result with score.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher used for calling the research.\n *\n * @returns {AssessmentResult} The result of the assessment.\n */\n getResult({paper, researcher}) {\n this._keyphraseCount = researcher.getResearch('getKeyphraseCount');\n const keyphraseLength = this._keyphraseCount.keyphraseLength;\n\n this._keyphraseDensity = researcher.getResearch('getKeyphraseDensity');\n\n this._keyphraseDensity = this._keyphraseDensity * keyphraseLengthFactor(keyphraseLength);\n\n const assessmentResult = new AssessmentResult({config: this._config});\n\n const calculatedResult = this.calculateResult(paper);\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n assessmentResult.setTitle(calculatedResult.title);\n return assessmentResult;\n }\n\n /**\n *\n * @returns {{title: string, score: number, status: string}}\n */\n calculateResult() {\n const density = this._keyphraseDensity;\n const roundedDensity = parseFloat(density.toFixed(2));\n\n let status = '';\n if (roundedDensity >= 1 && roundedDensity <= 1.5) {\n status = 'good';\n } else {\n status = 'bad';\n }\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n title: (this._config.title || 'Main keyword density') + ': ' + roundedDensity + '%',\n score,\n status\n };\n }\n\n /**\n * Checks whether the paper has a text of the minimum required length and a keyphrase is set. Language-specific length requirements and methods\n * 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\n * required length higher).\n *\n * @param {Paper} \t\tpaper \t\tThe paper to use for the assessment.\n * @param {Researcher} researcher The paper to use for the assessment.\n *\n * @returns {boolean} True if applicable.\n */\n isApplicable(paper, researcher) {\n return paper.hasText() && paper.hasKeyword();\n }\n}\n\nexport default KeyphraseDensityAssessment;\n"],"mappings":"AAAA,SAAQA,KAAK,QAAO,QAAQ;AAC5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AAEvB,OAAOC,qBAAqB;AAC5B,SAAQC,oBAAoB;;AAE5B;AACA;AACA;AACA,MAAMC,0BAA0B,SAASJ,UAAU,CAAC;EAClD;AACF;AACA;AACA;EACEK,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEL,oBAAoB;MACxBM,WAAW,EAAE,kBAAkB;MAC/BC,MAAM,EACJ,kGAAkG;MACpGC,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,MAAM;MAChBC,KAAK,EAAE,sBAAsB;MAC7BC,OAAO,EAAE;QACPC,IAAI,EAAE,iDAAiD;QACvDC,GAAG,EAAE,sCAAsC;QAC3CC,OAAO,EAAE;MACX;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGf,oBAAoB;IACtC,IAAI,CAACgB,OAAO,GAAGpB,KAAK,CAACQ,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEc,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B,IAAI,CAACC,eAAe,GAAGD,UAAU,CAACE,WAAW,CAAC,mBAAmB,CAAC;IAClE,MAAMC,eAAe,GAAG,IAAI,CAACF,eAAe,CAACE,eAAe;IAE5D,IAAI,CAACC,iBAAiB,GAAGJ,UAAU,CAACE,WAAW,CAAC,qBAAqB,CAAC;IAEtE,IAAI,CAACE,iBAAiB,GAAG,IAAI,CAACA,iBAAiB,GAAGxB,qBAAqB,CAACuB,eAAe,CAAC;IAExF,MAAME,gBAAgB,GAAG,IAAI1B,gBAAgB,CAAC;MAACK,MAAM,EAAE,IAAI,CAACa;IAAO,CAAC,CAAC;IAErE,MAAMS,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAACR,KAAK,CAAC;IAEpDM,gBAAgB,CAACG,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDJ,gBAAgB,CAACK,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IACnDN,gBAAgB,CAACO,QAAQ,CAACN,gBAAgB,CAACf,KAAK,CAAC;IACjD,OAAOc,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;EACEE,eAAeA,CAAA,EAAG;IAChB,MAAMM,OAAO,GAAG,IAAI,CAACT,iBAAiB;IACtC,MAAMU,cAAc,GAAGC,UAAU,CAACF,OAAO,CAACG,OAAO,CAAC,CAAC,CAAC,CAAC;IAErD,IAAIL,MAAM,GAAG,EAAE;IACf,IAAIG,cAAc,IAAI,CAAC,IAAIA,cAAc,IAAI,GAAG,EAAE;MAChDH,MAAM,GAAG,MAAM;IACjB,CAAC,MAAM;MACLA,MAAM,GAAG,KAAK;IAChB;IAEA,MAAMF,KAAK,GAAG,IAAI,CAACQ,QAAQ,CAAC,IAAI,CAACpB,OAAO,CAACP,QAAQ,EAAEqB,MAAM,CAAC;IAE1D,OAAO;MACLpB,KAAK,EAAE,CAAC,IAAI,CAACM,OAAO,CAACN,KAAK,IAAI,sBAAsB,IAAI,IAAI,GAAGuB,cAAc,GAAG,GAAG;MACnFL,KAAK;MACLE;IACF,CAAC;EACH;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,YAAYA,CAACnB,KAAK,EAAEC,UAAU,EAAE;IAC9B,OAAOD,KAAK,CAACoB,OAAO,CAAC,CAAC,IAAIpB,KAAK,CAACqB,UAAU,CAAC,CAAC;EAC9C;AACF;AAEA,eAAetC,0BAA0B","ignoreList":[]}
@@ -3,6 +3,7 @@ import { merge } from 'lodash';
3
3
  import Assessment from "../assessment";
4
4
  import AssessmentResult from "../../../values/AssessmentResult";
5
5
  import { SLUG_KEYWORD_ID } from "../../../const/analysis";
6
+ import { enhanceResearcherForChinese } from "../../../languageProcessing/helpers/language/chineseHelperFactory.js";
6
7
 
7
8
  /**
8
9
  * Represents the Slug keyword assessment. This assessment checks if the keyword is present in the slug.
@@ -45,7 +46,10 @@ class SlugKeywordAssessment extends Assessment {
45
46
  paper,
46
47
  researcher
47
48
  }) {
48
- this._keywordInSlug = researcher.getResearch('keywordCountInSlug');
49
+ // Enhance researcher for Chinese if needed - specifically for URL context
50
+ const enhancedResearcher = enhanceResearcherForChinese(researcher, paper.getSlug(), paper.getKeyword(), true // isUrlContext = true for enhanced Chinese URL matching
51
+ );
52
+ this._keywordInSlug = enhancedResearcher.getResearch('keywordCountInSlug');
49
53
  const assessmentResult = new AssessmentResult({
50
54
  config: this._config
51
55
  });
@@ -1 +1 @@
1
- {"version":3,"file":"UrlKeywordAssessment.js","names":["Text","merge","Assessment","AssessmentResult","SLUG_KEYWORD_ID","SlugKeywordAssessment","constructor","config","defaultConfig","id","docUrl","fixPosition","ctaType","priority","title","content","good","improve","bad","identifier","_config","getResult","paper","researcher","_keywordInSlug","getResearch","assessmentResult","calculatedResult","calculateResult","setScore","score","setStatus","status","isApplicable","isFrontPage","hasKeyword","hasSlug","hasResearch","percentWordMatches","getScore"],"sources":["../../../../src/scoring/assessments/seo/UrlKeywordAssessment.js"],"sourcesContent":["import {Text} from '@shopify/polaris';\nimport {merge} from 'lodash';\n\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\nimport {SLUG_KEYWORD_ID} from '@axyseo/const/analysis';\n\n/**\n * Represents the Slug keyword assessment. This assessment checks if the keyword is present in the slug.\n */\nclass SlugKeywordAssessment extends Assessment {\n /**\n * Sets the identifier and the config.\n *\n * @param {Object} config The configuration to use.\n * @returns {void}\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: SLUG_KEYWORD_ID,\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#keyword-in-url',\n fixPosition: 'url handle',\n ctaType: 'fix',\n priority: 'medium',\n title: 'Keyword in URL',\n content: {\n good: 'Keyword is included in URL.',\n improve: '',\n bad: 'Missing keyword in URL. Include it for relevance and better SEO.'\n }\n };\n\n this.identifier = SLUG_KEYWORD_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Executes the Assessment and returns a result.\n *\n * @param {Paper} paper The Paper object to assess.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {AssessmentResult} The result of the assessment, containing both a score and a descriptive text.\n */\n getResult({paper, researcher}) {\n this._keywordInSlug = researcher.getResearch('keywordCountInSlug');\n\n const assessmentResult = new AssessmentResult({config: this._config});\n\n const calculatedResult = this.calculateResult();\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n return assessmentResult;\n }\n\n /**\n * Checks whether the paper has a keyword and a slug.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher object.\n *\n * @returns {boolean} True if the paper contains a keyword and a slug, and if the keywordCountInSlug research is available on the researcher.\n */\n isApplicable(paper, researcher) {\n return (\n !paper.isFrontPage() &&\n paper.hasKeyword() &&\n paper.hasSlug() &&\n researcher.hasResearch('keywordCountInSlug')\n );\n }\n\n /**\n *\n * @returns {{score: number, status: (string)}}\n */\n calculateResult() {\n const status = (() => {\n return this._keywordInSlug.percentWordMatches >= 50 ? 'good' : 'bad';\n })();\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n score,\n status\n };\n }\n}\n\nexport default SlugKeywordAssessment;\n"],"mappings":"AAAA,SAAQA,IAAI,QAAO,kBAAkB;AACrC,SAAQC,KAAK,QAAO,QAAQ;AAE5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AACvB,SAAQC,eAAe;;AAEvB;AACA;AACA;AACA,MAAMC,qBAAqB,SAASH,UAAU,CAAC;EAC7C;AACF;AACA;AACA;AACA;AACA;EACEI,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEL,eAAe;MACnBM,MAAM,EACJ,4FAA4F;MAC9FC,WAAW,EAAE,YAAY;MACzBC,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,QAAQ;MAClBC,KAAK,EAAE,gBAAgB;MACvBC,OAAO,EAAE;QACPC,IAAI,EAAE,6BAA6B;QACnCC,OAAO,EAAE,EAAE;QACXC,GAAG,EAAE;MACP;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGf,eAAe;IACjC,IAAI,CAACgB,OAAO,GAAGnB,KAAK,CAACO,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEc,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B,IAAI,CAACC,cAAc,GAAGD,UAAU,CAACE,WAAW,CAAC,oBAAoB,CAAC;IAElE,MAAMC,gBAAgB,GAAG,IAAIvB,gBAAgB,CAAC;MAACI,MAAM,EAAE,IAAI,CAACa;IAAO,CAAC,CAAC;IAErE,MAAMO,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAAC,CAAC;IAE/CF,gBAAgB,CAACG,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDJ,gBAAgB,CAACK,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IACnD,OAAON,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,YAAYA,CAACX,KAAK,EAAEC,UAAU,EAAE;IAC9B,OACE,CAACD,KAAK,CAACY,WAAW,CAAC,CAAC,IACpBZ,KAAK,CAACa,UAAU,CAAC,CAAC,IAClBb,KAAK,CAACc,OAAO,CAAC,CAAC,IACfb,UAAU,CAACc,WAAW,CAAC,oBAAoB,CAAC;EAEhD;;EAEA;AACF;AACA;AACA;EACET,eAAeA,CAAA,EAAG;IAChB,MAAMI,MAAM,GAAG,CAAC,MAAM;MACpB,OAAO,IAAI,CAACR,cAAc,CAACc,kBAAkB,IAAI,EAAE,GAAG,MAAM,GAAG,KAAK;IACtE,CAAC,EAAE,CAAC;IAEJ,MAAMR,KAAK,GAAG,IAAI,CAACS,QAAQ,CAAC,IAAI,CAACnB,OAAO,CAACP,QAAQ,EAAEmB,MAAM,CAAC;IAE1D,OAAO;MACLF,KAAK;MACLE;IACF,CAAC;EACH;AACF;AAEA,eAAe3B,qBAAqB","ignoreList":[]}
1
+ {"version":3,"file":"UrlKeywordAssessment.js","names":["Text","merge","Assessment","AssessmentResult","SLUG_KEYWORD_ID","enhanceResearcherForChinese","SlugKeywordAssessment","constructor","config","defaultConfig","id","docUrl","fixPosition","ctaType","priority","title","content","good","improve","bad","identifier","_config","getResult","paper","researcher","enhancedResearcher","getSlug","getKeyword","_keywordInSlug","getResearch","assessmentResult","calculatedResult","calculateResult","setScore","score","setStatus","status","isApplicable","isFrontPage","hasKeyword","hasSlug","hasResearch","percentWordMatches","getScore"],"sources":["../../../../src/scoring/assessments/seo/UrlKeywordAssessment.js"],"sourcesContent":["import {Text} from '@shopify/polaris';\nimport {merge} from 'lodash';\n\nimport Assessment from '../assessment';\nimport AssessmentResult from '../../../values/AssessmentResult';\nimport {SLUG_KEYWORD_ID} from '@axyseo/const/analysis';\nimport {enhanceResearcherForChinese} from '../../../languageProcessing/helpers/language/chineseHelperFactory.js';\n\n/**\n * Represents the Slug keyword assessment. This assessment checks if the keyword is present in the slug.\n */\nclass SlugKeywordAssessment extends Assessment {\n /**\n * Sets the identifier and the config.\n *\n * @param {Object} config The configuration to use.\n * @returns {void}\n */\n constructor(config = {}) {\n super();\n\n const defaultConfig = {\n id: SLUG_KEYWORD_ID,\n docUrl:\n 'https://docs.avada.io/seo-suite-help-center/seo-audit/on-page-seo/checklist#keyword-in-url',\n fixPosition: 'url handle',\n ctaType: 'fix',\n priority: 'medium',\n title: 'Keyword in URL',\n content: {\n good: 'Keyword is included in URL.',\n improve: '',\n bad: 'Missing keyword in URL. Include it for relevance and better SEO.'\n }\n };\n\n this.identifier = SLUG_KEYWORD_ID;\n this._config = merge(defaultConfig, config);\n }\n\n /**\n * Executes the Assessment and returns a result.\n *\n * @param {Paper} paper The Paper object to assess.\n * @param {Researcher} researcher The Researcher object containing all available researches.\n *\n * @returns {AssessmentResult} The result of the assessment, containing both a score and a descriptive text.\n */\n getResult({paper, researcher}) {\n // Enhance researcher for Chinese if needed - specifically for URL context\n const enhancedResearcher = enhanceResearcherForChinese(\n researcher,\n paper.getSlug(),\n paper.getKeyword(),\n true // isUrlContext = true for enhanced Chinese URL matching\n );\n\n this._keywordInSlug = enhancedResearcher.getResearch('keywordCountInSlug');\n\n const assessmentResult = new AssessmentResult({config: this._config});\n\n const calculatedResult = this.calculateResult();\n\n assessmentResult.setScore(calculatedResult.score);\n assessmentResult.setStatus(calculatedResult.status);\n return assessmentResult;\n }\n\n /**\n * Checks whether the paper has a keyword and a slug.\n *\n * @param {Paper} paper The paper to use for the assessment.\n * @param {Researcher} researcher The researcher object.\n *\n * @returns {boolean} True if the paper contains a keyword and a slug, and if the keywordCountInSlug research is available on the researcher.\n */\n isApplicable(paper, researcher) {\n return (\n !paper.isFrontPage() &&\n paper.hasKeyword() &&\n paper.hasSlug() &&\n researcher.hasResearch('keywordCountInSlug')\n );\n }\n\n /**\n *\n * @returns {{score: number, status: (string)}}\n */\n calculateResult() {\n const status = (() => {\n return this._keywordInSlug.percentWordMatches >= 50 ? 'good' : 'bad';\n })();\n\n const score = this.getScore(this._config.priority, status);\n\n return {\n score,\n status\n };\n }\n}\n\nexport default SlugKeywordAssessment;\n"],"mappings":"AAAA,SAAQA,IAAI,QAAO,kBAAkB;AACrC,SAAQC,KAAK,QAAO,QAAQ;AAE5B,OAAOC,UAAU;AACjB,OAAOC,gBAAgB;AACvB,SAAQC,eAAe;AACvB,SAAQC,2BAA2B;;AAEnC;AACA;AACA;AACA,MAAMC,qBAAqB,SAASJ,UAAU,CAAC;EAC7C;AACF;AACA;AACA;AACA;AACA;EACEK,WAAWA,CAACC,MAAM,GAAG,CAAC,CAAC,EAAE;IACvB,KAAK,CAAC,CAAC;IAEP,MAAMC,aAAa,GAAG;MACpBC,EAAE,EAAEN,eAAe;MACnBO,MAAM,EACJ,4FAA4F;MAC9FC,WAAW,EAAE,YAAY;MACzBC,OAAO,EAAE,KAAK;MACdC,QAAQ,EAAE,QAAQ;MAClBC,KAAK,EAAE,gBAAgB;MACvBC,OAAO,EAAE;QACPC,IAAI,EAAE,6BAA6B;QACnCC,OAAO,EAAE,EAAE;QACXC,GAAG,EAAE;MACP;IACF,CAAC;IAED,IAAI,CAACC,UAAU,GAAGhB,eAAe;IACjC,IAAI,CAACiB,OAAO,GAAGpB,KAAK,CAACQ,aAAa,EAAED,MAAM,CAAC;EAC7C;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEc,SAASA,CAAC;IAACC,KAAK;IAAEC;EAAU,CAAC,EAAE;IAC7B;IACA,MAAMC,kBAAkB,GAAGpB,2BAA2B,CACpDmB,UAAU,EACVD,KAAK,CAACG,OAAO,CAAC,CAAC,EACfH,KAAK,CAACI,UAAU,CAAC,CAAC,EAClB,IAAI,CAAC;IACP,CAAC;IAED,IAAI,CAACC,cAAc,GAAGH,kBAAkB,CAACI,WAAW,CAAC,oBAAoB,CAAC;IAE1E,MAAMC,gBAAgB,GAAG,IAAI3B,gBAAgB,CAAC;MAACK,MAAM,EAAE,IAAI,CAACa;IAAO,CAAC,CAAC;IAErE,MAAMU,gBAAgB,GAAG,IAAI,CAACC,eAAe,CAAC,CAAC;IAE/CF,gBAAgB,CAACG,QAAQ,CAACF,gBAAgB,CAACG,KAAK,CAAC;IACjDJ,gBAAgB,CAACK,SAAS,CAACJ,gBAAgB,CAACK,MAAM,CAAC;IACnD,OAAON,gBAAgB;EACzB;;EAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;EACEO,YAAYA,CAACd,KAAK,EAAEC,UAAU,EAAE;IAC9B,OACE,CAACD,KAAK,CAACe,WAAW,CAAC,CAAC,IACpBf,KAAK,CAACgB,UAAU,CAAC,CAAC,IAClBhB,KAAK,CAACiB,OAAO,CAAC,CAAC,IACfhB,UAAU,CAACiB,WAAW,CAAC,oBAAoB,CAAC;EAEhD;;EAEA;AACF;AACA;AACA;EACET,eAAeA,CAAA,EAAG;IAChB,MAAMI,MAAM,GAAG,CAAC,MAAM;MACpB,OAAO,IAAI,CAACR,cAAc,CAACc,kBAAkB,IAAI,EAAE,GAAG,MAAM,GAAG,KAAK;IACtE,CAAC,EAAE,CAAC;IAEJ,MAAMR,KAAK,GAAG,IAAI,CAACS,QAAQ,CAAC,IAAI,CAACtB,OAAO,CAACP,QAAQ,EAAEsB,MAAM,CAAC;IAE1D,OAAO;MACLF,KAAK;MACLE;IACF,CAAC;EACH;AACF;AAEA,eAAe9B,qBAAqB","ignoreList":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "axyseo",
3
- "version": "2.1.9",
3
+ "version": "2.1.11",
4
4
  "main": "build/index.js",
5
5
  "scripts": {
6
6
  "prepublishOnly": "npm run build ",