@lynxflow/seo-engine 1.8.1 → 1.8.3
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.
- package/connectors/wordpress/lynxseo-admin-app.js +25 -0
- package/connectors/wordpress/lynxseo-connector.php +24 -0
- package/connectors/wordpress/lynxseo-connector.zip +0 -0
- package/dist/extended-schemas.d.ts +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.js +747 -177
- package/dist/index.mjs +360 -7
- package/dist/power-words-psychology.d.ts +35 -0
- package/package.json +1 -1
- package/src/extended-schemas.ts +8 -4
- package/src/index.ts +1 -0
- package/src/og-image-generator.ts +5 -5
- package/src/power-words-psychology.ts +369 -0
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 🧠 Universal Open Multilingual Power Words & Click Psychology Engine
|
|
3
|
+
* Supports 50+ World Languages (FR, EN, ES, DE, IT, NL, PT, AR, RU, ZH, JA, HI, PL, SV, TR, KO, etc.)
|
|
4
|
+
* With Dynamic Runtime Dictionary Registration for Infinite Language Extensibility.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export type LanguageCode = string;
|
|
8
|
+
|
|
9
|
+
export interface CategoryWordBank {
|
|
10
|
+
authority: string[];
|
|
11
|
+
urgency: string[];
|
|
12
|
+
value: string[];
|
|
13
|
+
curiosity: string[];
|
|
14
|
+
sentimentPositive: string[];
|
|
15
|
+
sentimentNegative: string[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface PsychologyAnalysisResult {
|
|
19
|
+
score: number; // 0 to 100
|
|
20
|
+
powerWordsDetected: string[];
|
|
21
|
+
sentiment: "positif" | "negatif" | "neutre";
|
|
22
|
+
hasNumber: boolean;
|
|
23
|
+
hasYear: boolean;
|
|
24
|
+
hasCuriosityTrigger: boolean;
|
|
25
|
+
hasUrgencyTrigger: boolean;
|
|
26
|
+
hasAuthorityTrigger: boolean;
|
|
27
|
+
hasCurrencyOrDiscount: boolean;
|
|
28
|
+
hasBracketsOrParentheses: boolean;
|
|
29
|
+
recommendations: string[];
|
|
30
|
+
suggestedTitles: string[];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// Pre-loaded high-converting dictionaries across global languages
|
|
34
|
+
export const MULTILINGUAL_POWER_WORDS: Record<string, CategoryWordBank> = {
|
|
35
|
+
fr: {
|
|
36
|
+
authority: [
|
|
37
|
+
"officiel", "guide", "expert", "complet", "certifié", "ultime", "meilleur", "meilleurs", "comparatif",
|
|
38
|
+
"maîtriser", "spécialiste", "leader", "professionnel", "authentique", "infaillible", "reconnu", "démontré", "avis"
|
|
39
|
+
],
|
|
40
|
+
urgency: [
|
|
41
|
+
"rapide", "instantané", "maintenant", "immédiat", "express", "2026", "dernière minute", "urgent",
|
|
42
|
+
"dépêchez-vous", "en 5 min", "en 10 min", "aujourd'hui", "débloquer", "vite"
|
|
43
|
+
],
|
|
44
|
+
value: [
|
|
45
|
+
"gratuit", "économique", "rentable", "offert", "sans engagement", "garanti", "facile", "clé en main",
|
|
46
|
+
"rentabilité", "gain", "économisez", "inclus", "accessible", "inédit", "remise", "promo"
|
|
47
|
+
],
|
|
48
|
+
curiosity: [
|
|
49
|
+
"exclusif", "secret", "astuces", "révélé", "inédit", "top", "méthode", "pourquoi", "comment",
|
|
50
|
+
"découvrir", "choc", "incontournable", "mystère", "incroyable", "vérité"
|
|
51
|
+
],
|
|
52
|
+
sentimentPositive: [
|
|
53
|
+
"parfait", "incroyable", "magnifique", "puissant", "réussi", "génial", "exceptionnel", "facile",
|
|
54
|
+
"meilleur", "succès", "brillant", "splendide", "radieux", "magique", "sublime"
|
|
55
|
+
],
|
|
56
|
+
sentimentNegative: [
|
|
57
|
+
"erreur", "piège", "danger", "éviter", "pire", "arnaque", "catastrophe", "risqué", "menace", "perte", "alerte"
|
|
58
|
+
]
|
|
59
|
+
},
|
|
60
|
+
en: {
|
|
61
|
+
authority: [
|
|
62
|
+
"ultimate", "complete", "definitive", "guide", "expert", "official", "proven", "master", "certified",
|
|
63
|
+
"best", "top", "authority", "case study", "benchmark", "professional", "review", "formula"
|
|
64
|
+
],
|
|
65
|
+
urgency: [
|
|
66
|
+
"instant", "fast", "quick", "now", "today", "2026", "deadline", "last chance", "step-by-step",
|
|
67
|
+
"in 5 minutes", "immediate", "urgent", "unlock", "rapid"
|
|
68
|
+
],
|
|
69
|
+
value: [
|
|
70
|
+
"free", "affordable", "save", "guaranteed", "easy", "bonus", "discount", "effortless",
|
|
71
|
+
"lifetime", "roi", "value", "exclusive deal", "profitable"
|
|
72
|
+
],
|
|
73
|
+
curiosity: [
|
|
74
|
+
"secret", "revealed", "insider", "hacks", "why", "how to", "untold", "surprising",
|
|
75
|
+
"shocking", "hidden", "must-know", "unbelievable", "myth"
|
|
76
|
+
],
|
|
77
|
+
sentimentPositive: [
|
|
78
|
+
"amazing", "brilliant", "powerful", "epic", "perfect", "stunning", "thriving", "seamless",
|
|
79
|
+
"exceptional", "masterpiece", "unbeatable"
|
|
80
|
+
],
|
|
81
|
+
sentimentNegative: [
|
|
82
|
+
"mistake", "warning", "fail", "avoid", "risk", "trap", "worst", "scam", "danger", "flaw"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
es: {
|
|
86
|
+
authority: [
|
|
87
|
+
"definitiva", "completa", "guía", "experto", "oficial", "certificado", "mejor", "mejores", "comparativa",
|
|
88
|
+
"profesional", "comprobado", "líder", "análisis"
|
|
89
|
+
],
|
|
90
|
+
urgency: [
|
|
91
|
+
"rápido", "ahora", "urgente", "hoy", "2026", "en 5 minutos", "inmediato", "última oportunidad", "fácil"
|
|
92
|
+
],
|
|
93
|
+
value: [
|
|
94
|
+
"gratis", "económico", "ahorra", "garantizado", "rentable", "oferta", "descuento", "exclusivo"
|
|
95
|
+
],
|
|
96
|
+
curiosity: [
|
|
97
|
+
"secreto", "revelado", "trucos", "por qué", "cómo", "impactante", "increíble", "descubre", "método"
|
|
98
|
+
],
|
|
99
|
+
sentimentPositive: [
|
|
100
|
+
"excelente", "increíble", "perfecto", "potente", "maravilloso", "éxito", "fantástico"
|
|
101
|
+
],
|
|
102
|
+
sentimentNegative: [
|
|
103
|
+
"error", "peligro", "evita", "peor", "estafa", "trampa", "fracaso", "riesgo"
|
|
104
|
+
]
|
|
105
|
+
},
|
|
106
|
+
de: {
|
|
107
|
+
authority: [
|
|
108
|
+
"ultimativ", "vollständig", "leitfaden", "experte", "offiziell", "zertifiziert", "beste", "bester", "vergleich", "profi"
|
|
109
|
+
],
|
|
110
|
+
urgency: [
|
|
111
|
+
"schnell", "jetzt", "sofort", "2026", "in 5 minuten", "heute", "dringend", "einfach"
|
|
112
|
+
],
|
|
113
|
+
value: [
|
|
114
|
+
"kostenlos", "gratis", "sparen", "garantiert", "rabatt", "günstig", "angebot", "profitabel"
|
|
115
|
+
],
|
|
116
|
+
curiosity: [
|
|
117
|
+
"geheimnis", "enthüllt", "tricks", "tipps", "warum", "wie man", "unglaublich", "entdecken"
|
|
118
|
+
],
|
|
119
|
+
sentimentPositive: [
|
|
120
|
+
"hervorragend", "perfekt", "genial", "erfolg", "fantastisch", "wunderbar", "stark"
|
|
121
|
+
],
|
|
122
|
+
sentimentNegative: [
|
|
123
|
+
"fehler", "gefahr", "vermeiden", "schlimmste", "betrug", "risiko", "warnung"
|
|
124
|
+
]
|
|
125
|
+
},
|
|
126
|
+
it: {
|
|
127
|
+
authority: [
|
|
128
|
+
"guida", "completa", "esperto", "ufficiale", "certificato", "migliore", "migliori", "confronto", "definitiva", "professionale"
|
|
129
|
+
],
|
|
130
|
+
urgency: [
|
|
131
|
+
"veloce", "ora", "subito", "immediato", "2026", "in 5 minuti", "oggi", "urgente"
|
|
132
|
+
],
|
|
133
|
+
value: [
|
|
134
|
+
"gratis", "gratuito", "risparmia", "garantito", "sconto", "facile", "offerta", "conveniente"
|
|
135
|
+
],
|
|
136
|
+
curiosity: [
|
|
137
|
+
"segreto", "svelato", "trucchi", "perché", "come fare", "incredibile", "scopri", "metodo"
|
|
138
|
+
],
|
|
139
|
+
sentimentPositive: [
|
|
140
|
+
"perfetto", "incredibile", "fantastico", "successo", "eccezionale", "meraviglioso"
|
|
141
|
+
],
|
|
142
|
+
sentimentNegative: [
|
|
143
|
+
"errore", "pericolo", "evitare", "peggiore", "truffa", "rischio", "trappola"
|
|
144
|
+
]
|
|
145
|
+
},
|
|
146
|
+
nl: {
|
|
147
|
+
authority: ["ultiem", "gids", "expert", "officieel", "beste", "vergelijk", "compleet", "bewezen"],
|
|
148
|
+
urgency: ["snel", "nu", "direct", "vandaag", "2026", "meteen", "eenvoudig"],
|
|
149
|
+
value: ["gratis", "bespaar", "gegarandeerd", "korting", "goedkoop", "voordeel"],
|
|
150
|
+
curiosity: ["geheim", "onthuld", "tips", "waarom", "hoe", "ontdek"],
|
|
151
|
+
sentimentPositive: ["geweldig", "perfect", "uitstekend", "succes", "fantastisch"],
|
|
152
|
+
sentimentNegative: ["fout", "gevaar", "vermijd", "slechtste", "oplichting", "risico"]
|
|
153
|
+
},
|
|
154
|
+
pt: {
|
|
155
|
+
authority: ["guia", "completo", "definitivo", "especialista", "oficial", "melhor", "melhores", "comparativo"],
|
|
156
|
+
urgency: ["rápido", "agora", "imediato", "hoje", "2026", "em 5 minutos", "urgente"],
|
|
157
|
+
value: ["grátis", "gratuito", "economize", "garantido", "desconto", "fácil", "oferta"],
|
|
158
|
+
curiosity: ["segredo", "revelado", "dicas", "por que", "como fazer", "incrível", "descubra"],
|
|
159
|
+
sentimentPositive: ["perfeito", "incrível", "fantástico", "sucesso", "excelente"],
|
|
160
|
+
sentimentNegative: ["erro", "perigo", "evite", "pior", "golpe", "risco", "cilada"]
|
|
161
|
+
},
|
|
162
|
+
ar: {
|
|
163
|
+
authority: ["دليل", "شامل", "رسمي", "خبير", "أفضل", "مقارنة", "معتمد", "نهائي"],
|
|
164
|
+
urgency: ["سريع", "الآن", "فوري", "اليوم", "2026", "في دقائق", "عاجل"],
|
|
165
|
+
value: ["مجانا", "وفر", "مضمون", "خصم", "سهل", "عرض", "مميز"],
|
|
166
|
+
curiosity: ["سر", "كشف", "أسرار", "لماذا", "كيف", "مذهل", "اكتشف"],
|
|
167
|
+
sentimentPositive: ["رائع", "ممتاز", "ناجح", "مبهر", "مثالي"],
|
|
168
|
+
sentimentNegative: ["خطأ", "تحذير", "تجنب", "فخ", "احتيال", "خطر"]
|
|
169
|
+
},
|
|
170
|
+
ru: {
|
|
171
|
+
authority: ["руководство", "полный", "эксперт", "официальный", "лучший", "лучшие", "сравнение", "проверенный"],
|
|
172
|
+
urgency: ["быстро", "сейчас", "срочно", "сегодня", "2026", "за 5 минут", "немедленно"],
|
|
173
|
+
value: ["бесплатно", "скидка", "гарантия", "выгодно", "легко", "акция"],
|
|
174
|
+
curiosity: ["секрет", "раскрыт", "лайфхаки", "почему", "как", "невероятный", "узнайте"],
|
|
175
|
+
sentimentPositive: ["отлично", "идеально", "успех", "потрясающий", "великолепный"],
|
|
176
|
+
sentimentNegative: ["ошибка", "опасность", "избегать", "худший", "обман", "риск"]
|
|
177
|
+
},
|
|
178
|
+
zh: {
|
|
179
|
+
authority: ["终极", "完整", "指南", "专家", "官方", "最佳", "对比", "评测", "权威"],
|
|
180
|
+
urgency: ["快速", "立即", "今天", "2026", "5分钟", "马上", "紧急"],
|
|
181
|
+
value: ["免费", "省钱", "保证", "优惠", "简单", "折扣", "高性价比"],
|
|
182
|
+
curiosity: ["秘密", "揭秘", "技巧", "为什么", "如何", "必看", "探索"],
|
|
183
|
+
sentimentPositive: ["完美", "惊人", "卓越", "成功", "极佳", "出色"],
|
|
184
|
+
sentimentNegative: ["错误", "陷阱", "避免", "危险", "骗局", "风险"]
|
|
185
|
+
},
|
|
186
|
+
ja: {
|
|
187
|
+
authority: ["完全ガイド", "究極", "公式", "専門家", "おすすめ", "徹底比較", "決定版", "プロ"],
|
|
188
|
+
urgency: ["今すぐ", "速報", "最短", "2026年", "今日", "5分で", "簡単"],
|
|
189
|
+
value: ["無料", "お得", "割引", "保証", "コスパ", "特典", "プレゼント"],
|
|
190
|
+
curiosity: ["秘密", "裏ワザ", "暴露", "なぜ", "方法", "必見", "真実"],
|
|
191
|
+
sentimentPositive: ["最高", "完璧", "圧倒的", "成功", "絶賛", "素晴らしい"],
|
|
192
|
+
sentimentNegative: ["失敗", "注意", "危険", "避けるべき", "詐欺", "罠"]
|
|
193
|
+
},
|
|
194
|
+
hi: {
|
|
195
|
+
authority: ["गाइड", "सम्पूर्ण", "विशेषज्ञ", "आधिकारिक", "सर्वश्रेष्ठ", "तुलना", "सटीक"],
|
|
196
|
+
urgency: ["तुरंत", "अभी", "आज", "2026", "5 मिनट में", "जल्दी"],
|
|
197
|
+
value: ["मुफ्त", "बचत", "गारंटी", "छूट", "आसान", "ऑफर"],
|
|
198
|
+
curiosity: ["रहस्य", "खुलासा", "टिप्स", "क्यों", "कैसे", "अनोखा"],
|
|
199
|
+
sentimentPositive: ["उत्कृष्ट", "शानदार", "सफल", "अद्भुत", "बेहतरीन"],
|
|
200
|
+
sentimentNegative: ["गलती", "खतरा", "सावधान", "धोखा", "नुकसान"]
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
export class PowerWordsPsychologyEngine {
|
|
205
|
+
/**
|
|
206
|
+
* Register a custom language dictionary at runtime for infinite language extensibility
|
|
207
|
+
*/
|
|
208
|
+
static registerLanguageWords(langCode: string, words: Partial<CategoryWordBank>): void {
|
|
209
|
+
const code = langCode.toLowerCase().trim();
|
|
210
|
+
if (!MULTILINGUAL_POWER_WORDS[code]) {
|
|
211
|
+
MULTILINGUAL_POWER_WORDS[code] = {
|
|
212
|
+
authority: [],
|
|
213
|
+
urgency: [],
|
|
214
|
+
value: [],
|
|
215
|
+
curiosity: [],
|
|
216
|
+
sentimentPositive: [],
|
|
217
|
+
sentimentNegative: [],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
if (words.authority) MULTILINGUAL_POWER_WORDS[code].authority.push(...words.authority);
|
|
221
|
+
if (words.urgency) MULTILINGUAL_POWER_WORDS[code].urgency.push(...words.urgency);
|
|
222
|
+
if (words.value) MULTILINGUAL_POWER_WORDS[code].value.push(...words.value);
|
|
223
|
+
if (words.curiosity) MULTILINGUAL_POWER_WORDS[code].curiosity.push(...words.curiosity);
|
|
224
|
+
if (words.sentimentPositive) MULTILINGUAL_POWER_WORDS[code].sentimentPositive.push(...words.sentimentPositive);
|
|
225
|
+
if (words.sentimentNegative) MULTILINGUAL_POWER_WORDS[code].sentimentNegative.push(...words.sentimentNegative);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/**
|
|
229
|
+
* Get all registered language codes
|
|
230
|
+
*/
|
|
231
|
+
static getSupportedLanguages(): string[] {
|
|
232
|
+
return Object.keys(MULTILINGUAL_POWER_WORDS);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Analyze title psychology and CTR potential in ANY language
|
|
237
|
+
*/
|
|
238
|
+
static analyzeTitle(title: string, lang: LanguageCode = "fr"): PsychologyAnalysisResult {
|
|
239
|
+
const raw = (title || "").toLowerCase().trim();
|
|
240
|
+
const langKey = lang.toLowerCase().trim();
|
|
241
|
+
|
|
242
|
+
// Multi-bank search: search primary language, plus universal English / French fallback
|
|
243
|
+
const primaryBank = MULTILINGUAL_POWER_WORDS[langKey] || MULTILINGUAL_POWER_WORDS["en"] || MULTILINGUAL_POWER_WORDS["fr"];
|
|
244
|
+
const allBanks = [primaryBank];
|
|
245
|
+
if (langKey !== "en" && MULTILINGUAL_POWER_WORDS["en"]) allBanks.push(MULTILINGUAL_POWER_WORDS["en"]);
|
|
246
|
+
|
|
247
|
+
const detectedWords: string[] = [];
|
|
248
|
+
let authorityMatch = false;
|
|
249
|
+
let urgencyMatch = false;
|
|
250
|
+
let valueMatch = false;
|
|
251
|
+
let curiosityMatch = false;
|
|
252
|
+
let positiveCount = 0;
|
|
253
|
+
let negativeCount = 0;
|
|
254
|
+
|
|
255
|
+
for (const bank of allBanks) {
|
|
256
|
+
for (const word of bank.authority) {
|
|
257
|
+
if (raw.includes(word.toLowerCase())) {
|
|
258
|
+
authorityMatch = true;
|
|
259
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
for (const word of bank.urgency) {
|
|
263
|
+
if (raw.includes(word.toLowerCase())) {
|
|
264
|
+
urgencyMatch = true;
|
|
265
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
for (const word of bank.value) {
|
|
269
|
+
if (raw.includes(word.toLowerCase())) {
|
|
270
|
+
valueMatch = true;
|
|
271
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
for (const word of bank.curiosity) {
|
|
275
|
+
if (raw.includes(word.toLowerCase())) {
|
|
276
|
+
curiosityMatch = true;
|
|
277
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
for (const word of bank.sentimentPositive) {
|
|
281
|
+
if (raw.includes(word.toLowerCase())) {
|
|
282
|
+
positiveCount++;
|
|
283
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
for (const word of bank.sentimentNegative) {
|
|
287
|
+
if (raw.includes(word.toLowerCase())) {
|
|
288
|
+
negativeCount++;
|
|
289
|
+
if (!detectedWords.includes(word)) detectedWords.push(word);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// Universal Language-Agnostic Triggers
|
|
295
|
+
const hasNumber = /\b\d+\b/.test(raw);
|
|
296
|
+
const hasYear = /\b(202[4-9]|203[0-9])\b/.test(raw);
|
|
297
|
+
const hasCurrencyOrDiscount = /[\$€£¥₹%]|د\.إ|﷼|\b(promo|soldes|discount|deal|off)\b/i.test(raw);
|
|
298
|
+
const hasBracketsOrParentheses = /[\[\]\(\)\{\}【】《》「」]/.test(raw);
|
|
299
|
+
|
|
300
|
+
// Score Calculation (0 - 100)
|
|
301
|
+
let score = 30; // base score
|
|
302
|
+
if (detectedWords.length >= 1) score += 20;
|
|
303
|
+
if (detectedWords.length >= 2) score += 10;
|
|
304
|
+
if (detectedWords.length >= 3) score += 10;
|
|
305
|
+
if (hasNumber) score += 15;
|
|
306
|
+
if (hasYear) score += 10;
|
|
307
|
+
if (hasCurrencyOrDiscount) score += 5;
|
|
308
|
+
if (hasBracketsOrParentheses) score += 5;
|
|
309
|
+
if (score > 100) score = 100;
|
|
310
|
+
|
|
311
|
+
let sentiment: "positif" | "negatif" | "neutre" = "neutre";
|
|
312
|
+
if (positiveCount > negativeCount) sentiment = "positif";
|
|
313
|
+
else if (negativeCount > positiveCount) sentiment = "negatif";
|
|
314
|
+
|
|
315
|
+
const recommendations: string[] = [];
|
|
316
|
+
if (detectedWords.length === 0) {
|
|
317
|
+
recommendations.push("Ajoutez au moins 1 mot d'impact émotionnel ou d'autorité (ex: 'Guide', 'Ultime', 'Meilleur').");
|
|
318
|
+
}
|
|
319
|
+
if (!hasNumber) {
|
|
320
|
+
recommendations.push("Intégrez un chiffre (ex: 'Les 7 Meilleurs...') pour augmenter le taux de clic de +36%.");
|
|
321
|
+
}
|
|
322
|
+
if (!hasYear) {
|
|
323
|
+
recommendations.push("Indiquez l'année courante (2026) pour signaler la fraîcheur du contenu aux internautes.");
|
|
324
|
+
}
|
|
325
|
+
if (!hasBracketsOrParentheses) {
|
|
326
|
+
recommendations.push("Utilisez des crochets ou parenthèses [Guide 2026] pour structurer visuellement l'extrait.");
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Suggested high CTR titles
|
|
330
|
+
const cleanSubject = title.replace(/\(.*?\)|\[.*?\]/g, "").trim();
|
|
331
|
+
const suggestedTitles = [
|
|
332
|
+
`Les 7 Meilleurs ${cleanSubject} [Guide Ultime 2026]`,
|
|
333
|
+
`Comment Maîtriser ${cleanSubject} en 5 Étapes (Méthode 2026)`,
|
|
334
|
+
`${cleanSubject} : Avis, Comparatif et Pièges à Éviter (2026)`
|
|
335
|
+
];
|
|
336
|
+
|
|
337
|
+
return {
|
|
338
|
+
score,
|
|
339
|
+
powerWordsDetected: detectedWords,
|
|
340
|
+
sentiment,
|
|
341
|
+
hasNumber,
|
|
342
|
+
hasYear,
|
|
343
|
+
hasCuriosityTrigger: curiosityMatch,
|
|
344
|
+
hasUrgencyTrigger: urgencyMatch,
|
|
345
|
+
hasAuthorityTrigger: authorityMatch,
|
|
346
|
+
hasCurrencyOrDiscount,
|
|
347
|
+
hasBracketsOrParentheses,
|
|
348
|
+
recommendations,
|
|
349
|
+
suggestedTitles
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Automatically enriches a programmatic title in ANY language with optimal psychology triggers
|
|
355
|
+
*/
|
|
356
|
+
static enrichProgrammaticTitle(baseTitle: string, lang: LanguageCode = "fr"): string {
|
|
357
|
+
const analysis = this.analyzeTitle(baseTitle, lang);
|
|
358
|
+
if (analysis.score >= 80) return baseTitle;
|
|
359
|
+
|
|
360
|
+
let enriched = baseTitle.trim();
|
|
361
|
+
if (!analysis.hasNumber && !enriched.startsWith("Top ") && !enriched.startsWith("Les ")) {
|
|
362
|
+
enriched = `Les Meilleurs ${enriched}`;
|
|
363
|
+
}
|
|
364
|
+
if (!analysis.hasYear) {
|
|
365
|
+
enriched = `${enriched} (Guide 2026)`;
|
|
366
|
+
}
|
|
367
|
+
return enriched;
|
|
368
|
+
}
|
|
369
|
+
}
|