--- #default rules shared among several speech rules - 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)" replace: - test: if: "$MathRate = 100" then: [{x: "*"}] else: - rate: value: "$MathRate" replace: [{x: "*"}] - name: empty-mrow tag: mrow match: "not(*)" replace: - T: " " # say nothing -- placeholder - name: default tag: mrow match: "." replace: - insert: nodes: "*" replace: [{pause: auto}] - name: default tag: mn match: "." replace: - bookmark: "@id" - test: if: "@data-roman-numeral" then: - test: if: "parent::*[1][self::m:chemical-formula]" then: - t: "hóa trị" - x: "@data-number" else: - x: "@data-number" - test: if: "$Verbosity = 'Verbose'" then: [t: "la mã"] else: # FIX: removing the digit block separators is likely locale dependent - x: "translate(., ' `', '')" # remove digit block separators - name: default tag: [mo, mtext] match: "." replace: - bookmark: "@id" - x: "text()" - name: default tag: mi match: "." replace: - bookmark: "@id" - test: if: "string-length(.) = 1" # need unicode.tdl to kick in for single letter tokens then: [x: "text()"] else: [x: "translate(., '-_\u00A0', ' ')" ] # from intent literals - name: default tag: ms match: "." replace: - T: "chuỗi" - pause: short - x: "text()" - name: default tag: mstyle match: "." replace: [{x: "*"}] - name: literal-simple # don't include nested fractions. E.g, fraction a plus b over c + 1 end fraction" is ambiguous # by simplistic SimpleSpeak's rules "b over c" is a fraction, but if we say nested fractions # are never simple, then any 'over' applies only to enclosing "fraction...end fraction" pair. tag: mfrac match: - "(IsNode(*[1],'leaf') and IsNode(*[2],'leaf')) and" - "not(ancestor::*[name() != 'mrow'][1]/self::m:fraction)" # FIX: can't test for mrow -- what should be used??? replace: - x: "*[1]" - T: "phần" # phrase("the fraction x 'over' y") - x: "*[2]" - pause: short - name: literal-default tag: mfrac match: "." replace: - T: "bắt đầu" # phrase("'start' fraction x over y end of fraction") - pause: short - x: "*[1]" - test: if: "not(IsNode(*[1],'leaf'))" then: [{pause: short}] - T: "trên" # phrase("the fraction x 'over' y") - test: if: "not(IsNode(*[2],'leaf'))" then: [{pause: short}] - x: "*[2]" - pause: short - T: "hết phân số" # phrase("start of fraction x over y 'end over'") - pause: medium # not sure what really should be said for these since we should not assume they are square roots - name: literal-default tag: msqrt match: "." replace: - test: if: "$Verbosity!='Terse'" then: [T: ""] # phrase("'the' root of x") - T: "căn" - test: if: "$Verbosity!='Terse'" then: [T: "của"] # phrase("the root 'of' x") - x: "*[1]" - pause: short - test: if: "not(IsNode(*[1],'leaf'))" then: [T: "hết căn"] # phrase("root of x 'end root symbol'") # not sure what really should be said for these since we should not assume they are square roots - name: literal-default tag: mroot match: "." replace: - test: if: "$Verbosity!='Terse'" then: [T: ""] # phrase("'the' root of x") - T: "căn" - T: "bậc" # phrase("the root of x 'with index' 5") - x: "*[1]" - pause: short - test: if: "$Verbosity!='Terse'" then: [T: "của"] # phrase("the root 'of' x") - x: "*[2]" - pause: short - test: if: "not(IsNode(*[2],'leaf'))" then: [T: "hết căn"] # phrase("root of x 'end root symbol'") - name: simple-sub tag: particular-value-of # invisible comma -- want "x sub 1 1" without "end sub" match: "IsNode(*[2], 'leaf') or *[2][self::m:mrow][*[2][text()='⁣']]" replace: - x: "*[1]" - x: "*[2]" - test: if: "$Verbosity!='Terse' or not(*[2][self::m:mn])" # just say "x 1" for terse vs "x sub 1" then: [{T: " "}] - name: default tag: particular-value-of match: "." replace: - x: "*[1]" - T: "chỉ số dưới là" - x: "*[2]" - T: "hết chỉ số dưới" - pause: short - name: literal tag: msub match: "." replace: - x: "*[1]" - x: "*[2]" - T: "dưới" # phrase(x 'sub' 2) - name: literal tag: [msup, msubsup] match: "." replace: - x: "*[1]" - test: if: "name(.)='msubsup'" then: - x: "*[2]" - T: "dưới" # phrase(x 'sub' 2) - test: if: "*[last()][translate(., '′″‴⁗†‡°*', '')='']" then: [x: "*[last()]"] else_test: if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" # FIX: is this test necessary? then: - x: "*[last()]" - T: "trên" # phrase(x 'super' 2) - test: if: "not(IsNode(*[last()], 'simple'))" then: [T: "hết chỉ số trên"] # phrase(x super 2 'end of super') else: #- t: "raised to the" # phrase(5 'raised to the' second power equals 25) - T: "mũ" # phrase(5 raised to the second 'power' equals 25) - x: "*[last()]" - name: default tag: munder match: "." replace: - test: if: "not(IsNode(*[1], 'leaf'))" then: [{T: " "}] - x: "*[1]" #- T: "với" #- x: "*[2]" - T: đáy - name: overparen tag: mover match: "*[2][.='⏜']" replace: - x: "*[2]" # shape indicator, overparen - x: "*[1]" - 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: " "}] - x: "*[1]" #- T: "với" #- x: "*[2]" - T: "ngang" - name: default tag: munderover match: "." replace: - test: if: "not(IsNode(*[1], 'leaf'))" then: [{T: " "}] - x: "*[1]" #- T: "với" #- x: "*[2]" - T: "đáy và" - x: "*[3]" - T: "ngang" - name: default # Here we support up to 2 prescripts and up to 4 postscripts -- that should cover all reasonable cases # If there are more, we just dump them out without regard to sup/super :-( # FIX: this could use more special cases # There is (currently) no way in MathCAT to deal with n-ary arguments other than "all" ('*') or an individual entry ('*[1]'). tag: mmultiscripts match: "." variables: # computing the number of postscripts is messy because of being optionally present -- we use "mod" to get the count right - Prescripts: "m:mprescripts/following-sibling::*" - NumChildren: "count(*)" # need to stash this since the count is wrong inside '*[...]' below - Postscripts: "*[position()>1 and position() < (last() + ($NumChildren mod 2) -count($Prescripts))]" replace: - x: "*[1]" - test: if: "$Prescripts" # more common case then: - with: variables: - PreSubscript: "IfThenElse($Verbosity='Verbose', 'pre subscript', 'pre sub')" - PreSuperscript: "IfThenElse($Verbosity='Verbose', 'pre superscript', 'pre super')" replace: - test: # only bother announcing if there is more than one prescript if: "count($Prescripts) > 2" then: - T: "với" - x: "count($Prescripts) div 2" - T: "chỉ số trước" - 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: "và"}] - test: if: "not($Prescripts[2][self::m:none])" then: - x: "$PreSuperscript" - x: "$Prescripts[2]" - pause: short - test: if: "count($Prescripts) > 2" # more common case then: - test: if: "not($Prescripts[3][self::m:none])" then: - x: "$PreSubscript" - x: "$Prescripts[3]" - test: if: "not($Prescripts[3][self::m:none] or $Prescripts[4][self::m:none])" then: [{T: "và"}] - test: if: "not($Prescripts[4][self::m:none])" then: - x: "$PreSuperscript" - x: "$Prescripts[4]" - test: if: "count($Prescripts) > 4" # give up and just dump them out so at least the content is there then: - T: "và chỉ số trước xen kẽ" - x: "$Prescripts[position() > 4]" - T: "hết chỉ số trước" - test: if: "$Postscripts" then: - with: variables: - PostSubscript: "IfThenElse($Verbosity='Verbose', 'subscript', 'sub')" - PostSuperscript: "IfThenElse($Verbosity='Verbose', 'superscript', 'super')" replace: - test: # only bother announcing if there is more than one postscript if: "count($Postscripts) > 2" then: - test: if: "$Prescripts" then: [{T: "và"}] - T: "với" - x: "count($Postscripts) div 2" - T: "chỉ số sau" - 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: "và"}] - test: if: "not($Postscripts[2][self::m:none])" then: - x: "$PostSuperscript" - x: "$Postscripts[2]" - test: if: "count($Postscripts) > 2" then: - test: if: "not($Postscripts[3][self::m:none])" then: - x: "$PostSubscript" - x: "$Postscripts[3]" - test: if: "not($Postscripts[3][self::m:none] or $Postscripts[4][self::m:none])" then: [{T: "và"}] - test: if: "not($Postscripts[4][self::m:none])" then: - x: "$PostSuperscript" - x: "$Postscripts[4]" - test: if: "count($Postscripts) > 4" then: - test: if: "not($Postscripts[5][self::m:none])" then: - x: "$PostSubscript" - x: "$Postscripts[5]" - test: if: "not($Postscripts[5][self::m:none] or $Postscripts[6][self::m:none])" then: [{T: "và"}] - test: if: "not($Postscripts[6][self::m:none])" then: - x: "$PostSuperscript" - x: "$Postscripts[6]" - test: if: "count($Postscripts) > 6" then: - test: if: "not($Postscripts[7][self::m:none])" then: - x: "$PostSubscript" - x: "$Postscripts[7]" - test: if: "not($Postscripts[7][self::m:none] or $Postscripts[8][self::m:none])" then: [{T: "và"}] - test: if: "not($Postscripts[8][self::m:none])" then: - x: "$PostSuperscript" - x: "$Postscripts[8]" - test: if: "count($Postscripts) > 8" # give up and just dump them out so at least the content is there then: - T: "và chỉ số xen kẽ" - x: "$Postscripts[position() > 8]" - T: "hết chỉ số" - name: default tag: mtable variables: [{IsColumnSilent: false()}] match: "." replace: - T: "bảng gồm" - x: count(*) - test: if: count(*)=1 then: {T: "dòng"} else: {T: "dòng"} - T: và - x: "count(*[1]/*)" - test: if: "count(*[1]/*)=1" then: {T: "cột"} else: {T: "cột"} - pause: long - x: "*" - name: default # callers/context should do that. # this may get called from navigation -- in that case, there is no context to speak the row #, so don't do it tag: [mtr, mlabeledtr] match: "." replace: - T: "dòng" # phrase(the first 'row' of a matrix) - x: "count(preceding-sibling::*)+1" - test: if: .[self::m:mlabeledtr] then: - T: "có nhãn" # phrase(the line 'with label' first equation) - x: "*[1]/*" - pause: short - pause: medium - test: if: .[self::m:mlabeledtr] then: [{x: "*[position()>1]"}] else: {x: "*"} - name: default tag: mtd match: "." replace: - test: # ClearSpeak normally speaks "column 1" even though it says the row number, which is a waste... # The following is commented out but the count(...)!=0 probably belongs in other rule sets # if: not($IsColumnSilent) and ($ClearSpeak_Matrix = 'SpeakColNum' or count(preceding-sibling::*) != 0) if: "not($IsColumnSilent)" then: - T: "cột" # phrase(the first 'column' of the matrix) - x: "count(preceding-sibling::*)+1" - pause: medium - x: "*" - test: # short pause after each element; medium pause if last element in a row; long pause for last element in matrix - if: count(following-sibling::*) > 0 then: {pause: short} - else_if: count(../following-sibling::*) > 0 then: {pause: medium} else: {pause: long} - name: empty-box # The ordering below is the order in which words come out when there is more than one value # Note: @notation can contain more than one value tag: menclose match: "@notation='box' and *[self::m:mtext and text()=' ']" replace: - T: "ô rỗng" # phrase(the 'empty box' contains no values) - name: default # The ordering below is the order in which words come out when there is more than one value # Note: @notation can contain more than one value tag: menclose match: "." replace: - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' box ')]" then: [{T: hộp}, {pause: short}] - test: if: ".[contains(@notation,'roundedbox')]" then: [{T: hộp tròn}, {pause: short}] - test: if: ".[contains(@notation,'circle')]" then: [{T: hình tròn}, {pause: short}] - test: if: ".[ contains(concat(' ', normalize-space(@notation), ' '), ' left ') or contains(concat(' ', normalize-space(@notation), ' '), ' right ') or contains(@notation,'top') or contains(@notation,'bottom') ]" then: - T: đường trên - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' left ')]" then: [{T: trái}, {pause: short}] - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' right ')]" then: [{T: phải}, {pause: short}] - test: if: ".[contains(@notation,'top')]" then: [{T: trên}, {pause: short}] - test: if: ".[contains(@notation,'bottom')]" then: [{T: đáy}, {pause: short}] - test: if: ".[ contains(@notation,'updiagonalstrike') or contains(@notation,'downdiagonalstrike') or contains(@notation,'verticalstrike') or contains(@notation,'horizontalstrike') ]" then: - T: đường cắt - test: if: ".[contains(@notation,'updiagonalstrike') and contains(@notation,'downdiagonalstrike')]" then: [{SPELL: "'x'"}, {pause: short}] # seems better to say 'x cross out' than 'up diagonal, down diagonal cross out' else: - test: if: ".[contains(@notation,'updiagonalstrike')]" then: [{T: chéo lên}, {pause: short}] - test: if: ".[contains(@notation,'downdiagonalstrike')]" then: [{T: chéo xuống}, {pause: short}] - test: if: ".[contains(@notation,'verticalstrike')]" then: [{T: thẳng đứng}, {pause: short}] - test: if: ".[contains(@notation,'horizontalstrike')]" then: [{T: ngang}, {pause: short}] #- T: đường cắt - pause: short - test: if: ".[contains(@notation,'uparrow')]" then: [{T: mũi tên lên}, {pause: short}] - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' downarrow ')]" then: [{T: mũi tên xuống}, {pause: short}] - test: if: ".[contains(@notation,'leftarrow')]" then: [{T: mũi tên trái}, {pause: short}] - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' rightarrow ')]" then: [{T: mũi tên phải}, {pause: short}] - test: if: ".[contains(@notation,'northeastarrow')]" then: [{T: mũi tên đông bắc}, {pause: short}] - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southeastarrow ')]" then: [{T: mũi tên đông nam}, {pause: short}] - test: if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southwestarrow ')]" then: [{T: mũi tên tây nam}, {pause: short}] - test: if: ".[contains(@notation,'northwestarrow')]" then: [{T: mũi tên tây bắc}, {pause: short}] - test: if: ".[contains(@notation,'updownarrow')]" then: [{T: mũi tên đứng kết đôi}, {pause: short}] - test: if: ".[contains(@notation,'leftrightarrow')]" then: [{T: mũi tên ngang kết đôi}, {pause: short}] - test: if: ".[contains(@notation,'northeastsouthwestarrow')]" then: [{T: mũi tên chéo lên kết đôi}, {pause: short}] - test: if: ".[contains(@notation,'northwestsoutheastarrow')]" then: [{T: mũi tên chéo xuống kết đôi}, {pause: short}] - test: if: ".[contains(@notation,'actuarial')]" then: [{T: ký hiệu phân tích}, {pause: short}] - test: if: ".[contains(@notation,'madrub')]" then: [{T: ký hiệu giai thừa ả rập}, {pause: short}] - test: if: ".[contains(@notation,'phasorangle')]" then: [{T: góc phasor}, {pause: short}] - test: if: ".[contains(@notation,'longdiv') or not(@notation) or normalize-space(@notation) ='']" # default then: [{T: ký hiệu chia dài}, {pause: short}] - test: if: ".[contains(@notation,'radical')]" then: [{T: căn bậc hai}, {pause: short}] - T: bọc - test: if: "*[self::m:mtext and text()=' ']" then: [T: "khoảng cách"] # (en: "some space") otherwise there is complete silence else: [x: "*"] - test: if: "$Impairment = 'Blindness' and ( $SpeechStyle != 'SimpleSpeak' or not(IsNode(*[1], 'leaf')) )" then: [{T: hết bọc}] - pause: short - name: semantics tag: "semantics" match: "*[@encoding='MathML-Presentation']" replace: - x: "*[@encoding='MathML-Presentation']/*[1]" - name: semantics-default tag: "semantics" match: . replace: - x: "*[1]" - name: apply-function tag: "apply-function" match: . replace: - x: "*[1]" - T: "ứng với" # phrase(the function sine 'applied to' x plus y) - x: "*[2]" # Here are the intent hints that need to be handled: 'prefix' | 'infix' | 'postfix' | 'function' | 'silent' - name: silent-intent # uncaught intent -- speak as arg1 arg2 .... tag: "*" match: "contains(@data-intent-property, ':silent:') and count(*)>0" replace: - x: "*" - name: prefix-intent # uncaught intent -- speak as arg1 arg2 .... tag: "*" match: "contains(@data-intent-property, ':prefix:') and count(*)>0" replace: - x: "translate(name(.), '-_', ' ')" - x: "*" - pause: short - name: postfix-intent # uncaught intent -- speak as arg1 arg2 .... tag: "*" match: "contains(@data-intent-property, ':postfix:') and count(*)>0" replace: - pause: short - x: "*" - x: "translate(name(.), '-_', ' ')" - name: infix-intent # uncaught intent -- speak as foo of arg1 comma arg2 .... tag: "*" match: "contains(@data-intent-property, ':infix:') and count(*)>0" replace: - pause: short - insert: nodes: "*" replace: [x: "translate(name(.), '-_', ' ')", pause: auto] - pause: short - name: function-intent # uncaught intent -- speak as foo of arg1 comma arg2 .... tag: "*" match: count(*)>0 replace: - x: "translate(name(.), '-_', ' ')" - T: "của" - pause: short - insert: nodes: "*" replace: [{T: "phẩy"}, {pause: auto}] - name: default-text # unknown leaf -- just speak the text -- could be a literal intent tag: "*" match: "." replace: - x: "translate(name(), '-_', ' ')"