Commit c8548e01 by PLN (Algolia)

tsa: CAT-Q instrument — data, scoring, fr/en copy, unit tests

25 items Likert 1-7 (verbatim Hull 2019 in EN, our translation in FR,
flagged non-validated), items 3/12/19/22/24 reversed, subscales
compensation/masking/assimilation. Percentiles via normal CDF on the
Hull 2020 gender-matched norms; Baker 2024 descriptor bands on the
total score. Storage contract catq:v1, same discipline as clarte:v1.
parent 8ef0bad8
/* Clarté TSA — structure de l'instrument CAT-Q (Hull et al., 2019).
25 items, Likert 1–7, items 3/12/19/22/24 inversés. Les TEXTES vivent dans
i18n (fr.js / en.js) via les clés catq.q{n} ; l'anglais est verbatim Hull
2019, le français est notre traduction (non validée psychométriquement).
Subscales et inversion sont l'instrument : intouchables.
Normes : Hull et al. (2020), moyennes/écart-types par genre, reproduites
depuis la fiche NovoPsych (novopsych.com) — elles alimentent le calcul de
percentiles par distribution normale (voir engine.js). */
export const LIKERT_MIN = 1;
export const LIKERT_MAX = 7;
export const CATQ_ITEMS = [
{ id: 'catq_1', num: 1, sub: 'compensation', reverse: false, t: 'catq.q1' },
{ id: 'catq_2', num: 2, sub: 'masking', reverse: false, t: 'catq.q2' },
{ id: 'catq_3', num: 3, sub: 'assimilation', reverse: true, t: 'catq.q3' },
{ id: 'catq_4', num: 4, sub: 'compensation', reverse: false, t: 'catq.q4' },
{ id: 'catq_5', num: 5, sub: 'compensation', reverse: false, t: 'catq.q5' },
{ id: 'catq_6', num: 6, sub: 'masking', reverse: false, t: 'catq.q6' },
{ id: 'catq_7', num: 7, sub: 'assimilation', reverse: false, t: 'catq.q7' },
{ id: 'catq_8', num: 8, sub: 'compensation', reverse: false, t: 'catq.q8' },
{ id: 'catq_9', num: 9, sub: 'masking', reverse: false, t: 'catq.q9' },
{ id: 'catq_10', num: 10, sub: 'assimilation', reverse: false, t: 'catq.q10' },
{ id: 'catq_11', num: 11, sub: 'compensation', reverse: false, t: 'catq.q11' },
{ id: 'catq_12', num: 12, sub: 'masking', reverse: true, t: 'catq.q12' },
{ id: 'catq_13', num: 13, sub: 'assimilation', reverse: false, t: 'catq.q13' },
{ id: 'catq_14', num: 14, sub: 'compensation', reverse: false, t: 'catq.q14' },
{ id: 'catq_15', num: 15, sub: 'masking', reverse: false, t: 'catq.q15' },
{ id: 'catq_16', num: 16, sub: 'assimilation', reverse: false, t: 'catq.q16' },
{ id: 'catq_17', num: 17, sub: 'compensation', reverse: false, t: 'catq.q17' },
{ id: 'catq_18', num: 18, sub: 'masking', reverse: false, t: 'catq.q18' },
{ id: 'catq_19', num: 19, sub: 'assimilation', reverse: true, t: 'catq.q19' },
{ id: 'catq_20', num: 20, sub: 'compensation', reverse: false, t: 'catq.q20' },
{ id: 'catq_21', num: 21, sub: 'masking', reverse: false, t: 'catq.q21' },
{ id: 'catq_22', num: 22, sub: 'assimilation', reverse: true, t: 'catq.q22' },
{ id: 'catq_23', num: 23, sub: 'compensation', reverse: false, t: 'catq.q23' },
{ id: 'catq_24', num: 24, sub: 'masking', reverse: true, t: 'catq.q24' },
{ id: 'catq_25', num: 25, sub: 'assimilation', reverse: false, t: 'catq.q25' },
];
export const SUBSCALES = [
{ key: 'compensation', nameKey: 'catq.sub.compensation', descKey: 'catq.sub.compensation.desc', items: [1, 4, 5, 8, 11, 14, 17, 20, 23] },
{ key: 'masking', nameKey: 'catq.sub.masking', descKey: 'catq.sub.masking.desc', items: [2, 6, 9, 12, 15, 18, 21, 24] },
{ key: 'assimilation', nameKey: 'catq.sub.assimilation', descKey: 'catq.sub.assimilation.desc', items: [3, 7, 10, 13, 16, 19, 22, 25] },
];
export const CATQ_IDS = CATQ_ITEMS.map((q) => q.id);
export const TOTAL_QUESTIONS = CATQ_ITEMS.length; /* 25 */
export const GENDERS = ['f', 'm', 'nb', 'unspecified'];
/* Moyennes (mu) et écarts-types (sd) — Hull et al. 2020, fiche NovoPsych.
f = femmes, m = hommes, nb = non-binaires ; unspecified = moyenne des trois
groupes (pooled, approximation assumée, comme chez NovoPsych). */
export const NORMS = {
nonAutistic: {
total: { f: { mu: 90.87, sd: 27.67 }, m: { mu: 96.89, sd: 24.22 }, nb: { mu: 109.44, sd: 27.20 } },
compensation: { f: { mu: 27.18, sd: 11.5 }, m: { mu: 30.06, sd: 10.92 }, nb: { mu: 35.48, sd: 11.32 } },
masking: { f: { mu: 34.69, sd: 9.05 }, m: { mu: 36.34, sd: 8.13 }, nb: { mu: 38.70, sd: 7.61 } },
assimilation: { f: { mu: 29.00, sd: 11.73 }, m: { mu: 30.48, sd: 10.33 }, nb: { mu: 35.26, sd: 12.11 } },
},
autistic: {
total: { f: { mu: 124.35, sd: 23.27 }, m: { mu: 109.64, sd: 26.50 }, nb: { mu: 122.00, sd: 17.12 } },
compensation: { f: { mu: 41.85, sd: 11.11 }, m: { mu: 36.81, sd: 12.14 }, nb: { mu: 43.50, sd: 9.89 } },
masking: { f: { mu: 37.87, sd: 10.54 }, m: { mu: 32.90, sd: 10.57 }, nb: { mu: 36.06, sd: 8.78 } },
assimilation: { f: { mu: 44.63, sd: 7.82 }, m: { mu: 39.93, sd: 11.26 }, nb: { mu: 39.88, sd: 6.43 } },
},
};
/* Bornes des descripteurs qualitatifs du score total (Baker et al., 2024,
fiche NovoPsych) — dérivées des percentiles de l'échantillon non-autiste
(Hull et al., 2020) : ≤5e, 6–24, 25–75, 76–94, ≥95e. Les mêmes tranches de
percentiles sont appliquées aux subscales (mêmes normes, même logique). */
export const TOTAL_BANDS = [
{ key: 'extremely_low', min: 25, max: 51 },
{ key: 'low', min: 52, max: 75 },
{ key: 'moderate', min: 76, max: 112 },
{ key: 'high', min: 113, max: 136 },
{ key: 'extremely_high', min: 137, max: 175 },
];
/* Clarté TSA — moteur : persistance (contrat catq:v1, même discipline que
clarte:v1) et scoring CAT-Q. Les réponses stockent le Likert BRUT 1–7 tel
qu'affiché ; l'inversion (items 3/12/19/22/24) se fait au scoring
(8 − v), comme sur la fiche papier du constructeur.
Percentiles : Φ((x − μ)/σ) sur les normes Hull 2020 appariées au genre —
la même voie que NovoPsych, sans table propriétaire. */
import { CATQ_ITEMS, SUBSCALES, NORMS, TOTAL_BANDS, TOTAL_QUESTIONS, GENDERS } from './data';
export const STORAGE_KEY = 'catq:v1';
export function storageAvailable() {
try {
const k = STORAGE_KEY + ':probe';
localStorage.setItem(k, '1');
localStorage.removeItem(k);
return true;
} catch (e) { return false; }
}
export function readState() {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (!raw) return null;
const v = JSON.parse(raw);
if (!v || typeof v !== 'object' || typeof v.responses !== 'object' || v.responses === null) return null;
return v;
} catch (e) { return null; }
}
export function writeState(state) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(state));
return true;
} catch (e) { return false; }
}
export function clearState() {
try { localStorage.removeItem(STORAGE_KEY); } catch (e) {}
}
/* Φ par erf d'Abramowitz-Stegun (précision ~1e-7, largement assez pour un
percentile affiché en entier). */
function erf(x) {
const sign = x < 0 ? -1 : 1;
const a = Math.abs(x);
const t = 1 / (1 + 0.3275911 * a);
const y = 1 - ((((1.061405429 * t - 1.453152027) * t + 1.421413741) * t - 0.284496736) * t + 0.254829592) * t * Math.exp(-a * a);
return sign * y;
}
export function normalCdf(x, mu, sd) {
return 0.5 * (1 + erf((x - mu) / (sd * Math.SQRT2)));
}
function normFor(pool, scaleKey, gender) {
const entry = NORMS[pool][scaleKey];
if (gender && GENDERS.indexOf(gender) !== -1 && entry[gender]) return entry[gender];
/* pooled : moyenne des trois groupes (approximation assumée) */
const gs = ['f', 'm', 'nb'];
return {
mu: gs.reduce((s, g) => s + entry[g].mu, 0) / 3,
sd: gs.reduce((s, g) => s + entry[g].sd, 0) / 3,
};
}
/* Percentile affiché : 1–99 (Φ brut, borné pour éviter le 0/100 trompeur). */
export function percentileOf(score, pool, scaleKey, gender) {
const { mu, sd } = normFor(pool, scaleKey, gender);
const p = normalCdf(score, mu, sd) * 100;
return Math.max(1, Math.min(99, Math.round(p)));
}
/* Descripteur qualitatif par tranche de percentile (≤5, 6–24, 25–75, 76–94, ≥95). */
export function descriptorFromPercentile(p) {
if (p <= 5) return 'extremely_low';
if (p <= 24) return 'low';
if (p <= 75) return 'moderate';
if (p <= 94) return 'high';
return 'extremely_high';
}
export function totalBand(score) {
const band = TOTAL_BANDS.find((b) => score >= b.min && score <= b.max);
return band ? band.key : null;
}
export function scoreCatq(responses, gender) {
const raw = (id) => responses[id];
const scored = (q) => {
const v = raw(q.id);
if (v === undefined) return undefined;
const s = Math.max(1, Math.min(7, v));
return q.reverse ? 8 - s : s;
};
const subscales = {};
let total = 0;
let complete = true;
SUBSCALES.forEach((sub) => {
let score = 0;
sub.items.forEach((num) => {
const q = CATQ_ITEMS[num - 1];
const v = scored(q);
if (v === undefined) complete = false;
else score += v;
});
subscales[sub.key] = {
score,
min: sub.items.length,
max: sub.items.length * 7,
pNonAutistic: percentileOf(score, 'nonAutistic', sub.key, gender),
pAutistic: percentileOf(score, 'autistic', sub.key, gender),
};
});
CATQ_ITEMS.forEach((q) => { const v = scored(q); if (v !== undefined) total += v; });
return {
complete,
subscales,
total: {
score: total,
min: TOTAL_QUESTIONS,
max: TOTAL_QUESTIONS * 7,
pNonAutistic: percentileOf(total, 'nonAutistic', 'total', gender),
pAutistic: percentileOf(total, 'autistic', 'total', gender),
band: totalBand(total),
},
};
}
export function calculateResults(responses, gender) {
return scoreCatq(responses, gender);
}
/* Clarté TSA – English dictionary. Items catq.q1–q25: verbatim wording from
Hull et al. (2019) as reproduced on the NovoPsych scoring sheet. */
export const dict = {
/* ---------- header / home ---------- */
'header.logo.h1': 'Clarté TSA',
'header.subtitle': 'Autistic camouflaging self-assessment for adults – CAT-Q (Hull et al., 2019)',
'home.h2': 'Camouflaging: measure what standard tests can’t see',
'home.intro.p': 'The CAT-Q (Camouflaging Autistic Traits Questionnaire) assesses camouflaging strategies: compensating, masking, assimilating. 25 statements on a 7-point scale, about 4 minutes. It does not measure autism itself – it measures the effort to hide it, a valuable angle for women and gender-diverse people, who are often underdiagnosed for exactly this reason.',
'home.card.catq.h4': 'CAT-Q – autistic camouflaging',
'home.card.catq.p': '25 items · 3 subscales (compensation, masking, assimilation) · total score 25–175, compared against autistic and non-autistic adult norms (Hull et al., 2020).',
'home.card.norms.h4': 'Gender-matched norms',
'home.card.norms.p': 'Your scores are placed in percentiles against non-autistic and autistic adults of the same gender (female / male / non-binary), or against pooled norms if you prefer not to say.',
'home.card.privacy.h4': '100% local',
'home.card.privacy.p': 'No account, nothing sent: your answers stay in this browser (localStorage) and can be wiped at any time.',
'home.gender.label': 'To situate your scores (optional, stored locally):',
'home.gender.f': 'Female',
'home.gender.m': 'Male',
'home.gender.nb': 'Non-binary',
'home.gender.unspecified': 'Prefer not to say (pooled norms)',
'home.warning.h4': 'Before you start',
'home.warning.li1': 'This is not a diagnosis.',
'home.warning.li2': 'The CAT-Q measures camouflaging, not autism: autistic and non-autistic people both camouflage, and a high score can also reflect social anxiety or other neurodivergence (e.g. ADHD).',
'home.warning.li3': 'The instrument is administered here in English as published; score interpretation follows Hull et al. (2019/2020) and Baker et al. (2024).',
'home.warning.li4': 'No automated report replaces a clinician; this test exists to prepare that conversation.',
'home.warning.li5': 'A change of 13 points or more on the total score counts as a meaningful change (MID, Baker et al., 2024).',
'home.consent.label': 'I understand this is not a diagnosis and my answers stay in this browser.',
'home.start.btn': 'Start',
'resume.count': 'You have already answered {n} of 25 questions. Pick up where you left off, or start over below.',
'resume.primary': 'Resume',
'resume.secondary': 'Home',
'home.restart.btn': 'Start over',
'home.storage.line': 'Your answers are saved locally (key “catq:v1”). Nothing leaves your device.',
/* ---------- flow intro ---------- */
'intro.catq.h': 'CAT-Q – 25 statements',
'intro.catq.p': 'For each statement, choose the response that best fits your usual experience in social interactions. There are no right or wrong answers; you can go back at any time.',
'intro.continue.btn': 'Let’s go',
'intro.back': '← Back',
'intro.skip.btn': 'Skip →',
/* ---------- items (verbatim Hull et al., 2019) ---------- */
'catq.q1': 'When I am interacting with someone, I deliberately copy their body language or facial expressions.',
'catq.q2': 'I monitor my body language or facial expressions so that I appear relaxed.',
'catq.q3': 'I rarely feel the need to put on an act in order to get through a social situation.',
'catq.q4': 'I have developed a script to follow in social situations.',
'catq.q5': 'I will repeat phrases that I have heard others say in the exact same way that I first heard them.',
'catq.q6': 'I adjust my body language or facial expressions so that I appear interested by the person I am interacting with.',
'catq.q7': 'In social situations, I feel like I’m ‘performing’ rather than being myself.',
'catq.q8': 'In my own social interactions, I use behaviours that I have learned from watching other people interacting.',
'catq.q9': 'I always think about the impression I make on other people.',
'catq.q10': 'I need the support of other people in order to socialise.',
'catq.q11': 'I practise my facial expressions and body language to make sure they look natural.',
'catq.q12': 'I don’t feel the need to make eye contact with other people if I don’t want to.',
'catq.q13': 'I have to force myself to interact with people when I am in social situations.',
'catq.q14': 'I have tried to improve my understanding of social skills by watching other people.',
'catq.q15': 'I monitor my body language or facial expressions so that I appear interested by the person I am interacting with.',
'catq.q16': 'When in social situations, I try to find ways to avoid interacting with others.',
'catq.q17': 'I have researched the rules of social interactions to improve my own social skills.',
'catq.q18': 'I am always aware of the impression I make on other people.',
'catq.q19': 'I feel free to be myself when I am with other people.',
'catq.q20': 'I learn how people use their bodies and faces to interact by watching television or films, or by reading fiction.',
'catq.q21': 'I adjust my body language or facial expressions so that I appear relaxed.',
'catq.q22': 'When talking to other people, I feel like the conversation flows naturally.',
'catq.q23': 'I have spent time learning social skills from television shows and films, and try to use these in my interactions.',
'catq.q24': 'In social interactions, I do not pay attention to what my face or body are doing.',
'catq.q25': 'In social situations, I feel like I am pretending to be ‘normal’.',
/* ---------- Likert options ---------- */
'catq.likert.h2': 'CAT-Q',
'catq.option.1': 'Strongly disagree',
'catq.option.2': 'Disagree',
'catq.option.3': 'Somewhat disagree',
'catq.option.4': 'Neither agree nor disagree',
'catq.option.5': 'Somewhat agree',
'catq.option.6': 'Agree',
'catq.option.7': 'Strongly agree',
'progress.answered': '{n} question(s) answered · {m} to go',
/* ---------- recap ---------- */
'recap.title': 'Recap',
'recap.unanswered.chip': 'unanswered',
'recap.missing.count': '{n} question(s) left.',
'recap.missing.go': 'Go to first missing',
'recap.missing.skip': 'Leave everything “strongly disagree” and see results',
'recap.complete.btn': 'See my results →',
'recap.backHome': 'Back to home',
'recap.review.fromresults': 'Review my answers',
'recap.share.copy': 'Copy',
/* ---------- results ---------- */
'results.warning.p': 'Reminder: this is not a diagnosis. The CAT-Q measures camouflaging strategies, not autism.',
'results.summary.h2': 'Your total score',
'results.summary.text': 'Total score: {score} / 175 – {band} (percentile {pNA} vs non-autistic adults · percentile {pA} vs autistic adults, {genre} norms).',
'results.band.extremely_low': 'extremely low',
'results.band.low': 'low',
'results.band.moderate': 'moderate',
'results.band.high': 'high',
'results.band.extremely_high': 'extremely high',
'results.gender.f': 'female',
'results.gender.m': 'male',
'results.gender.nb': 'non-binary',
'results.gender.unspecified': 'pooled',
'results.sub.h2': 'The three subscales',
'results.sub.line': '{name}: {score} / {max} – percentile {pNA} non-autistic · percentile {pA} autistic ({band})',
'catq.sub.compensation': 'Compensation',
'catq.sub.compensation.desc': 'Strategies for overcoming social challenges, such as using learned social scripts or imitating behaviours.',
'catq.sub.masking': 'Masking',
'catq.sub.masking.desc': 'Efforts to hide or suppress Autistic traits to appear more neurotypical.',
'catq.sub.assimilation': 'Assimilation',
'catq.sub.assimilation.desc': 'Attempts to fit into social situations, such as modifying behaviours to blend in.',
'results.percentile.h2': 'Reading your percentiles',
'results.percentile.p': 'Percentiles compare your scores to non-autistic adults of the same gender (Hull et al., 2020; Baker et al., 2024): the 50th percentile is typical camouflaging among non-autistic adults, the 90th is the top 10% of that sample. A high percentile against autistic adults proves nothing either – the two distributions overlap substantially.',
'results.caveats.h2': 'Interpret with care',
'results.caveat.1': 'Camouflaging varies by gender: autistic women and non-binary people camouflage more on average – precisely why this test matters for them.',
'results.caveat.2': 'Camouflaging is not specific to autism: non-autistic people do it too, and the CAT-Q does not separate the two.',
'results.caveat.3': '“Moderate” scores are deliberately left without fine interpretation: they overlap both populations, so no conclusion can be drawn from that band alone.',
'results.caveat.4': 'Some items conflate autistic camouflaging with strategies closer to social anxiety; a high score can reflect another source (ADHD, anxiety).',
'results.caveat.5': 'Greater camouflaging among autistic adults is associated with more mental-health difficulties (Cook et al., 2021) – if your score is high, that point deserves a place in the conversation with a professional.',
'results.consult.title': 'For the consultation',
'results.consult.sentence.label': 'Sentence to copy for your appointment:',
'results.consult.sentence': 'I took the CAT-Q (autistic camouflaging, Hull et al. 2019) as a self-assessment: total score {score}/175 ({band}), compensation {comp}/{compMax}, masking {mask}/{maskMax}, assimilation {assim}/{assimMax}.',
'results.download.btn': 'Download PDF report',
'results.restart.btn': 'Restart from zero',
'results.source.h2': 'Sources',
'results.source.p': 'Hull, L., Mandy, W., Lai, M.-C., Baron-Cohen, S., Allison, C., Smith, P., & Petrides, K.-V. (2019). Development and validation of the Camouflaging Autistic Traits Questionnaire (CAT-Q). Journal of Autism and Developmental Disorders, 49(3), 819–833. Norms: Hull et al. (2020), Autism 24(2); descriptors: Baker et al. (2024).',
/* ---------- PDF ---------- */
'pdf.title': 'Clarté TSA – CAT-Q report (autistic camouflaging)',
'pdf.warning.p': 'Self-assessment document, generated locally in the browser. This is not a diagnosis: the CAT-Q measures camouflaging strategies, not autism.',
'pdf.summary.h': 'Summary',
'pdf.section1.h': 'Scores',
'pdf.annexe.h': 'Appendix – detailed answers',
'pdf.unanswered': 'unanswered',
'pdf.refs.h': 'References',
'pdf.refs.items': 'Hull L, Mandy WP, Lai M-C, Baron-Cohen S, Allison C, Smith P, Petrides KV (2019). Development and validation of the Camouflaging Autistic Traits Questionnaire (CAT-Q). Journal of Autism and Developmental Disorders, 49(3), 819–833. • Hull L, et al. (2020). Gender differences in self-reported camouflaging in autistic and non-autistic adults. Autism, 24(2), 352–363. • Baker S, et al. (2024). A Review of the Clinical Utility and Psychometric Characteristics of the CAT-Q. • Cook J, Hull L, Crane L, Mandy W (2021). Camouflaging in autism: A systematic review. Clinical Psychology Review, 89, 102080.',
'pdf.pagefooter': 'Clarté TSA · local self-assessment · page {i} · not a diagnosis',
};
/* Clarté TSA – dictionnaire français. Items catq.q1–q25 : notre traduction du
CAT-Q (Hull et al., 2019), non validée psychométriquement – dit explicitement
dans home.warning et results. Références et chiffres clés conservés. */
export const dict = {
/* ---------- entête / accueil ---------- */
'header.logo.h1': 'Clarté TSA',
'header.subtitle': 'Auto-évaluation du camouflage autistique chez l’adulte – CAT-Q (Hull et al., 2019)',
'home.h2': 'Camouflage : mesurez ce que les tests classiques ne voient pas',
'home.intro.p': 'Le CAT-Q (Camouflaging Autistic Traits Questionnaire) évalue les stratégies de « camouflage » : compenser, masquer, s’assimiler. 25 affirmations, une réponse à échelle de 7 points, environ 4 minutes. Il ne mesure pas l’autisme lui-même : il mesure l’effort pour le dissimuler – un angle précieux chez les femmes et les personnes de genre divers, souvent sous-diagnostiquées pour cette raison.',
'home.card.catq.h4': 'CAT-Q – camouflage autistique',
'home.card.catq.p': '25 items · 3 sous-échelles (compensation, masquage, assimilation) · score total 25–175, comparé aux normes adultes autistes et non-autistes (Hull et al., 2020).',
'home.card.norms.h4': 'Normes par genre',
'home.card.norms.p': 'Vos scores sont situés en percentiles contre les adultes non-autistes et autistes du même genre (femme / homme / non-binaire), ou contre les normes groupées si vous ne précisez pas.',
'home.card.privacy.h4': '100 % local',
'home.card.privacy.p': 'Aucun compte, aucun envoi : vos réponses restent dans ce navigateur (localStorage), effaçables à tout moment.',
'home.gender.label': 'Pour situer vos scores (facultatif, stocké localement) :',
'home.gender.f': 'Femme',
'home.gender.m': 'Homme',
'home.gender.nb': 'Non-binaire',
'home.gender.unspecified': 'Préfère ne pas préciser (normes groupées)',
'home.warning.h4': 'Avant de commencer',
'home.warning.li1': 'Ce n’est pas un diagnostic.',
'home.warning.li2': 'Le CAT-Q mesure le camouflage, pas l’autisme : autistes comme non-autistes camouflent, et un score élevé peut aussi refléter de l’anxiété sociale ou une autre neurodivergence (par ex. le TDAH).',
'home.warning.li3': 'La traduction française des items est la nôtre et n’a pas été validée psychométriquement ; la version originale fait foi.',
'home.warning.li4': 'Aucun compte rendu automatisé ne remplace une clinicienne ou un clinicien ; ce test sert à préparer la conversation.',
'home.warning.li5': 'Un changement de 13 points ou plus au score total est considéré comme une évolution meaningful (MID, Baker et al., 2024).',
'home.consent.label': 'Je comprends que ce n’est pas un diagnostic et que mes réponses restent dans ce navigateur.',
'home.start.btn': 'Commencer',
'resume.count': 'Vous avez déjà répondu à {n} question(s) sur 25. Reprenez où vous étiez, ou repartez de zéro plus bas.',
'resume.primary': 'Reprendre',
'resume.secondary': 'Accueil',
'home.restart.btn': 'Repartir de zéro',
'home.storage.line': 'Vos réponses sont enregistrées localement (clé « catq:v1 »). Rien ne quitte votre appareil.',
/* ---------- intro du flux ---------- */
'intro.catq.h': 'CAT-Q – 25 affirmations',
'intro.catq.p': 'Pour chaque affirmation, choisissez la réponse qui décrit le mieux votre expérience habituelle en interaction sociale. Il n’y a ni bonne ni mauvaise réponse ; vous pourrez revenir en arrière.',
'intro.continue.btn': 'C’est parti',
'intro.back': '← Retour',
'intro.skip.btn': 'Passer →',
/* ---------- items ---------- */
'catq.q1': 'Quand j’interagis avec quelqu’un, j’imite délibérément son langage corporel ou ses expressions faciales.',
'catq.q2': 'Je surveille mon langage corporel ou mes expressions faciales pour paraître détendu(e).',
'catq.q3': 'Je ressens rarement le besoin de jouer un rôle pour traverser une situation sociale.',
'catq.q4': 'J’ai mis au point un script à suivre dans les situations sociales.',
'catq.q5': 'Je répète des phrases entendues chez d’autres exactement de la même façon que lorsque je les ai entendues pour la première fois.',
'catq.q6': 'J’ajuste mon langage corporel ou mes expressions faciales pour paraître intéressé(e) par la personne avec qui j’interagis.',
'catq.q7': 'Dans les situations sociales, j’ai l’impression de « jouer une représentation » plutôt que d’être moi-même.',
'catq.q8': 'Dans mes interactions sociales, j’utilise des comportements appris en observant d’autres personnes interagir.',
'catq.q9': 'Je pense toujours à l’impression que je fais sur les autres.',
'catq.q10': 'J’ai besoin du soutien d’autres personnes pour socialiser.',
'catq.q11': 'Je répète mes expressions faciales et mon langage corporel pour m’assurer qu’ils ont l’air naturels.',
'catq.q12': 'Je ne ressens pas le besoin d’établir un contact visuel avec les autres si je n’en ai pas envie.',
'catq.q13': 'Je dois me forcer à interagir avec les gens quand je suis dans une situation sociale.',
'catq.q14': 'J’ai essayé d’améliorer ma compréhension des compétences sociales en observant les autres.',
'catq.q15': 'Je surveille mon langage corporel ou mes expressions faciales pour paraître intéressé(e) par la personne avec qui j’interagis.',
'catq.q16': 'Dans les situations sociales, je cherche des moyens d’éviter d’interagir avec les autres.',
'catq.q17': 'J’ai étudié les règles des interactions sociales pour améliorer mes compétences sociales.',
'catq.q18': 'Je suis toujours conscient(e) de l’impression que je fais sur les autres.',
'catq.q19': 'Je me sens libre d’être moi-même quand je suis avec d’autres personnes.',
'catq.q20': 'J’apprends comment les gens utilisent leur corps et leur visage pour interagir en regardant la télévision ou des films, ou en lisant de la fiction.',
'catq.q21': 'J’ajuste mon langage corporel ou mes expressions faciales pour paraître détendu(e).',
'catq.q22': 'Quand je parle avec d’autres personnes, j’ai l’impression que la conversation coule naturellement.',
'catq.q23': 'J’ai passé du temps à apprendre des compétences sociales grâce aux séries télévisées et aux films, et j’essaie de les utiliser dans mes interactions.',
'catq.q24': 'Dans les interactions sociales, je ne fais pas attention à ce que font mon visage ou mon corps.',
'catq.q25': 'Dans les situations sociales, j’ai l’impression de faire semblant d’être « normal(e) ».',
/* ---------- options Likert ---------- */
'catq.likert.h2': 'CAT-Q',
'catq.option.1': 'Pas du tout d’accord',
'catq.option.2': 'Pas d’accord',
'catq.option.3': 'Plutôt pas d’accord',
'catq.option.4': 'Ni d’accord ni pas d’accord',
'catq.option.5': 'Plutôt d’accord',
'catq.option.6': 'D’accord',
'catq.option.7': 'Tout à fait d’accord',
'progress.answered': '{n} question(s) répondue(s) · il en reste {m}',
/* ---------- récapitulatif ---------- */
'recap.title': 'Récapitulatif',
'recap.unanswered.chip': 'non répondu',
'recap.missing.count': 'Il reste {n} question(s).',
'recap.missing.go': 'Aller à la première manquante',
'recap.missing.skip': 'Tout laisser en « pas du tout d’accord » et voir les résultats',
'recap.complete.btn': 'Voir mes résultats →',
'recap.backHome': 'Retour à l’accueil',
'recap.review.fromresults': 'Revoir mes réponses',
'recap.share.copy': 'Copier',
/* ---------- résultats ---------- */
'results.warning.p': 'Rappel : ce n’est pas un diagnostic. Le CAT-Q mesure des stratégies de camouflage, pas l’autisme.',
'results.summary.h2': 'Votre score total',
'results.summary.text': 'Score total : {score} / 175 – {band} (percentile {pNA} vs adultes non-autistes · percentile {pA} vs adultes autistes, normes {genre}).',
'results.band.extremely_low': 'extrêmement bas',
'results.band.low': 'bas',
'results.band.moderate': 'modéré',
'results.band.high': 'élevé',
'results.band.extremely_high': 'extrêmement élevé',
'results.gender.f': 'femmes',
'results.gender.m': 'hommes',
'results.gender.nb': 'personnes non-binaires',
'results.gender.unspecified': 'groupées (tous genres)',
'results.sub.h2': 'Les trois sous-échelles',
'results.sub.line': '{name} : {score} / {max} – percentile {pNA} non-autistes · percentile {pA} autistes ({band})',
'catq.sub.compensation': 'Compensation',
'catq.sub.compensation.desc': 'Stratégies pour surmonter les défis sociaux : scripts appris, imitation des comportements.',
'catq.sub.masking': 'Masquage',
'catq.sub.masking.desc': 'Efforts pour cacher ou supprimer les traits autistes et paraître plus neurotypique.',
'catq.sub.assimilation': 'Assimilation',
'catq.sub.assimilation.desc': 'Tentatives de s’intégrer aux situations sociales en adaptant ses comportements.',
'results.percentile.h2': 'Lire vos percentiles',
'results.percentile.p': 'Les percentiles comparent vos scores à des adultes non-autistes du même genre (Hull et al., 2020 ; Baker et al., 2024) : le 50e percentile correspond au niveau de camouflage typique chez les non-autistes, le 90e au top 10 % de cet échantillon. Un percentile élevé chez les adultes autistes ne « prouve » rien non plus : les deux distributions se recouvrent largement.',
'results.caveats.h2': 'Interpréter avec prudence',
'results.caveat.1': 'Le camouflage varie selon le genre : femmes et personnes non-binaires autistes camouflent en moyenne davantage – c’est précisément pourquoi ce test compte pour elles et eux.',
'results.caveat.2': 'Le camouflage n’est pas propre à l’autisme : il existe chez les non-autistes aussi, et le CAT-Q ne distingue pas les deux.',
'results.caveat.3': 'Les scores « modérés » sont délibérément sans interprétation fine : ils recouvrent les deux populations, aucune conclusion n’est possible sur cette tranche seule.',
'results.caveat.4': 'Certains items confondent camouflage autistique et stratégies proches de l’anxiété sociale ; un score élevé peut refléter une autre source (TDAH, anxiété).',
'results.caveat.5': 'Plus le camouflage est fort chez un adulte autiste, plus les difficultés de santé mentale sont fréquentes (Cook et al., 2021) – si votre score est élevé, ce point mérite une place dans la conversation avec un professionnel.',
'results.consult.title': 'Pour la consultation',
'results.consult.sentence.label': 'Phrase à copier pour votre rendez-vous :',
'results.consult.sentence': 'J’ai passé le CAT-Q (camouflage autistique, Hull et al. 2019) en auto-évaluation : score total {score}/175 ({band}), compensation {comp}/{compMax}, masquage {mask}/{maskMax}, assimilation {assim}/{assimMax}.',
'results.download.btn': 'Télécharger le rapport PDF',
'results.restart.btn': 'Recommencer à zéro',
'results.source.h2': 'Sources',
'results.source.p': 'Hull, L., Mandy, W., Lai, M.-C., Baron-Cohen, S., Allison, C., Smith, P., & Petrides, K.-V. (2019). Development and validation of the Camouflaging Autistic Traits Questionnaire (CAT-Q). Journal of Autism and Developmental Disorders, 49(3), 819–833. Normes : Hull et al. (2020), Autism 24(2) ; descripteurs : Baker et al. (2024). Traduction française : non validée.',
/* ---------- PDF ---------- */
'pdf.title': 'Clarté TSA – Compte rendu CAT-Q (camouflage autistique)',
'pdf.warning.p': 'Document d’auto-évaluation, généré localement dans le navigateur. Ce n’est pas un diagnostic : le CAT-Q mesure des stratégies de camouflage, pas l’autisme.',
'pdf.summary.h': 'Synthèse',
'pdf.section1.h': 'Scores',
'pdf.annexe.h': 'Annexe – réponses détaillées',
'pdf.unanswered': 'non répondu',
'pdf.refs.h': 'Références',
'pdf.refs.items': 'Hull L, Mandy WP, Lai M-C, Baron-Cohen S, Allison C, Smith P, Petrides KV (2019). Development and validation of the Camouflaging Autistic Traits Questionnaire (CAT-Q). Journal of Autism and Developmental Disorders, 49(3), 819–833. • Hull L, et al. (2020). Gender differences in self-reported camouflaging in autistic and non-autistic adults. Autism, 24(2), 352–363. • Baker S, et al. (2024). A Review of the Clinical Utility and Psychometric Characteristics of the CAT-Q. • Cook J, Hull L, Crane L, Mandy W (2021). Camouflaging in autism: A systematic review. Clinical Psychology Review, 89, 102080. • Traduction française des items : non validée psychométriquement.',
'pdf.pagefooter': 'Clarté TSA · auto-évaluation locale · page {i} · pas un diagnostic',
};
/* Clarté TSA — moteur : scoring CAT-Q (sous-échelles, inversion 8−v, total
25–175), percentiles Φ appariés au genre, bandes qualitatives, persistance. */
import {
scoreCatq, totalBand, percentileOf, descriptorFromPercentile,
storageAvailable, readState, writeState, clearState, normalCdf,
} from '@/components/tsa/engine';
import { CATQ_ITEMS, NORMS } from '@/components/tsa/data';
const allDisagree = () => {
const r = {};
CATQ_ITEMS.forEach((q) => { r[q.id] = 1; });
return r;
};
const allAgree = () => {
const r = {};
CATQ_ITEMS.forEach((q) => { r[q.id] = 7; });
return r;
};
describe('CAT-Q scoring', () => {
test('all « pas du tout d’accord » (1) → total 55 (les 5 items inversés valent 7)', () => {
const r = scoreCatq(allDisagree(), 'f');
/* items inversés : 3, 12, 19, 22, 24 → 8−1 = 7 */
expect(r.total.score).toBe(25 + 5 * 6);
expect(r.complete).toBe(true);
});
test('all « tout à fait d’accord » (7) → total 145 (les 5 items inversés valent 1)', () => {
const r = scoreCatq(allAgree(), 'f');
expect(r.total.score).toBe(175 - 5 * 6);
});
test('répondre « pas du tout d’accord » minimise les subscales hors inversés', () => {
const r = scoreCatq(allDisagree(), 'm');
/* compensation (9 items, aucun inversé) : min 9 */
expect(r.subscales.compensation.score).toBe(9);
expect(r.subscales.compensation.max).toBe(63);
/* masking : items 12 et 24 inversés → 2×7 + 6×1 = 20 */
expect(r.subscales.masking.score).toBe(20);
expect(r.subscales.masking.max).toBe(56);
/* assimilation : items 3,19,22 inversés (3×7) + 5×1 = 26 */
expect(r.subscales.assimilation.score).toBe(26);
expect(r.subscales.assimilation.max).toBe(56);
});
test('inversion : 8 − v sur les items 3/12/19/22/24', () => {
const r = {};
CATQ_ITEMS.forEach((q) => { r[q.id] = q.reverse ? 2 : 6; });
const s = scoreCatq(r, 'unspecified');
/* chaque item vaut 6 dans les deux cas */
expect(s.total.score).toBe(25 * 6);
});
test('incomplet → complete=false et total partiel cohérent', () => {
const r = allDisagree();
delete r.catq_13;
const s = scoreCatq(r, 'f');
expect(s.complete).toBe(false);
expect(s.total.score).toBe(55 - 1); /* sans catq_13 (non inversé → 1) */
});
test('clamp : réponses hors 1–7 bornées', () => {
const r = allDisagree();
r.catq_1 = 42;
r.catq_2 = -3;
const s = scoreCatq(r, 'f');
/* catq_1 non inversé → 7 (+6) ; catq_2 non inversé → 1 (+0) */
expect(s.total.score).toBe(55 + 6);
});
});
describe('bands and percentiles', () => {
test('bornes des bandes totales (Baker 2024)', () => {
expect(totalBand(25)).toBe('extremely_low');
expect(totalBand(51)).toBe('extremely_low');
expect(totalBand(52)).toBe('low');
expect(totalBand(75)).toBe('low');
expect(totalBand(76)).toBe('moderate');
expect(totalBand(112)).toBe('moderate');
expect(totalBand(113)).toBe('high');
expect(totalBand(136)).toBe('high');
expect(totalBand(137)).toBe('extremely_high');
expect(totalBand(175)).toBe('extremely_high');
});
test('percentile croissant avec le score', () => {
const p1 = percentileOf(40, 'nonAutistic', 'total', 'f');
const p2 = percentileOf(90, 'nonAutistic', 'total', 'f');
const p3 = percentileOf(140, 'nonAutistic', 'total', 'f');
expect(p1).toBeLessThan(p2);
expect(p2).toBeLessThan(p3);
expect(p1).toBeGreaterThanOrEqual(1);
expect(p3).toBeLessThanOrEqual(99);
});
test('Φ normale : mu ± 1sd ≈ 84e percentile', () => {
const { mu, sd } = NORMS.nonAutistic.total.f;
expect(Math.round(normalCdf(mu + sd, mu, sd) * 100)).toBe(84);
expect(Math.round(normalCdf(mu, mu, sd) * 100)).toBe(50);
});
test('descripteur par tranche de percentile', () => {
expect(descriptorFromPercentile(3)).toBe('extremely_low');
expect(descriptorFromPercentile(15)).toBe('low');
expect(descriptorFromPercentile(50)).toBe('moderate');
expect(descriptorFromPercentile(85)).toBe('high');
expect(descriptorFromPercentile(97)).toBe('extremely_high');
});
test('genre non précisé → normes groupées (entre f et nb)', () => {
const pooled = percentileOf(90, 'nonAutistic', 'total', 'unspecified');
const f = percentileOf(90, 'nonAutistic', 'total', 'f');
const nb = percentileOf(90, 'nonAutistic', 'total', 'nb');
expect(pooled).toBeGreaterThanOrEqual(Math.min(f, nb));
expect(pooled).toBeLessThanOrEqual(Math.max(f, nb));
});
});
describe('persistence', () => {
beforeEach(() => { localStorage.clear(); });
test('writeState/readState/clearState roundtrip', () => {
expect(readState()).toBeNull();
writeState({ responses: { catq_1: 4 }, gender: 'nb', pos: 3, consented: true });
expect(readState()).toEqual({ responses: { catq_1: 4 }, gender: 'nb', pos: 3, consented: true });
clearState();
expect(readState()).toBeNull();
});
test('readState refuse les payloads non conformes', () => {
localStorage.setItem('catq:v1', 'not json');
expect(readState()).toBeNull();
localStorage.setItem('catq:v1', '{"nope":1}');
expect(readState()).toBeNull();
});
});
/* Clarté TSA — dictionnaires : parité fr/en, 25 items résolus dans les deux
langues, options Likert 1–7 complètes, zéro tiret cadratin, pas de
placeholder oublié dans les gabarits de résultats. */
import { dict as fr } from '@/components/tsa/i18n/fr';
import { dict as en } from '@/components/tsa/i18n/en';
import { CATQ_ITEMS, SUBSCALES, GENDERS } from '@/components/tsa/data';
describe('dictionary parity fr/en', () => {
test('mêmes clés dans les deux langues', () => {
const fk = Object.keys(fr).sort();
const ek = Object.keys(en).sort();
expect(fk).toEqual(ek);
});
test('aucune valeur vide', () => {
[fr, en].forEach((d) => {
Object.keys(d).forEach((k) => {
expect(typeof d[k]).toBe('string');
expect(d[k].length).toBeGreaterThan(0);
});
});
});
test('zéro tiret cadratin (convention maison : tiret demi-cadratin ou parenthèses)', () => {
[fr, en].forEach((d) => {
Object.keys(d).forEach((k) => {
expect(d[k]).not.toMatch(/—/);
});
});
});
});
describe('items', () => {
test('25 items, clés catq.q1..q25 résolues en fr et en', () => {
expect(CATQ_ITEMS).toHaveLength(25);
CATQ_ITEMS.forEach((q) => {
expect(fr[q.t]).toBeTruthy();
expect(en[q.t]).toBeTruthy();
});
});
test('exactement 5 items inversés : 3, 12, 19, 22, 24', () => {
const reversed = CATQ_ITEMS.filter((q) => q.reverse).map((q) => q.num);
expect(reversed).toEqual([3, 12, 19, 22, 24]);
});
test('subscales = l’instrument (9/8/8, aucun recouvrement)', () => {
expect(SUBSCALES.map((s) => s.items.length)).toEqual([9, 8, 8]);
const all = SUBSCALES.flatMap((s) => s.items);
expect(new Set(all).size).toBe(25);
expect([...all].sort((a, b) => a - b)).toEqual([...Array(25)].map((_, i) => i + 1));
});
test('options Likert 1–7 complètes dans les deux langues', () => {
[fr, en].forEach((d) => {
[1, 2, 3, 4, 5, 6, 7].forEach((v) => {
expect(d['catq.option.' + v]).toBeTruthy();
});
});
});
});
describe('copy integrity', () => {
test('gabarits résultats : placeholders tous substituables', () => {
const total = fr['results.summary.text'];
expect(total).toMatch(/\{score\}/);
expect(total).toMatch(/\{band\}/);
expect(total).toMatch(/\{pNA\}/);
expect(total).toMatch(/\{pA\}/);
expect(total).toMatch(/\{genre\}/);
expect(fr['results.sub.line']).toMatch(/\{name\}.*\{score\}.*\{max\}.*\{pNA\}.*\{pA\}.*\{band\}/);
});
test('les 4 options de genre résolues dans les deux langues', () => {
GENDERS.forEach((g) => {
expect(fr['home.gender.' + g]).toBeTruthy();
expect(en['home.gender.' + g]).toBeTruthy();
});
});
test('l’avertissement de traduction non validée figure dans le français', () => {
expect(fr['home.warning.li3']).toMatch(/pas été validée/);
});
});
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment