{"version":3,"sources":["../../src/fr/elision.ts","../../src/fr/index.ts"],"names":["splitPhrase","splitTrailingPunctuation","isCapitalized","capitalize","joinPhrase","registerLanguage"],"mappings":";;;;;AAUA,IAAM,iBAAA,GAAoB;AAAA,EACxB,MAAA;AAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA;AAAA,EACA,UAAA;AAAA,EACA,MAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,WAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,MAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,UAAA;AAAA,EACA,UAAA;AAAA;AAAA,EACA,UAAA;AAAA,EACA,OAAA;AAAA,EACA,OAAA;AAAA,EACA,aAAA;AAAA,EACA,SAAA;AAAA,EACA,QAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,MAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA,OAAA;AAAA,EACA,QAAA;AAAA,EACA,OAAA;AAAA,EACA,MAAA;AAAA;AAAA,EACA,MAAA;AAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAA;AAEA,IAAM,yBAAS,IAAI,GAAA,CAAI,CAAC,GAAG,0IAA4C,CAAC,CAAA;AAGjE,SAAS,UAAU,IAAA,EAAuB;AAC/C,EAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,EAAY;AAC/B,EAAA,OAAO,kBAAkB,IAAA,CAAK,CAAC,MAAM,KAAA,CAAM,UAAA,CAAW,CAAC,CAAC,CAAA;AAC1D;AAOO,SAAS,OAAO,IAAA,EAAuB;AAC5C,EAAA,MAAM,KAAA,GAAQ,KAAK,CAAC,CAAA;AACpB,EAAA,IAAI,KAAA,KAAU,QAAW,OAAO,KAAA;AAChC,EAAA,IAAI,KAAA,KAAU,GAAA,IAAO,KAAA,KAAU,GAAA,EAAK,OAAO,KAAA;AAC3C,EAAA,IAAI,MAAA,CAAO,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,IAAA;AAC9B,EAAA,IAAI,UAAU,GAAA,IAAO,KAAA,KAAU,KAAK,OAAO,CAAC,UAAU,IAAI,CAAA;AAC1D,EAAA,OAAO,KAAA;AACT;;;AClEA,IAAM,UAAA,mBAAa,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,YAAY,QAAA,EAAU,UAAA,EAAY,YAAA,EAAW,UAAO,CAAC,CAAA;AAExF,IAAM,UAAA,uBAAiB,GAAA,CAAI;AAAA,EACzB,MAAA;AAAA,EACA,QAAA;AAAA,EACA,UAAA;AAAA,EACA,WAAA;AAAA,EACA,SAAA;AAAA,EACA,SAAA;AAAA,EACA;AACF,CAAC,CAAA;AAED,IAAM,UAAA,uBAAiB,GAAA,CAAI,CAAC,QAAQ,MAAA,EAAQ,QAAA,EAAU,QAAQ,CAAC,CAAA;AAGxD,SAAS,UAAU,IAAA,EAAsB;AAC9C,EAAA,MAAM,KAAA,GAAQ,KAAK,WAAA,EAAY;AAC/B,EAAA,IAAI,QAAA,CAAS,IAAA,CAAK,KAAK,CAAA,EAAG,OAAO,IAAA;AACjC,EAAA,IAAI,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,GAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,GAAA,CAAA;AACtD,EAAA,IAAI,KAAA,CAAM,QAAA,CAAS,IAAI,CAAA,IAAK,CAAC,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,SAAU,CAAA,EAAG,IAAA,CAAK,KAAA,CAAM,CAAA,EAAG,EAAE,CAAC,CAAA,GAAA,CAAA;AAC/E,EAAA,IAAI,cAAA,CAAe,IAAA,CAAK,KAAK,CAAA,IAAK,CAAC,UAAA,CAAW,GAAA,CAAI,KAAK,CAAA,EAAG,OAAO,CAAA,EAAG,IAAI,CAAA,CAAA,CAAA;AACxE,EAAA,OAAO,GAAG,IAAI,CAAA,CAAA,CAAA;AAChB;AAGA,IAAM,gBAAA,GAAmB,iEAAA;AAOzB,SAAS,aAAA,CAAc,UAA+B,QAAA,EAA+B;AACnF,EAAA,IAAI,QAAA,CAAS,QAAQ,OAAO,EAAE,QAAQ,QAAA,CAAS,MAAA,EAAQ,SAAS,KAAA,EAAM;AACtE,EAAA,MAAM,KAAA,GAAQ,SAAS,WAAA,EAAY;AACnC,EAAA,OAAO;AAAA,IACL,MAAA,EAAQ,gBAAA,CAAiB,IAAA,CAAK,KAAK,IAAI,UAAA,GAAa,WAAA;AAAA,IACpD,OAAA,EAAS;AAAA,GACX;AACF;AAEA,IAAM,QAAA,uBAAe,GAAA,CAAI,CAAC,MAAM,IAAA,EAAM,KAAA,EAAO,IAAI,CAAC,CAAA;AAClD,IAAM,6BAAa,IAAI,GAAA,CAAI,CAAC,IAAA,EAAM,KAAA,EAAO,KAAK,CAAC,CAAA;AAE/C,SAAS,UAAA,CACP,IAAA,EACA,MAAA,EACA,MAAA,EACA,QAAA,EACQ;AACR,EAAA,IAAI,SAAS,YAAA,EAAc;AACzB,IAAA,OAAO,MAAA,GAAS,KAAA,GAAQ,MAAA,KAAW,UAAA,GAAa,KAAA,GAAQ,IAAA;AAAA,EAC1D;AACA,EAAA,IAAI,QAAQ,OAAO,KAAA;AACnB,EAAA,IAAI,MAAA,CAAO,QAAQ,CAAA,EAAG,OAAO,IAAA;AAC7B,EAAA,OAAO,MAAA,KAAW,aAAa,IAAA,GAAO,IAAA;AACxC;AAEA,SAAS,aAAA,CACP,MAAA,EACA,QAAA,EACA,GAAA,EACkB;AAClB,EAAA,MAAM,KAAA,GAAQA,8BAAY,MAAM,CAAA;AAChC,EAAA,IAAI,KAAA,CAAM,MAAM,MAAA,KAAW,CAAA,SAAU,EAAE,IAAA,EAAM,MAAA,EAAQ,UAAA,EAAY,MAAA,EAAO;AAExE,EAAA,MAAM,MAAA,GAAS,SAAS,MAAA,KAAW,QAAA;AACnC,EAAA,IAAI,UAAA,GAA6B,MAAA;AAGjC,EAAA,IAAI,MAAA,EAAQ;AACV,IAAA,MAAM,UAAU,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,KAAA,CAAM,SAAS,CAAC,CAAA;AACxD,IAAA,MAAM,CAAC,MAAM,KAAK,CAAA,GAAIC,2CAAyB,KAAA,CAAM,KAAA,CAAM,OAAO,CAAW,CAAA;AAC7E,IAAA,KAAA,CAAM,KAAA,CAAM,OAAO,CAAA,GAAI,SAAA,CAAU,IAAI,CAAA,GAAI,KAAA;AAAA,EAC3C;AAGA,EAAA,MAAM,QAAA,GAAW,KAAA,CAAM,WAAA,CAAY,CAAC,CAAA;AACpC,EAAA,MAAM,SAAA,GAAY,KAAA,CAAM,KAAA,CAAM,QAAQ,CAAA;AACtC,EAAA,MAAM,UAAA,GAAa,UAAU,WAAA,EAAY;AACzC,EAAA,MAAM,YAAA,GAAe,QAAA,CAAS,GAAA,CAAI,UAAU,CAAA,GACxC,aACA,UAAA,CAAW,GAAA,CAAI,UAAU,CAAA,GACvB,YAAA,GACA,MAAA;AAEN,EAAA,MAAM,WAAW,MAAc;AAC7B,IAAA,MAAM,MAAM,KAAA,CAAM,WAAA,CAAY,KAAA,CAAM,KAAA,CAAM,SAAS,CAAC,CAAA;AACpD,IAAA,MAAM,CAAC,IAAI,CAAA,GAAIA,0CAAA,CAAyB,GAAA,KAAQ,SAAY,EAAA,GAAM,KAAA,CAAM,KAAA,CAAM,GAAG,CAAY,CAAA;AAC7F,IAAA,OAAO,IAAA;AAAA,EACT,CAAA;AACA,EAAA,MAAM,uBAAuB,MAAc;AACzC,IAAA,MAAM,GAAA,GAAM,KAAA,CAAM,WAAA,CAAY,CAAC,CAAA;AAC/B,IAAA,OAAO,GAAA,KAAQ,MAAA,GAAY,EAAA,GAAM,KAAA,CAAM,MAAM,GAAG,CAAA;AAAA,EAClD,CAAA;AAEA,EAAA,MAAM,YAAA,GAAe,CAAC,IAAA,EAAiC,OAAA,KAA2B;AAChF,IAAA,MAAM,KAAA,GAAQ,aAAA,CAAc,QAAA,EAAU,QAAA,EAAU,CAAA;AAChD,IAAA,IAAI,KAAA,CAAM,OAAA,IAAW,CAAC,MAAA,IAAU,EAAE,SAAS,UAAA,IAAc,MAAA,CAAO,oBAAA,EAAsB,CAAA,CAAA,EAAI;AACxF,MAAA,GAAA,CAAI,IAAA,CAAK,kBAAkB,CAAA,QAAA,EAAW,KAAA,CAAM,MAAM,CAAA,MAAA,EAAS,QAAA,EAAU,CAAA,CAAA,CAAG,CAAA;AACxE,MAAA,UAAA,GAAa,KAAA;AAAA,IACf;AACA,IAAA,MAAM,IAAA,GAAO,OAAA,GAAU,oBAAA,EAAqB,GAAI,SAAA;AAChD,IAAA,MAAM,UAAU,UAAA,CAAW,IAAA,EAAM,KAAA,CAAM,MAAA,EAAQ,QAAQ,IAAI,CAAA;AAC3D,IAAA,IAAI,OAAA,EAAS;AACX,MAAA,MAAM,GAAA,GAAMC,gCAAc,SAAS,CAAA;AACnC,MAAA,KAAA,CAAM,MAAM,QAAQ,CAAA,GAAI,GAAA,GAAMC,4BAAA,CAAW,OAAO,CAAA,GAAI,OAAA;AACpD,MAAA,IAAI,OAAA,KAAY,IAAA,IAAQ,KAAA,CAAM,WAAA,CAAY,SAAS,CAAA,EAAG;AACpD,QAAA,KAAA,CAAM,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA,GAAI,EAAA;AAAA,MAC9B;AAAA,IACF,CAAA,MAAA,IAAW,YAAY,IAAA,EAAM;AAC3B,MAAA,KAAA,CAAM,KAAA,CAAM,QAAQ,CAAA,GAAI,CAAA,EAAA,EAAK,SAAS,CAAA,CAAA;AAAA,IACxC,CAAA,MAAO;AACL,MAAA,KAAA,CAAM,MAAM,QAAQ,CAAA,GAAI,CAAA,EAAG,OAAO,IAAI,SAAS,CAAA,CAAA;AAAA,IACjD;AAAA,EACF,CAAA;AAEA,EAAA,IAAI,QAAA,CAAS,OAAA,KAAY,MAAA,IAAU,YAAA,EAAc;AAC/C,IAAA,KAAA,CAAM,KAAA,CAAM,QAAQ,CAAA,GAAI,EAAA;AACxB,IAAA,IAAI,KAAA,CAAM,YAAY,MAAA,GAAS,CAAA,QAAS,KAAA,CAAM,QAAA,GAAW,CAAC,CAAA,GAAI,EAAA;AAAA,EAChE,WAAW,YAAA,EAAc;AACvB,IAAA,YAAA,CAAa,cAAc,IAAI,CAAA;AAAA,EACjC,WAAW,QAAA,CAAS,OAAA,KAAY,UAAA,IAAc,QAAA,CAAS,YAAY,YAAA,EAAc;AAC/E,IAAA,YAAA,CAAa,QAAA,CAAS,SAAS,KAAK,CAAA;AAAA,EACtC;AAEA,EAAA,OAAO,EAAE,IAAA,EAAMC,4BAAA,CAAW,KAAK,GAAG,UAAA,EAAW;AAC/C;AAGO,IAAM,EAAA,GAAmB;AAAA,EAC9B,MAAA,EAAQ,IAAA;AAAA,EACR;AACF;AAEAC,kCAAA,CAAiB,EAAE,CAAA","file":"index.cjs","sourcesContent":["/**\n * French elision: `le`/`la`/`de` become `l'`/`d'` before a vowel or mute h —\n * unless the word begins with an *h aspiré*, which blocks elision\n * (\"le haricot\", never \"l'haricot\").\n */\n\n/**\n * Common h-aspiré word stems (prefix-matched, lowercase). Curated from\n * standard dictionaries; extend via the data pipeline if gaps show up.\n */\nconst H_ASPIRE_PREFIXES = [\n  \"hach\", // hache, hacher\n  \"haie\",\n  \"haine\",\n  \"hair\", // haïr (unaccented fold)\n  \"haïss\",\n  \"hall\",\n  \"halo\",\n  \"halte\",\n  \"hamac\",\n  \"hamburger\",\n  \"hameau\",\n  \"hamster\",\n  \"hanche\",\n  \"handicap\",\n  \"hangar\",\n  \"hanter\",\n  \"harcel\",\n  \"hard\",\n  \"hareng\",\n  \"hargne\",\n  \"haricot\",\n  \"harnais\",\n  \"harpe\",\n  \"hasard\",\n  \"hât\", // hâte\n  \"hauss\", // hausse\n  \"haut\",\n  \"havre\",\n  \"hérisson\",\n  \"héron\",\n  \"héros\", // but: l'héroïne — prefix stops at \"héros\"\n  \"hêtre\",\n  \"heurt\",\n  \"hibou\",\n  \"hiérarch\",\n  \"hip-hop\",\n  \"hippie\",\n  \"hockey\",\n  \"hollande\",\n  \"homard\",\n  \"hongr\", // Hongrie, hongrois\n  \"honte\",\n  \"hoquet\",\n  \"hors\",\n  \"hot-dog\",\n  \"houblon\",\n  \"houle\",\n  \"housse\",\n  \"huche\",\n  \"huit\", // le huit, le huitième\n  \"hurl\", // hurler, hurlement\n  \"hussard\",\n  \"hutte\",\n] as const;\n\nconst VOWELS = new Set([...\"aàâäeéèêëiîïoôöœuùûüyAÀÂÄEÉÈÊËIÎÏOÔÖŒUÙÛÜY\"]);\n\n/** True when `word` starts with an h aspiré (blocks elision and liaison). */\nexport function isHAspire(word: string): boolean {\n  const lower = word.toLowerCase();\n  return H_ASPIRE_PREFIXES.some((p) => lower.startsWith(p));\n}\n\n/**\n * True when the article before `word` elides: the word starts with a vowel,\n * or with a mute `h` (i.e. an `h` that is not aspiré). `y` is treated as a\n * consonant (\"le yaourt\").\n */\nexport function elides(word: string): boolean {\n  const first = word[0];\n  if (first === undefined) return false;\n  if (first === \"y\" || first === \"Y\") return false;\n  if (VOWELS.has(first)) return true;\n  if (first === \"h\" || first === \"H\") return !isHAspire(word);\n  return false;\n}\n","import type { GrammaticalFeatures, GrammaticalGender } from \"../core/features.js\";\nimport type { InflectionContext, InflectionResult, LanguagePack } from \"../core/pack.js\";\nimport {\n  capitalize,\n  isCapitalized,\n  joinPhrase,\n  splitPhrase,\n  splitTrailingPunctuation,\n} from \"../core/phrase.js\";\nimport { registerLanguage } from \"../core/registry.js\";\nimport { elides } from \"./elision.js\";\n\n/**\n * French language pack: definite/indefinite articles with elision\n * (le/la/les/l'/un/une/des), h-aspiré handling, and noun pluralization.\n *\n * `import \"i18n-inflect/fr\"` registers it as a side effect.\n */\n\n/** `-al` nouns that pluralize regularly (+s) instead of `-aux`. */\nconst AL_REGULAR = new Set([\"bal\", \"carnaval\", \"chacal\", \"festival\", \"récital\", \"régal\"]);\n/** `-ail` nouns that take `-aux` (the default for `-ail` is +s). */\nconst AIL_TO_AUX = new Set([\n  \"bail\",\n  \"corail\",\n  \"émail\",\n  \"soupirail\",\n  \"travail\",\n  \"vantail\",\n  \"vitrail\",\n]);\n/** `-eu`/`-au` nouns that take +s instead of +x. */\nconst EU_REGULAR = new Set([\"bleu\", \"pneu\", \"landau\", \"sarrau\"]);\n\n/** Pluralize a French noun (regular patterns + closed exception classes). */\nexport function pluralize(word: string): string {\n  const lower = word.toLowerCase();\n  if (/[sxz]$/.test(lower)) return word;\n  if (AIL_TO_AUX.has(lower)) return `${word.slice(0, -3)}aux`;\n  if (lower.endsWith(\"al\") && !AL_REGULAR.has(lower)) return `${word.slice(0, -2)}aux`;\n  if (/(eau|au|eu)$/.test(lower) && !EU_REGULAR.has(lower)) return `${word}x`;\n  return `${word}s`;\n}\n\n/** Feminine noun-ending heuristic, used only when no gender is supplied. */\nconst FEMININE_ENDINGS = /(tion|sion|té|ance|ence|ure|elle|ette|euse|ienne|ade|aison|ie)$/;\n\ninterface GenderGuess {\n  gender: GrammaticalGender;\n  guessed: boolean;\n}\n\nfunction resolveGender(features: GrammaticalFeatures, headNoun: string): GenderGuess {\n  if (features.gender) return { gender: features.gender, guessed: false };\n  const lower = headNoun.toLowerCase();\n  return {\n    gender: FEMININE_ENDINGS.test(lower) ? \"feminine\" : \"masculine\",\n    guessed: true,\n  };\n}\n\nconst DEFINITE = new Set([\"le\", \"la\", \"les\", \"l'\"]);\nconst INDEFINITE = new Set([\"un\", \"une\", \"des\"]);\n\nfunction articleFor(\n  kind: \"definite\" | \"indefinite\",\n  gender: GrammaticalGender,\n  plural: boolean,\n  nextWord: string,\n): string {\n  if (kind === \"indefinite\") {\n    return plural ? \"des\" : gender === \"feminine\" ? \"une\" : \"un\";\n  }\n  if (plural) return \"les\";\n  if (elides(nextWord)) return \"l'\";\n  return gender === \"feminine\" ? \"la\" : \"le\";\n}\n\nfunction inflectPhrase(\n  phrase: string,\n  features: GrammaticalFeatures,\n  ctx: InflectionContext,\n): InflectionResult {\n  const split = splitPhrase(phrase);\n  if (split.words.length === 0) return { text: phrase, confidence: \"high\" };\n\n  const plural = features.number === \"plural\";\n  let confidence: \"high\" | \"low\" = \"high\";\n\n  // 1. Pluralize the head (last word).\n  if (plural) {\n    const headIdx = split.wordIndexes[split.words.length - 1] as number;\n    const [core, punct] = splitTrailingPunctuation(split.parts[headIdx] as string);\n    split.parts[headIdx] = pluralize(core) + punct;\n  }\n\n  // 2. Article agreement (existing article re-agreed, or one prepended).\n  const firstIdx = split.wordIndexes[0] as number;\n  const firstWord = split.parts[firstIdx] as string;\n  const firstLower = firstWord.toLowerCase();\n  const existingKind = DEFINITE.has(firstLower)\n    ? \"definite\"\n    : INDEFINITE.has(firstLower)\n      ? \"indefinite\"\n      : undefined;\n\n  const headWord = (): string => {\n    const idx = split.wordIndexes[split.words.length - 1];\n    const [core] = splitTrailingPunctuation(idx === undefined ? \"\" : (split.parts[idx] as string));\n    return core;\n  };\n  const nextWordAfterArticle = (): string => {\n    const idx = split.wordIndexes[1];\n    return idx === undefined ? \"\" : (split.parts[idx] as string);\n  };\n\n  const applyArticle = (kind: \"definite\" | \"indefinite\", replace: boolean): void => {\n    const guess = resolveGender(features, headWord());\n    if (guess.guessed && !plural && !(kind === \"definite\" && elides(nextWordAfterArticle()))) {\n      ctx.warn(\"missing-gender\", `guessed ${guess.gender} for \"${headWord()}\"`);\n      confidence = \"low\";\n    }\n    const next = replace ? nextWordAfterArticle() : firstWord;\n    const article = articleFor(kind, guess.gender, plural, next);\n    if (replace) {\n      const cap = isCapitalized(firstWord);\n      split.parts[firstIdx] = cap ? capitalize(article) : article;\n      if (article === \"l'\" && split.wordIndexes.length > 1) {\n        split.parts[firstIdx + 1] = \"\"; // l' attaches directly: \"l'ami\"\n      }\n    } else if (article === \"l'\") {\n      split.parts[firstIdx] = `l'${firstWord}`;\n    } else {\n      split.parts[firstIdx] = `${article} ${firstWord}`;\n    }\n  };\n\n  if (features.article === \"none\" && existingKind) {\n    split.parts[firstIdx] = \"\";\n    if (split.wordIndexes.length > 1) split.parts[firstIdx + 1] = \"\";\n  } else if (existingKind) {\n    applyArticle(existingKind, true);\n  } else if (features.article === \"definite\" || features.article === \"indefinite\") {\n    applyArticle(features.article, false);\n  }\n\n  return { text: joinPhrase(split), confidence };\n}\n\n/** The French language pack. */\nexport const fr: LanguagePack = {\n  locale: \"fr\",\n  inflectPhrase,\n};\n\nregisterLanguage(fr);\n"]}