From 1c076680af7a6f196cfa8a081d6136f3f3186b5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Moritz=20Gro=C3=9F?= Date: Sat, 18 Apr 2026 22:46:44 +0200 Subject: [PATCH] Add French language support for SimpleSpeak rules, geometry rules, shared rules, and tests --- Rules/Languages/fr/SharedRules/calculus.yaml | 56 +++ Rules/Languages/fr/SharedRules/default.yaml | 414 ++++++++++++++++++ Rules/Languages/fr/SharedRules/general.yaml | 152 +++++++ Rules/Languages/fr/SharedRules/geometry.yaml | 78 ++++ .../fr/SharedRules/linear-algebra.yaml | 35 ++ Rules/Languages/fr/SimpleSpeak_Rules.yaml | 308 +++++++++++++ Rules/Languages/fr/overview.yaml | 103 +++++ tests/Languages/fr.rs | 7 + tests/Languages/fr/SimpleSpeak/functions.rs | 27 ++ tests/Languages/fr/alphabets.rs | 11 + tests/Languages/fr/shared.rs | 19 + tests/languages.rs | 1 + 12 files changed, 1211 insertions(+) create mode 100644 Rules/Languages/fr/SharedRules/calculus.yaml create mode 100644 Rules/Languages/fr/SharedRules/default.yaml create mode 100644 Rules/Languages/fr/SharedRules/general.yaml create mode 100644 Rules/Languages/fr/SharedRules/geometry.yaml create mode 100644 Rules/Languages/fr/SharedRules/linear-algebra.yaml create mode 100644 Rules/Languages/fr/SimpleSpeak_Rules.yaml create mode 100644 Rules/Languages/fr/overview.yaml create mode 100644 tests/Languages/fr.rs create mode 100644 tests/Languages/fr/SimpleSpeak/functions.rs create mode 100644 tests/Languages/fr/alphabets.rs create mode 100644 tests/Languages/fr/shared.rs 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 = "sinx+cosy"; + test("fr", "SimpleSpeak", expr, "sinus de x plus cosinus de y")?; + Ok(()) +} + +#[test] +fn simple_fraction() -> Result<()> { + // AI generated + let expr = "2134"; + test("fr", "SimpleSpeak", expr, "21 sur 34")?; + Ok(()) +} + +#[test] +fn square_root() -> Result<()> { + // AI generated + let expr = "x"; + 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 = "x^+t"; + test("fr", "SimpleSpeak", expr, "x circonflexe plus vecteur t")?; + Ok(()) +} + +#[test] +fn subscript_literal() -> Result<()> { + // AI generated + let expr = "x1"; + 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; }