diff --git a/Rules/Languages/fr/SharedRules/calculus.yaml b/Rules/Languages/fr/SharedRules/calculus.yaml
new file mode 100644
index 00000000..0ee45898
--- /dev/null
+++ b/Rules/Languages/fr/SharedRules/calculus.yaml
@@ -0,0 +1,56 @@
+---
+
+- name: laplacian
+ tag: laplacian
+ match: "count(*) <= 1"
+ replace:
+ - t: "laplacien"
+ - test:
+ if: "count(*) = 1"
+ then:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [pause: short]
+ - x: "*[1]"
+
+- name: divergence
+ tag: divergence
+ match: "count(*) = 1"
+ replace:
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "div"]
+ else: [t: "divergence de"]
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [pause: short]
+ - x: "*[1]"
+
+- name: curl
+ tag: curl
+ match: "count(*) = 1"
+ replace:
+ - t: "rotationnel"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [pause: short]
+ - x: "*[1]"
+
+- name: gradient
+ tag: gradient
+ match: "count(*) = 1"
+ replace:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "gradient de"]
+ else: [t: "del"]
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [pause: short]
+ - x: "*[1]"
diff --git a/Rules/Languages/fr/SharedRules/default.yaml b/Rules/Languages/fr/SharedRules/default.yaml
new file mode 100644
index 00000000..7f38a389
--- /dev/null
+++ b/Rules/Languages/fr/SharedRules/default.yaml
@@ -0,0 +1,414 @@
+---
+- name: default
+ tag: math
+ match: "."
+ replace:
+ - with:
+ variables:
+ - ClearSpeak_Fractions: "IfThenElse($Verbosity='Verbose' and $ClearSpeak_Fractions='Auto', 'EndFrac', $ClearSpeak_Fractions)"
+ - ClearSpeak_AbsoluteValue: "IfThenElse($Verbosity='Verbose' and $ClearSpeak_AbsoluteValue='Auto', 'AbsEnd', $ClearSpeak_AbsoluteValue)"
+ - ClearSpeak_Roots: "IfThenElse($Verbosity='Verbose' and $ClearSpeak_Roots='Auto', 'RootEnd', $ClearSpeak_Roots)"
+ - ClearSpeak_Matrix: "IfThenElse($Verbosity='Verbose' and $ClearSpeak_Matrix='Auto', 'EndMatrix', $ClearSpeak_Matrix)"
+ - MatchingPause: false()
+ - IsColumnSilent: false()
+ replace:
+ - test:
+ if: "$MathRate = 100"
+ then: [x: "*"]
+ else:
+ - rate:
+ value: "$MathRate"
+ replace: [x: "*"]
+
+- name: empty-mrow
+ tag: mrow
+ match: "not(*)"
+ replace:
+ - t: " "
+
+- name: default
+ tag: mrow
+ match: "."
+ replace:
+ - insert:
+ nodes: "*"
+ replace: [pause: auto]
+
+- name: default
+ tag: mn
+ match: "."
+ replace:
+ - bookmark: "@id"
+ - x: "translate(., $BlockSeparators, '')"
+
+- name: default
+ tag: [mo, mtext]
+ match: "."
+ replace:
+ - bookmark: "@id"
+ - x: "text()"
+
+- name: default
+ tag: mi
+ match: "."
+ replace:
+ - bookmark: "@id"
+ - test:
+ - if: "string-length(.) = 1 and text() != '_'"
+ then: [x: "text()"]
+ - else_if: "@data-chem-element"
+ then: [spell: "text()", pause: "short"]
+ else: [x: "translate(., '-_\u00A0', ' ')"]
+
+- name: default
+ tag: ms
+ match: "."
+ replace:
+ - t: "la chaîne"
+ - pause: short
+ - x: "text()"
+
+- name: default
+ tag: mstyle
+ match: "."
+ replace: [x: "*"]
+
+- name: literal-simple
+ tag: mfrac
+ match:
+ - "(IsNode(*[1],'leaf') and IsNode(*[2],'leaf')) and"
+ - "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)"
+ replace:
+ - x: "*[1]"
+ - t: "sur"
+ - x: "*[2]"
+ - pause: short
+
+- name: literal-default
+ tag: mfrac
+ match: "."
+ replace:
+ - t: "début"
+ - pause: short
+ - x: "*[1]"
+ - test:
+ if: "not(IsNode(*[1],'leaf'))"
+ then: [pause: short]
+ - t: "sur"
+ - test:
+ if: "not(IsNode(*[2],'leaf'))"
+ then: [pause: short]
+ - x: "*[2]"
+ - pause: short
+ - test:
+ if: "$Impairment = 'Blindness'"
+ then: [t: "fin"]
+ - pause: medium
+
+- name: literal-default
+ tag: msqrt
+ match: "."
+ replace:
+ - t: "racine"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - x: "*[1]"
+ - pause: short
+ - test:
+ if: "not(IsNode(*[1],'leaf')) or $Impairment = 'Blindness'"
+ then: [t: "fin de racine", pause: medium]
+
+- name: literal-default
+ tag: mroot
+ match: "."
+ replace:
+ - t: "racine d'indice"
+ - x: "*[2]"
+ - pause: short
+ - t: "de"
+ - x: "*[1]"
+ - pause: short
+ - test:
+ if: "not(IsNode(*[2],'leaf'))"
+ then: [t: "fin de racine"]
+
+- name: simple-sub
+ tag: indexed-by
+ match: "count(*)=2 and $Verbosity='Terse' and *[2][self::m:mn and translate(., '.,', '')=.]"
+ replace:
+ - x: "*[1]"
+ - x: "*[2]"
+ - pause: short
+
+- name: no-end-sub
+ tag: indexed-by
+ match: "count(*)=2 and *[2][self::m:mrow and *[2][.='']]"
+ replace:
+ - x: "*[1]"
+ - t: "indice"
+ - x: "*[2]"
+ - pause: short
+
+- name: power-indexed-by
+ tag: power-indexed-by
+ match: "."
+ replace:
+ - x: "*[1]"
+ - t: "indice"
+ - x: "*[2]"
+ - pause: short
+
+- name: literal
+ tag: msub
+ match: "."
+ replace:
+ - x: "*[1]"
+ - test:
+ if: "not($Verbosity='Terse' and *[2][self::m:mn and not(translate(., '.,', '')!=.)])"
+ then: [t: "indice"]
+ - x: "*[2]"
+
+- name: literal
+ tag: [msup, msubsup]
+ match: "."
+ replace:
+ - x: "*[1]"
+ - test:
+ if: "name(.)='msubsup'"
+ then:
+ - t: "indice"
+ - x: "*[2]"
+ - test:
+ if: "*[last()][translate(., '′″‴⁗†‡°*', '')='']"
+ then: [x: "*[last()]"]
+ else_test:
+ if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]"
+ then:
+ - t: "exposant"
+ - x: "*[last()]"
+ - test:
+ if: "not(IsNode(*[last()], 'simple')) or $Impairment = 'Blindness'"
+ then: [t: "fin d'exposant"]
+ else:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then: [t: "exposant"]
+ else: [t: "super"]
+ - x: "*[last()]"
+ - test:
+ if: "$Verbosity='Verbose'"
+ then: [t: "fin d'exposant"]
+ else: [t: "fin de super"]
+
+- name: default
+ tag: munder
+ match: "."
+ replace:
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [t: "quantité"]
+ - x: "*[1]"
+ - t: "avec"
+ - x: "*[2]"
+ - t: "en dessous"
+
+- name: diacriticals
+ tag: mover
+ match: "*[1][self::m:mi] and *[2][translate(., '\u0306\u030c.\u00A8\u02D9\u20DB\u20DC`^ˇ~→¯_', '')='']"
+ replace:
+ - x: "*[1]"
+ - x: "*[2]"
+
+- name: default
+ tag: mover
+ match: "."
+ replace:
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [t: "quantité"]
+ - x: "*[1]"
+ - t: "avec"
+ - x: "*[2]"
+ - t: "au-dessus"
+
+- name: default
+ tag: munderover
+ match: "."
+ replace:
+ - test:
+ if: "not(IsNode(*[1], 'leaf'))"
+ then: [t: "quantité"]
+ - x: "*[1]"
+ - t: "avec"
+ - x: "*[2]"
+ - t: "en dessous et"
+ - x: "*[3]"
+ - t: "au-dessus"
+
+- name: default
+ tag: mmultiscripts
+ match: "."
+ variables:
+ - Prescripts: "m:mprescripts/following-sibling::*"
+ - NumChildren: "count(*)"
+ - Postscripts: "*[position()>1 and position() < (last() + ($NumChildren mod 2) -count($Prescripts))]"
+ replace:
+ - x: "*[1]"
+ - test:
+ if: "$Prescripts"
+ then:
+ - with:
+ variables:
+ - PreSubscript: "IfThenElse($Verbosity='Verbose', 'pré-indice', 'pré-indice')"
+ - PreSuperscript: "IfThenElse($Verbosity='Verbose', 'pré-exposant', 'pré-exposant')"
+ replace:
+ - test:
+ if: "count($Prescripts) > 2"
+ then:
+ - t: "avec"
+ - x: "count($Prescripts) div 2"
+ - t: "préscripts"
+ - pause: short
+ - test:
+ if: "not($Prescripts[1][self::m:none])"
+ then:
+ - x: "$PreSubscript"
+ - x: "$Prescripts[1]"
+ - test:
+ if: "not($Prescripts[1][self::m:none] or $Prescripts[2][self::m:none])"
+ then: [t: "et"]
+ - test:
+ if: "not($Prescripts[2][self::m:none])"
+ then:
+ - x: "$PreSuperscript"
+ - x: "$Prescripts[2]"
+ - test:
+ if: "$Postscripts"
+ then:
+ - with:
+ variables:
+ - PostSubscript: "IfThenElse($Verbosity='Verbose', 'indice', 'indice')"
+ - PostSuperscript: "IfThenElse($Verbosity='Verbose', 'exposant', 'exposant')"
+ replace:
+ - test:
+ if: "count($Postscripts) > 2"
+ then:
+ - test:
+ if: "$Prescripts"
+ then: [t: "et"]
+ - t: "avec"
+ - x: "count($Postscripts) div 2"
+ - t: "postscripts"
+ - pause: short
+ - test:
+ if: "not($Postscripts[1][self::m:none])"
+ then:
+ - x: "$PostSubscript"
+ - x: "$Postscripts[1]"
+ - test:
+ if: "not($Postscripts[1][self::m:none] or $Postscripts[2][self::m:none])"
+ then: [t: "et"]
+ - test:
+ if: "not($Postscripts[2][self::m:none])"
+ then:
+ - x: "$PostSuperscript"
+ - x: "$Postscripts[2]"
+
+- name: apply-function
+ tag: "apply-function"
+ match: .
+ replace:
+ - x: "*[1]"
+ - t: "de"
+ - pause: auto
+ - x: "*[position() > 1]"
+
+- name: silent-intent
+ tag: "*"
+ match: "count(*)>0 and contains(@data-intent-property, ':silent:')"
+ replace:
+ - x: "*"
+ - test:
+ if: "IsNode(., '2D')"
+ then: [pause: short]
+ else: [pause: auto]
+
+- name: prefix-intent
+ tag: "*"
+ match: "count(*)>0 and contains(@data-intent-property, ':prefix:')"
+ replace:
+ - x: "SpeakIntentName(name(.), $Verbosity, 'prefix')"
+ - x: "*"
+ - test:
+ if: "not( IsBracketed(., '', '') or IsNode(*[last()], 'simple') )"
+ then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'prefix', 'end')"]
+ - test:
+ if: "IsNode(., '2D')"
+ then: [pause: short]
+ else: [pause: auto]
+
+- name: postfix-intent
+ tag: "*"
+ match: "count(*)>0 and contains(@data-intent-property, ':postfix:')"
+ replace:
+ - test:
+ if: "$Impairment = 'Blindness' and not( IsBracketed(., '', '') or IsNode(*[1], 'simple') )"
+ then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'postfix', 'start')"]
+ - x: "*"
+ - x: "SpeakIntentName(name(.), $Verbosity, 'postfix')"
+
+- name: infix-intent
+ tag: "*"
+ match: "count(*)>0 and contains(@data-intent-property, ':infix:')"
+ replace:
+ - test:
+ if: "$Impairment = 'Blindness' and not( IsBracketed(., '', '') or IsNode(*[1], 'simple') )"
+ then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'infix', 'start')"]
+ - test:
+ if: "count(*) = 1"
+ then:
+ - x: "SpeakIntentName(name(.), $Verbosity, 'infix')"
+ - pause: auto
+ - x: "*[1]"
+ else:
+ - insert:
+ nodes: "*"
+ replace: [x: "SpeakIntentName(name(.), $Verbosity, 'infix')", pause: auto]
+ - test:
+ if: "$Impairment = 'Blindness' and not( IsBracketed(., '', '') or IsNode(*[last()], 'simple') )"
+ then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'infix', 'end')"]
+ - test:
+ if: "IsNode(., '2D')"
+ then: [pause: short]
+ else: [pause: auto]
+
+- name: function-intent
+ tag: "*"
+ match: "count(*)>0"
+ replace:
+ - x: "SpeakIntentName(name(.), $Verbosity, 'function')"
+ - test:
+ if: "$Verbosity != 'Terse' and not(contains(@data-intent-property, ':literal:')) and
+ not(count(*)=2 and (IsInDefinition(*[1], 'TrigFunctionNames') or IsInDefinition(name(.), 'TerseFunctionNames')) and IsNode(*[2], 'simple'))"
+ then: [t: "de", pause: auto]
+ - insert:
+ nodes: "*"
+ replace:
+ - test:
+ if: "not(contains(@data-intent-property, ':literal:'))"
+ then: [x: "','"]
+ - pause: auto
+ - test:
+ if: "$Impairment = 'Blindness' and not(*[last()][IsBracketed(., '', '') or IsNode(., 'simple')] )"
+ then: [x: "GetBracketingIntentName(name(.), $Verbosity, 'function', 'end')"]
+ - test:
+ if: "IsNode(., '2D')"
+ then: [pause: short]
+ else: [pause: auto]
+
+- name: default-text
+ tag: "*"
+ match: "."
+ replace:
+ - x: "translate(name(), '-_', ' ')"
diff --git a/Rules/Languages/fr/SharedRules/general.yaml b/Rules/Languages/fr/SharedRules/general.yaml
new file mode 100644
index 00000000..1c2e155f
--- /dev/null
+++ b/Rules/Languages/fr/SharedRules/general.yaml
@@ -0,0 +1,152 @@
+---
+- name: sin
+ tag: mi
+ match: ".='sin'"
+ replace:
+ - bookmark: "@id"
+ - t: "sinus"
+
+- name: cos
+ tag: mi
+ match: ".='cos'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "cos"]
+ else: [t: "cosinus"]
+
+- name: tan
+ tag: mi
+ match: ".='tan' or .='tg'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "tan"]
+ else: [t: "tangente"]
+
+- name: sec
+ tag: mi
+ match: ".='sec'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "sec"]
+ else: [t: "sécante"]
+
+- name: csc
+ tag: mi
+ match: ".='csc'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "csc"]
+ else: [t: "cosécante"]
+
+- name: cot
+ tag: mi
+ match: ".='cot'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "cot"]
+ else: [t: "cotangente"]
+
+- name: sinh
+ tag: mi
+ match: ".='sinh'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "sinh"]
+ else: [t: "sinus hyperbolique"]
+
+- name: cosh
+ tag: mi
+ match: ".='cosh'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "cosh"]
+ else: [t: "cosinus hyperbolique"]
+
+- name: tanh
+ tag: mi
+ match: ".='tanh'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "tanh"]
+ else: [t: "tangente hyperbolique"]
+
+- name: sech
+ tag: mi
+ match: ".='sech'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "sech"]
+ else: [t: "sécante hyperbolique"]
+
+- name: csch
+ tag: mi
+ match: ".='csch'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "csch"]
+ else: [t: "cosécante hyperbolique"]
+
+- name: coth
+ tag: mi
+ match: ".='coth'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "coth"]
+ else: [t: "cotangente hyperbolique"]
+
+- name: exponential
+ tag: mi
+ match: ".='exp'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "exp"]
+ else: [t: "exponentielle"]
+
+- name: covariance
+ tag: mi
+ match: ".='Cov'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ if: "$Verbosity='Terse'"
+ then: [t: "Cov"]
+ else: [t: "covariance"]
+
+- name: log
+ tag: mi
+ match: ".='log' or .='ln'"
+ replace:
+ - bookmark: "@id"
+ - test:
+ - if: "$Verbosity!='Terse'"
+ then: [t: "le"]
+ - test:
+ - if: ".= 'log'"
+ then: [t: "log"]
+ - else_if: "$Verbosity='Terse'"
+ then: [spell: "'ln'"]
+ else: [t: "logarithme naturel"]
diff --git a/Rules/Languages/fr/SharedRules/geometry.yaml b/Rules/Languages/fr/SharedRules/geometry.yaml
new file mode 100644
index 00000000..b5ab22a7
--- /dev/null
+++ b/Rules/Languages/fr/SharedRules/geometry.yaml
@@ -0,0 +1,78 @@
+---
+
+- name: line-segment
+ tag: line-segment
+ match: "count(*)=2"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then:
+ - t: "le segment de droite de"
+ - x: "*[1]"
+ - t: "à"
+ - x: "*[2]"
+ else:
+ - t: "segment"
+ - x: "*[1]"
+ - x: "*[2]"
+
+- name: geometry-ray
+ tag: ray
+ match: "count(*)=2"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then:
+ - t: "la demi-droite de"
+ - x: "*[1]"
+ - t: "à"
+ - x: "*[2]"
+ else:
+ - t: "demi-droite"
+ - x: "*[1]"
+ - x: "*[2]"
+
+- name: geometry-arc
+ tag: arc
+ match: "count(*)=2"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then: [t: "l'"]
+ - t: "arc"
+ - x: "*[1]"
+ - x: "*[2]"
+
+- name: measure-of-angle
+ tag: measure-of-angle
+ match: "count(*)=3"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then:
+ - t: "la mesure de l'angle"
+ else:
+ - t: "mesure de l'angle"
+ - x: "*[1]"
+ - x: "*[2]"
+ - x: "*[3]"
+
+- name: coordinate
+ tag: coordinate
+ match: "."
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then: [t: "le"]
+ - t: "point"
+ - test:
+ if: "$Verbosity='Verbose'"
+ then: [t: "de coordonnées"]
+ - pause: short
+ - insert:
+ nodes: "*"
+ replace: [t: "virgule", pause: auto]
+ - pause: short
+ - test:
+ if: "($SpeechStyle='ClearSpeak' and $Verbosity='Verbose') or not(IsNode(*[last()],'leaf'))"
+ then: [t: "fin du point"]
diff --git a/Rules/Languages/fr/SharedRules/linear-algebra.yaml b/Rules/Languages/fr/SharedRules/linear-algebra.yaml
new file mode 100644
index 00000000..ce8a03dc
--- /dev/null
+++ b/Rules/Languages/fr/SharedRules/linear-algebra.yaml
@@ -0,0 +1,35 @@
+---
+
+- name: scalar-determinant
+ tag: determinant
+ match: "count(*)=1 and not(*[1][self::m:mtr])"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then:
+ - t: "le"
+ - t: "déterminant"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then:
+ - t: "de"
+ - x: "*[1]"
+ - test:
+ if: "not(IsNode(*[1], 'simple')) and $Impairment = 'Blindness'"
+ then: [t: "fin du déterminant"]
+
+- name: subscripted-norm
+ tag: subscripted-norm
+ match: "count(*)=2"
+ replace:
+ - test:
+ if: "$Verbosity='Verbose'"
+ then:
+ - t: "la"
+ - x: "*[2]"
+ - t: "norme"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then:
+ - t: "de"
+ - x: "*[1]"
diff --git a/Rules/Languages/fr/SimpleSpeak_Rules.yaml b/Rules/Languages/fr/SimpleSpeak_Rules.yaml
new file mode 100644
index 00000000..59a8ec54
--- /dev/null
+++ b/Rules/Languages/fr/SimpleSpeak_Rules.yaml
@@ -0,0 +1,308 @@
+---
+- name: pause
+ tag: "!*"
+ match: "not(self::m:math) and not($MatchingPause) and @data-intent-property[contains(., ':pause')]"
+ replace:
+ - with:
+ variables: [MatchingPause: "true()"]
+ replace:
+ - test:
+ - if: "contains(@data-intent-property, ':pause-long')"
+ then: [pause: long]
+ - else_if: "contains(@data-intent-property, ':pause-short')"
+ then: [pause: short]
+ else: [pause: medium]
+ - x: "."
+
+- name: intent-literal-silent
+ tag: [mi, mo, mn]
+ match: "contains(@data-intent-property, ':silent:')"
+ replace: []
+
+- name: intent-literal-negative-number
+ tag: mn
+ match: "starts-with(text(), '-')"
+ replace:
+ - t: "moins"
+ - x: "translate(text(), '-_', '')"
+
+- name: default
+ tag: square-root
+ match: "."
+ replace:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "la"]
+ - t: "racine carrée"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ else: [pause: short]
+ - x: "*[1]"
+ - pause: short
+ - test:
+ if: "not(IsNode(*[1], 'leaf')) and $Impairment = 'Blindness'"
+ then: [t: "fin de racine", pause: medium]
+
+- name: default
+ tag: root
+ match: "."
+ replace:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "la"]
+ - test:
+ if: "*[2][self::m:mn and not(contains(., '.'))]"
+ then_test:
+ - if: "*[2][.='2']"
+ then: [t: "racine carrée"]
+ - else_if: "*[2][.='3']"
+ then: [t: "racine cubique"]
+ - else:
+ - x: "*[2]"
+ - t: "ième racine"
+ else:
+ - x: "*[2]"
+ - t: "racine"
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - x: "*[1]"
+ - pause: short
+ - test:
+ if: "not(IsNode(*[1], 'leaf')) and $Impairment = 'Blindness'"
+ then: [t: "fin de racine", pause: medium]
+
+- name: common-fraction
+ tag: fraction
+ match:
+ - "*[1][self::m:mn][not(contains(., '.')) and text()<20] and"
+ - "*[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]"
+ variables: [IsPlural: "*[1]!=1"]
+ replace:
+ - x: "*[1]"
+ - x: "ToOrdinal(*[2], true(), $IsPlural)"
+
+- name: common-fraction-mixed-number
+ tag: fraction
+ match:
+ - "preceding-sibling::*[1][self::m:mo][.=''] and"
+ - "*[1][self::m:mn][not(contains(., '.'))] and"
+ - "*[2][self::m:mn][not(contains(., '.'))]"
+ variables: [IsPlural: "*[1]!=1"]
+ replace:
+ - x: "*[1]"
+ - x: "ToOrdinal(*[2], true(), $IsPlural)"
+
+- name: per-fraction
+ tag: fraction
+ match:
+ - "BaseNode(*[1])[contains(@data-intent-property, ':unit') or"
+ - " ( self::m:mrow and count(*)=3 and"
+ - " *[1][self::m:mn] and *[2][.='\u2062'] and BaseNode(*[3])[contains(@data-intent-property, ':unit')] ) ] and"
+ - "BaseNode(*[2])[contains(@data-intent-property, ':unit')] "
+ replace:
+ - x: "*[1]"
+ - t: "par"
+ - x: "*[2]"
+
+- name: simple
+ tag: fraction
+ match:
+ - "(IsNode(*[1],'leaf') and IsNode(*[2],'leaf')) and"
+ - "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)"
+ replace:
+ - x: "*[1]"
+ - t: "sur"
+ - x: "*[2]"
+ - pause: short
+
+- name: default
+ tag: fraction
+ match: "."
+ replace:
+ - test:
+ if: "$Impairment = 'Blindness'"
+ then: [t: "fraction"]
+ - pause: short
+ - x: "*[1]"
+ - test:
+ if: "not(IsNode(*[1],'leaf'))"
+ then: [pause: short]
+ - t: "sur"
+ - test:
+ if: "not(IsNode(*[2],'leaf'))"
+ then: [pause: short]
+ - x: "*[2]"
+ - pause: short
+ - test:
+ if: "$Impairment = 'Blindness'"
+ then: [t: "fin de fraction"]
+ - pause: medium
+
+- name: inverse-function
+ tag: inverse-function
+ match: "."
+ replace:
+ - t: "inverse"
+ - x: "*[1]"
+
+- name: function-squared-or-cubed
+ tag: power
+ match:
+ - "*[2][self::m:mn][.='2' or .='3'] and"
+ - "following-sibling::*[1][self::m:mo][.='']"
+ replace:
+ - x: "*[1]"
+ - bookmark: "*[2]/@id"
+ - test:
+ if: "*[2][.=2]"
+ then: [t: "au carré"]
+ else: [t: "au cube"]
+
+- name: function-power
+ tag: power
+ match:
+ - "following-sibling::*[1][self::m:mo][.='']"
+ replace:
+ - bookmark: "*[2]/@id"
+ - x: "*[2]"
+ - t: "puissance de"
+ - pause: short
+ - x: "*[1]"
+
+- name: squared-or-cubed
+ tag: power
+ match: "*[2][self::m:mn][.='2' or .='3']"
+ replace:
+ - x: "*[1]"
+ - bookmark: "*[2]/@id"
+ - test:
+ if: "*[2][.=2]"
+ then: [t: "au carré"]
+ else: [t: "au cube"]
+
+- name: simple-integer
+ tag: power
+ match: "*[2][self::m:mn][not(contains(., '.'))]"
+ replace:
+ - x: "*[1]"
+ - t: "à la"
+ - x: "*[2]"
+
+- name: simple-negative-integer
+ tag: power
+ match:
+ - "*[2][self::m:minus and count(*)=1 and"
+ - " *[1][self::m:mn][not(contains(., '.'))]]"
+ replace:
+ - x: "*[1]"
+ - t: "à la"
+ - x: "*[2]"
+
+- name: simple-var
+ tag: power
+ match: "*[2][self::m:mi][string-length(.)=1]"
+ replace:
+ - x: "*[1]"
+ - t: "à la"
+ - x: "*[2]"
+ - pronounce: [text: "-ième", ipa: "jɛm", sapi5: "ième", eloquence: "ième"]
+
+- name: simple
+ tag: power
+ match: "IsNode(*[2], 'leaf')"
+ replace:
+ - x: "*[1]"
+ - t: "à la"
+ - x: "*[2]"
+
+- name: nested
+ tag: power
+ match:
+ - "*[2]["
+ - " (self::m:power and not(IsNode(*[2], 'leaf'))) or"
+ - " self::m:mrow[*[last()][self::m:power[not(IsNode(*[2], 'leaf'))]]]"
+ - " ]"
+ replace:
+ - x: "*[1]"
+ - t: "élevé à la"
+ - x: "*[2]"
+ - pause: short
+ - test:
+ if: "$Impairment = 'Blindness'"
+ then:
+ - t: "fin d'exposant"
+ - pause: short
+ else:
+ - pause: medium
+
+- name: default
+ tag: power
+ match: "."
+ replace:
+ - x: "*[1]"
+ - t: "élevé à la"
+ - x: "*[2]"
+ - t: "puissance"
+ - pause: short
+
+- name: set
+ tag: set
+ match: "."
+ replace:
+ - test:
+ - if: "count(*)=0"
+ then:
+ - t: "ensemble vide"
+ - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']"
+ then:
+ - t: "ensemble de tous les"
+ - x: "*[1]/*[1]"
+ - t: "tel que"
+ - x: "*[1]/*[3]"
+ else:
+ - t: "ensemble"
+ - x: "*[1]"
+
+- name: times
+ tag: mo
+ match:
+ - ".='' and"
+ - "not(@data-function-guess) and ("
+ - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')]) and "
+ - " following-sibling::*[1]["
+ - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|') or "
+ - " self::m:matrix or self::m:determinant or self::m:binomial or"
+ - " self::m:square-root or self::m:msqrt or self::m:root or self::m:mroot or"
+ - " (self::m:msub or self::m:msubsup or"
+ - " ((self::m:msup or self::m:power) and not(IsNode(*[1], 'leaf') and *[2][self::m:mn and (.=2 or '.=3')]))) and "
+ - " (*[1][self::m:mrow[IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')] or "
+ - " self::m:matrix or self::m:determinant] or"
+ - " not(IsNode(*[2], 'simple')) or "
+ - " (self::m:msubsup and not(IsNode(*[3], 'simple')))"
+ - " )"
+ - " ]"
+ - " or "
+ - " preceding-sibling::*[1]["
+ - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')]"
+ - " )"
+ replace:
+ - t: "fois"
+
+- name: no-say-parens
+ tag: mrow
+ match:
+ - "parent::*[not(self::m:msup) and not(self::m:msub) and not(self::m:msubsup) and not(self::m:power) and"
+ - " not(self::m:math) ] and "
+ - "( IsBracketed(., '(', ')') or IsBracketed(., '[', ']') ) and "
+ - "( IsNode(*[2], 'simple') ) and"
+ - "not(preceding-sibling::*[1][.='\u2062' and @data-function-guess]) and"
+ - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')])"
+ replace:
+ - x: "*[2]"
+
+- include: "SharedRules/geometry.yaml"
+- include: "SharedRules/linear-algebra.yaml"
+- include: "SharedRules/general.yaml"
+- include: "SharedRules/default.yaml"
diff --git a/Rules/Languages/fr/overview.yaml b/Rules/Languages/fr/overview.yaml
new file mode 100644
index 00000000..306b74f2
--- /dev/null
+++ b/Rules/Languages/fr/overview.yaml
@@ -0,0 +1,103 @@
+---
+
+- name: overview-default
+ tag: [mfrac, fraction]
+ match: "."
+ replace:
+ - test:
+ if: "IsNode(*[1], 'simple') and IsNode(*[2], 'simple')"
+ then:
+ - x: "*[1]"
+ - t: "sur"
+ - x: "*[2]"
+ else:
+ - t: "fraction"
+
+- name: overview-default
+ tag: [msqrt, "square-root"]
+ match: "."
+ replace:
+ - t: "racine carrée"
+ - test:
+ if: "IsNode(*[1], 'simple')"
+ then:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - x: "*[1]"
+
+- name: overview-default
+ tag: [mroot, root]
+ match: "."
+ replace:
+ - test:
+ if: "*[2][self::m:mn]"
+ then_test:
+ - if: "*[2][.='2']"
+ then: [t: "racine carrée"]
+ - else_if: "*[2][.='3']"
+ then: [t: "racine cubique"]
+ - else:
+ - x: "*[2]"
+ - t: "ième racine"
+ else:
+ - x: "*[2]"
+ - t: "racine"
+ - test:
+ if: "IsNode(*[1], 'simple')"
+ then:
+ - test:
+ if: "$Verbosity!='Terse'"
+ then: [t: "de"]
+ - x: "*[1]"
+
+- name: matrix-override
+ tag: mrow
+ match:
+ - "*[2][self::m:mtable] and"
+ - "(IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|'))"
+ replace:
+ - t: "la"
+ - x: count(*[2]/*)
+ - t: "par"
+ - x: count(*[2]/*[self::m:mtr][1]/*)
+ - test:
+ if: "*[1][.='|']"
+ then: [t: "déterminant"]
+ else: [t: "matrice"]
+
+- name: overview-default
+ tag: mtable
+ match: "."
+ replace:
+ - t: "la"
+ - x: count(*[2]/*)
+ - t: "par"
+ - x: count(*[2]/*[self::m:mtr][1]/*)
+ - t: "table"
+
+- name: short-mrow
+ tag: mrow
+ match: "count(*)<6"
+ replace:
+ - insert:
+ nodes: "*"
+ replace: [pause: auto]
+
+- name: long-mrow
+ tag: mrow
+ match: "."
+ replace:
+ - x: "*[1]"
+ - pause: auto
+ - x: "*[2]"
+ - pause: auto
+ - x: "*[3]"
+ - pause: auto
+ - x: "*[4]"
+ - pause: auto
+ - x: "*[5]"
+ - pause: auto
+ - t: "et ainsi de suite"
+
+- include: "SimpleSpeak_Rules.yaml"
diff --git a/tests/Languages/fr.rs b/tests/Languages/fr.rs
new file mode 100644
index 00000000..66b5c72f
--- /dev/null
+++ b/tests/Languages/fr.rs
@@ -0,0 +1,7 @@
+#![allow(non_snake_case)]
+
+mod SimpleSpeak {
+ mod functions;
+}
+
+mod alphabets;
diff --git a/tests/Languages/fr/SimpleSpeak/functions.rs b/tests/Languages/fr/SimpleSpeak/functions.rs
new file mode 100644
index 00000000..84b22b35
--- /dev/null
+++ b/tests/Languages/fr/SimpleSpeak/functions.rs
@@ -0,0 +1,27 @@
+/// AI generated
+use crate::common::*;
+use anyhow::Result;
+
+#[test]
+fn trig_names() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "sinus de x plus cosinus de y")?;
+ Ok(())
+}
+
+#[test]
+fn simple_fraction() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "21 sur 34")?;
+ Ok(())
+}
+
+#[test]
+fn square_root() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "la racine carrée de x")?;
+ Ok(())
+}
diff --git a/tests/Languages/fr/alphabets.rs b/tests/Languages/fr/alphabets.rs
new file mode 100644
index 00000000..f1ea8ac3
--- /dev/null
+++ b/tests/Languages/fr/alphabets.rs
@@ -0,0 +1,11 @@
+/// AI generated
+use crate::common::*;
+use anyhow::Result;
+
+#[test]
+fn greek_letters() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "alpha comma, oméga")?;
+ Ok(())
+}
diff --git a/tests/Languages/fr/shared.rs b/tests/Languages/fr/shared.rs
new file mode 100644
index 00000000..b359ecb9
--- /dev/null
+++ b/tests/Languages/fr/shared.rs
@@ -0,0 +1,19 @@
+/// AI generated
+use crate::common::*;
+use anyhow::Result;
+
+#[test]
+fn modified_vars() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "x circonflexe plus vecteur t")?;
+ Ok(())
+}
+
+#[test]
+fn subscript_literal() -> Result<()> {
+ // AI generated
+ let expr = "";
+ test("fr", "SimpleSpeak", expr, "x indice 1")?;
+ Ok(())
+}
diff --git a/tests/languages.rs b/tests/languages.rs
index 4d5c890a..00394997 100644
--- a/tests/languages.rs
+++ b/tests/languages.rs
@@ -10,6 +10,7 @@ mod Languages {
mod sv;
mod nb;
mod de;
+ mod fr;
mod vi {
mod vi;
}