From ed4c562f61b4403fd339d6addf8465fa493ad648 Mon Sep 17 00:00:00 2001 From: cary-rowen Date: Sat, 22 Aug 2026 19:07:26 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E4=BF=9D=E5=AD=98=20MathCAT=20=E7=AE=80?= =?UTF-8?q?=E4=BD=93=E4=B8=AD=E6=96=87=E7=BF=BB=E8=AF=91=E6=A3=80=E6=9F=A5?= =?UTF-8?q?=E7=82=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Rules/Languages/zh/ClearSpeak_Rules.yaml | 761 ++++ Rules/Languages/zh/SharedRules/calculus.yaml | 56 + Rules/Languages/zh/SharedRules/default.yaml | 765 ++++ Rules/Languages/zh/SharedRules/general.yaml | 1161 +++++ Rules/Languages/zh/SharedRules/geometry.yaml | 76 + .../zh/SharedRules/linear-algebra.yaml | 36 + Rules/Languages/zh/SimpleSpeak_Rules.yaml | 358 ++ Rules/Languages/zh/definitions.yaml | 687 +++ Rules/Languages/zh/navigate.yaml | 1776 ++++++++ Rules/Languages/zh/overview.yaml | 122 + Rules/Languages/zh/unicode-full.yaml | 3802 +++++++++++++++++ Rules/Languages/zh/unicode.yaml | 530 +++ tests/Languages/zh.rs | 3 +- tests/Languages/zh/cn.rs | 674 +++ 14 files changed, 10806 insertions(+), 1 deletion(-) create mode 100644 Rules/Languages/zh/ClearSpeak_Rules.yaml create mode 100644 Rules/Languages/zh/SharedRules/calculus.yaml create mode 100644 Rules/Languages/zh/SharedRules/default.yaml create mode 100644 Rules/Languages/zh/SharedRules/general.yaml create mode 100644 Rules/Languages/zh/SharedRules/geometry.yaml create mode 100644 Rules/Languages/zh/SharedRules/linear-algebra.yaml create mode 100644 Rules/Languages/zh/SimpleSpeak_Rules.yaml create mode 100644 Rules/Languages/zh/definitions.yaml create mode 100644 Rules/Languages/zh/navigate.yaml create mode 100644 Rules/Languages/zh/overview.yaml create mode 100644 Rules/Languages/zh/unicode-full.yaml create mode 100644 Rules/Languages/zh/unicode.yaml create mode 100644 tests/Languages/zh/cn.rs diff --git a/Rules/Languages/zh/ClearSpeak_Rules.yaml b/Rules/Languages/zh/ClearSpeak_Rules.yaml new file mode 100644 index 000000000..84b5b0eb0 --- /dev/null +++ b/Rules/Languages/zh/ClearSpeak_Rules.yaml @@ -0,0 +1,761 @@ +--- +- 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:')" + # say nothing + replace: [] + +# handling of negative numbers that come from 'intent' is hard -- we do something that is close to right here +- name: intent-literal-negative-number + tag: mn + match: "starts-with(text(), '-')" + replace: + - T: "负" # A leading minus sign makes this a negative number. + - x: "translate(text(), '-_', '')" + +- name: default + tag: square-root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] # phrase('the' square root of 25) + - test: + if: "$ClearSpeak_Roots = 'PosNegSqRoot' or $ClearSpeak_Roots = 'PosNegSqRootEnd'" + then: + - bookmark: "*[1]/@id" + - test: + if: "parent::*[self::m:minus and count(*)=1]" + then: [T: "负"] # phrase(minus 4 is a 'negative' number) + else: [T: "正"] # phrase(10 is a 'positive' number) + - T: "根号" # phrase(8 is the 'square root' of 64) + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase(the square root 'of' 5) + else: [pause: short] + - x: "*[1]" + - test: + - if: "$ClearSpeak_Roots = 'RootEnd' or $ClearSpeak_Roots = 'PosNegSqRootEnd'" + then: + - pause: short + - T: "结束根号" # phrase(the square root of x 'end root') + - pause: medium + - else_if: "IsNode(*[1], 'simple')" + then: [pause: short] + else: [pause: long] + +- name: default + tag: root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] # phrase(6 is 'the' square root of 36) + - test: + if: "$ClearSpeak_Roots = 'PosNegSqRoot' or $ClearSpeak_Roots = 'PosNegSqRootEnd'" + then: + - test: + if: "parent::*[(self::m:minus or self::m:plus) and count(*)=1]" + then: [bookmark: "parent/@id"] + - test: + if: "parent::m:minus" + then: [T: "负"] # phrase(minus 6 is a 'negative' number) + else: [T: "正"] # phrase(10 is a 'positive' number) + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase(the square root 'of' 36) + - x: "*[1]" + - T: "的" + - test: + if: "*[2][self::m:mn and not(contains(., '.'))]" + then_test: + - if: "*[2][.='2']" + then: [T: "平方根"] # phrase(5 is the 'square root' of 25) + - else_if: "*[2][.='3']" + then: [T: "立方根"] # phrase(5 is the 'cube root' of 625) + - else: [x: "*[2]", T: "次方根"] # phrase(the square 'root' of 25) + else: + - test: + if: "*[2][self::m:mi][string-length(.)=1]" + then: + - x: "*[2]" + #- pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] + else: [x: "*[2]"] + - T: "次方根" # phrase(the square 'root' of 36) + - test: + if: "$ClearSpeak_Roots = 'RootEnd' or $ClearSpeak_Roots = 'PosNegSqRootEnd'" + then: + - pause: short + - T: "结束根号" + - pause: medium + else_test: + if: "IsNode(*[1], 'simple')" + then: [pause: short] + else: [pause: long] + +# The 'negative' rule interacts with the msqrt/mroot rules as those might pick off this case ("the negative square root of x") +- name: negative_and_positive + tag: [plus, minus] + match: "count(*)=1 and contains(@data-intent-property, ':prefix:')" + replace: + - test: + if: + - "*[1][self::m:square-root or self::m:root] and" + - "($ClearSpeak_Roots = 'PosNegSqRoot' or $ClearSpeak_Roots = 'PosNegSqRootEnd')" + then: [T: ""] + else: + - bookmark: "@id" + - test: + if: "self::m:minus" + then: [T: "负"] # phrase(minus 5 is a 'negative' number) + else: [T: "正"] # phrase(7 is a 'positive' number) + - x: "*[1]" + +# Fraction rules +# Mixed numbers mostly "just work" because the invisible char reads as "and" and other parts read properly on their own + +# Units (e.g., meters per second) +- name: per-fraction + tag: fraction + match: + - "$ClearSpeak_Fractions='Per' or" + - "(BaseNode(*[1])[contains(@data-intent-property, ':unit') or" + - " ( self::m:mrow and count(*)=3 and" + - " *[1][self::m:mn] and *[2][.='⁢'] and BaseNode(*[3])[contains(@data-intent-property, ':unit')] ) ] and" + - " BaseNode(*[2])[contains(@data-intent-property, ':unit')] )" + replace: + - x: "*[1]" + - T: "每" # phrase('5 meters 'per' second) + - x: "*[2]" + +- name: common-fraction + tag: fraction + match: + - "($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and" + - "*[1][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and" + - "*[2][self::m:mn][not(contains(., $DecimalSeparators)) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))]" + variables: [IsPlural: "*[1]!=1"] + replace: + - x: "*[2]" + - T: "分之" + - x: "*[1]" + +- name: fraction-over-simple + tag: fraction + match: + - "($ClearSpeak_Fractions='Over' or $ClearSpeak_Fractions='FracOver' or $ClearSpeak_Fractions='OverEndFrac') or" + - "( not($ClearSpeak_Fractions='General' or $ClearSpeak_Fractions='GeneralEndFrac') and" + - " (IsNode(*[1],'simple') and IsNode(*[2],'simple')) )" # simple fraction in ClearSpeak spec + replace: + - test: + if: "$ClearSpeak_Fractions='FracOver'" + then: + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "分数" # phrase(the 'fraction' with 3 over 4) + - x: "*[2]" + - T: "分之" # phrase(the fraction 3 'over' 4) + - x: "*[1]" + - test: + # very ugly!!! -- replicate nested ordinal fraction as they are an exception + if: "$ClearSpeak_Fractions='OverEndFrac' or ($ClearSpeak_Fractions='EndFrac' and not( ($ClearSpeak_Fractions='Auto' or $ClearSpeak_Fractions='Ordinal' or $ClearSpeak_Fractions='EndFrac') and *[1][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] and *[2][*[1][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or text()<20)] and *[2][self::m:mn][not(contains(., '.')) and ($ClearSpeak_Fractions='Ordinal' or (2<= text() and text()<=10))] ] ) )" + then: + - pause: short + - T: "结束分数" # phrase(7 over 8 'end fraction') + - pause: short + +- # fraction with text or numbers followed by text in both numerator and denominator + name: fraction-over-text + tag: fraction + match: + - "not($ClearSpeak_Fractions='General' or $ClearSpeak_Fractions='GeneralEndFrac') and" + - "( " + - " ((*[1][self::m:mi or self::m:mtext][string-length(.)>1]) or " # fractions with text + - " (*[1][self::m:mrow][count(*)=3][ " + - " *[1][self::m:mn] and " + - " *[2][self::m:mo][.='⁢'] and " # invisible times + - " *[3][self::m:mi or self::m:mtext][string-length(.)>1] ]) ) and" + - " ((*[2][self::m:mi or self::m:mtext][string-length(.)>1]) or " # fractions with text + - " (*[2][self::m:mrow][count(*)=3][ " + - " *[1][self::m:mn] and " + - " *[2][self::m:mo][.='⁢'] and " # invisible times + - " *[3][self::m:mi or self::m:mtext][string-length(.)>1] ]) )" + - ")" + replace: + - x: "*[2]" + - T: "分之" # phrase(the fraction 3 'over' 4) + - x: "*[1]" + - test: + if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='OverEndFrac'" + then: + - pause: short + - T: "结束分数" # phrase(7 over 8 'end fraction') + - pause: short + +- name: default + tag: fraction + match: "." + replace: + - OT: "" # phrase(5 is 'the' square toot of 25) + - T: "分数,分子为" # phrase(the 'fraction with numerator' 6) + - test: + if: not(IsNode(*[1], 'simple')) + then: [pause: medium] + - x: "*[1]" + - pause: medium + - T: "分母为" # phrase(the fraction with numerator 5 'and denominator' 8) + - x: "*[2]" + - pause: long + - test: + if: "$ClearSpeak_Fractions='EndFrac' or $ClearSpeak_Fractions='GeneralEndFrac'" + then: + - pause: short + - T: "结束分数" # phrase(the fraction with 3 over 4 'end fraction') + - pause: short + +# rules for functions raised to a power +# these could have been written on 'mrow' but putting them on msup seems more specific +# to see if it is a function, we look right to see if the following sibling is apply-function +- name: ClearSpeak-function-inverse + tag: inverse-function + match: "." + replace: + - test: + if: $ClearSpeak_Trig = 'TrigInverse' + then: [T: "反", x: "*[1]", bookmark: "*[2]/@id"] # Use the standard mainland names such as 反正弦. + else_test: + if: $ClearSpeak_Trig = 'ArcTrig' + then: [bookmark: "*[2]/@id", T: "反", x: "*[1]"] + else: [bookmark: "*[2]/@id", x: "*[1]", T: "的反函数"] # default/Auto + +- name: function-squared-or-cubed + tag: power + match: + - "*[2][self::m:mn][.='2' or .='3'] and" + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - test: + if: "*[2][.='2']" + then: [T: "平方"] # phrase(25 equals 5 'squared') + else: [T: "立方"] # phrase(625 equals 5 'cubed') +- name: function-power + tag: power + match: + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - x: "*[1]" + - T: "的" + - bookmark: "*[2]/@id" + - test: + if: "*[2][self::m:mn][not(contains(., '.'))]" + then: [x: "*[2]"] + else: [x: "*[2]"] + - T: "次方" # phrase(the third 'power of' 6) + - pause: short + +- name: ClearSpeak-log-with-base + tag: mrow + match: "count(*)=3 and *[1][self::m:logarithm-with-base] and *[2][.='⁡']" + replace: + - T: "以" + - x: "*[1]/*[1]" + - T: "为底" + - pause: short + - x: "*[3]" + - T: "的对数" + +- name: AfterPower-nested + tag: power + match: # directly a superscript or an mrow that contains a superscript + - "$ClearSpeak_Exponents = 'AfterPower' and" + - "*[2][self::m:power or self::m:power or self::m:mrow[m:power]]" + replace: + - x: "*[1]" + - T: "的指数为" # phrase(5 'raised to the exponent' x plus 1) + - pause: short + - x: "*[2]" + - pause: short + - T: "结束指数" # phrase(5 raised to the exponent x plus 1 'end exponent') + +- name: AfterPower-default + tag: power + match: "$ClearSpeak_Exponents = 'AfterPower'" + replace: + - x: "*[1]" + - T: "的" # phrase(x is 'raised to the power' 4) + - x: "*[2]" + - T: "次方" + - pause: short + +- name: squared + tag: power + match: "*[2][self::m:mn][.='2'] and $ClearSpeak_Exponents = 'Auto'" + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - T: "平方" # phrase(7 'squared' equals 49) + +- name: cubed + tag: power + match: "*[2][self::m:mn][.='3'] and $ClearSpeak_Exponents = 'Auto'" + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - T: "立方" # phrase(5 'cubed' equals 125) + +- name: simple-integer + tag: power + match: "*[2][self::m:mn][not(contains(., '.'))]" + replace: + - x: "*[1]" + - T: "的" # phrase(2 raised 'to the' power 7) + - test: + if: "*[2][.>0]" + then: [x: "*[2]"] + else: [x: "*[2]"] + - test: + if: "$ClearSpeak_Exponents != 'Ordinal'" + then: [T: "次方"] # phrase(2 raised to the 'power' 7) + +- 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: "的" # phrase(2 raised 'to the' power 7) + - x: "*[2]" + - test: + if: "$ClearSpeak_Exponents != 'Ordinal'" + then: [T: "次方"] # phrase(2 raised to the 'power' 7) + +- name: simple-var + tag: power + match: "*[2][self::m:mi][string-length(.)=1]" + replace: + - x: "*[1]" + - T: "的" # phrase(3 raised 'to the' power 7) + - x: "*[2]" + - test: + if: "$ClearSpeak_Exponents != 'Ordinal'" + then: [T: "次方"] # phrase(2 raised to the 'power' 7) + +# match nested exponent, where the nested exponent is has the power 2 or 3 (n below) +# [xxx]^n, - [xxx]^n, [xxx] var^n, -[xxx] var^n +# where xxx is a number or common fraction (or a var in the first two forms) +# short of creating a specialized built-in function, I don't see a way to eliminate a lot of repetition in the matches +# also really bad is that the test of a common fraction is replicated here (four times!) +# Note: the ClearSpeak doc doesn't say these only apply when the pref is "Auto", +# but the test cases all fall back to "raised to the exponent" when not "Auto" +# If these are allowed for non-Auto values, then you end up with "...power power...". +- # [xxx]^n + name: nested-squared-or-cubed + tag: power + match: + - "$ClearSpeak_Exponents = 'Auto' and" + - "*[2][self::m:power][" + - " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3 + # base is mn, mi, common fraction ([xxx] case) + - " *[1][self::m:mn or self::m:mi or " + - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and" + - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]" + - " ]" + - " ]" + replace: + - x: "*[1]" + - T: "的" # phrase(x 'raised to the' second power) + - x: "*[2]" + - T: "次方" # phrase(x raised to the second 'power') + +- # - [xxx]^n + name: nested-negative-squared-or-cubed + tag: power + match: + - "$ClearSpeak_Exponents = 'Auto' and" + - " *[2][self::m:minus and count(*)=1 and " + - " *[1]/*[1][self::m:power][" + - " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3" + # base is mn, mi, common fraction ([xxx] case) + - " *[1][self::m:mn or self::m:mi or " + - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and" + - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]" + - " ]" + - " ]" + - " ]" + replace: + - x: "*[1]" + - T: "的" # phrase(x 'raised to the' second power) + - x: "*[2]" + - T: "次方" # phrase(x raised to the second 'power') + +- # [xxx] var^n + name: nested-var-squared-or-cubed + tag: power + match: + - "$ClearSpeak_Exponents = 'Auto' and" + - " *[2][self::m:mrow][count(*)=3][ " + - " *[3][self::m:power][" + - " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3 + - " *[1][self::m:mi]" + - " ] and " + - " *[2][self::m:mo][.='⁢'] and " # invisible times + # base is mn, or common fraction ([xxx] case) + - " *[1][self::m:mn or " + - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and" + - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]" + - " ]" + - " ]" + replace: + - x: "*[1]" + - T: "的" # phrase(x 'raised to the' second power) + - x: "*[2]" + - T: "次方" # phrase(x raised to the second 'power') + +- # -[xxx] var^n + name: nested-negative-var-squared-or-cubed + tag: power + match: + - "$ClearSpeak_Exponents = 'Auto' and" + - " *[2][self::m:mrow][count(*)=3][ " + - " *[3][self::m:power][" + - " *[2][self::m:mn][.='2' or .='3'] and " # exp is 2 or 3 + - " *[1][self::m:mi]" + - " ] and " + - " *[2][self::m:mo][.='⁢'] and " # invisible times + - " *[1][self::m:minus and count(*)=1 and " + # base is mn, or common fraction ([xxx] case) + - " *[1][self::m:mn or " + - " self::m:fraction[*[1][self::m:mn][not(contains(., '.')) and text()<20] and" + - " *[2][self::m:mn][not(contains(., '.')) and 2<= text() and text()<=10]]" + - " ]" + - " ]" + - " ]" + replace: + - x: "*[1]" + - T: "的" # phrase(x 'raised to the' second power) + - x: "*[2]" + - T: "次方" # phrase(x raised to the second 'power') + +- name: default-exponent-power + tag: power + match: # directly a superscript or an mrow that contains a superscript + - "*[2][self::m:power or self::m:power or self::m:mrow[m:power]]" + replace: + - x: "*[1]" + - T: "的指数为" # phrase(x is 'raised to the exponent') + - pause: short + - x: "*[2]" + - pause: short + - T: "结束指数" # phrase(and now 'end exponent' has been reached) + +- name: default + tag: power + match: "." + replace: + - x: "*[1]" + - T: "的" # phrase(x 'raised to the' second power) + - x: "*[2]" + - T: "次方" # phrase(x raised to the second 'power') + +# +# Some rules on mrows +# +- # the inference rules lump absolute value and cardinality together, so those rules are implemented here + name: ClearSpeak-absolute-value + tag: absolute-value + match: "." + variables: + - WordToSay: "IfThenElse($ClearSpeak_AbsoluteValue = 'Cardinality', '的基数', '的绝对值')" + - EndToSay: "IfThenElse($ClearSpeak_AbsoluteValue = 'Cardinality', '结束基数', '结束绝对值')" + replace: + - x: "*[1]" + - x: "$WordToSay" + - test: + if: "$ClearSpeak_AbsoluteValue = 'AbsEnd'" + then: + - pause: short + - x: "$EndToSay" + - pause: short + +- name: set + tag: set + # Empty set and braced/{set-builder} forms (typically 0-1 child). + # Multi-arg intent set($x,$y,...) and set($x) with @arg fall through to function-intent. + match: "count(*)=0 or (count(*)=1 and not(*[1][@arg]))" + replace: + - test: + - if: "count(*)=0" + then: [T: "空集"] # phrase('the empty set') + - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']" + then: + - test: + if: "$ClearSpeak_Sets != 'woAll'" + then: [T: "所有满足"] + else: [T: "满足"] # phrase(the set S 'such that' x is less than y) + - x: "*[1]/*[3]" + - T: "的" + - x: "*[1]/*[1]" + - T: "的集合" + else: + - x: "*[1]" + - test: + if: "$ClearSpeak_Sets != 'SilentBracket'" + then: [T: "的集合"] + +- # intervals are controlled by a ClearSpeak Preference -- parens/brackets don't have to match, so we avoid IsBracketed + # alternatively, we could have four (or ten) rules, but there is a lot of duplication if we do that + # this one rule handles all ten cases listed as part $ClearSpeak_Paren = 'Interval' + # note that *[2] is an mrow with X, ",", Y, so getting X or Y is a double index + name: ClearSpeak-intervals # avoid overriding with default "intervals" name + variables: + - is_intervals_start_infinity: "*[1][self::m:minus and *[1][text()='∞']]" + - is_intervals_end_infinity: "*[2][text()='∞'or (self::m:plus and *[1][text()='∞'])]" + tag: [open-interval, open-closed-interval, closed-interval, closed-open-interval] + match: "." + replace: + - T: "从" # phrase('the interval from' a to b) + - x: "*[1]" + - T: "到" # phrase(the interval from a 'to' b) + - x: "*[2]" + - T: "的区间" + - pause: short + - test: + if: "not($is_intervals_start_infinity)" + then: + - test: + if: "starts-with(name(.), 'open')" + then: [T: "不"] # phrase(the interval from a to b 'not' including b) + - T: "包含" # phrase(the interval from a to b not 'including' b) + - x: "*[1]" + # logic to deal with [not] arg #1 + - test: + if: "not($is_intervals_start_infinity or $is_intervals_end_infinity)" + then_test: + - if: "name(.)='open-interval'" + then: [T: "或"] # phrase(the interval including a 'or' b ) + - else_if: "name(.)='closed-interval'" + then: [T: "且"] # phrase(the interval including a 'and' b) + else: [T: "但"] # phrase(the interval including a 'but' not b) + # some ugly logic dealing with connectives: or, but, but, and (cleaner to be part of next clause?) + - test: + if: not($is_intervals_end_infinity) + then: + - test: + # there is some asymmetry to the test because of the and/or/but logic above + if: not( name(.)='open-interval' or name(.)='closed-interval' ) or $is_intervals_start_infinity + then: + - test: + if: "name(.) = 'open-interval' or name(.) = 'closed-open-interval'" + then: [T: "不"] # phrase(the interval 'not' including a) + - T: "包含" # phrase(the interval not 'including' a) + - x: "*[2]" + + # onto the [not] [including]... part +- name: binomial-frac-vector + tag: matrix + match: + - "$ClearSpeak_Matrix = 'Combinatorics' and " + - "count(*[1]/*)=1 and count(*)=2" + replace: + - x: "*[1]/*[1]/*" # mtable/mtr/mtd + - T: "取" # phrase(the binomial coefficient n 'choose' m) + - x: "*[2]/*[1]/*" + +- name: ClearSpeak-default + tag: [mtr, mlabeledtr] + match: "parent::m:matrix or parent::m:determinant" + variables: [NextLineIsContinuedRow: "following-sibling::*[1][contains(@data-intent-property, ':continued-row:')]"] + replace: + - pause: medium + - T: "行" # phrase(the first 'row' of a matrix) + - x: "count(preceding-sibling::*)+1" + - test: + if: ".[self::m:mlabeledtr]" + then: + - T: "标签为" # phrase(the line 'with label' first equation) + - x: "*[1]/*" + - pause: short + - pause: medium + - test: + if: ".[self::m:mlabeledtr]" + then: [x: "*[position()>1]"] + else: [x: "*"] + +- # handle both log and ln + name: ClearSpeak-log + tag: mi + match: ".='log' or .='ln'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity!='Terse' and not(IsNode(following-sibling::*[2],'simple'))" + then: [OT: ""] + - test: + - if: ".='log'" + then: [T: "对数"] + - else_if: "$ClearSpeak_Log = 'LnAsNaturalLog'" + then: [T: "自然对数"] + else: [SPELL: "'ln'"] + +- name: ClearSpeak-multi-line + tag: [piecewise, system-of-equations, lines] # these are ignored in favor of the ClearSpeak prefs + match: "." + variables: + - LineCount: "count(*[not(contains(@data-intent-property, ':continued-row:'))])" + - NextLineIsContinuedRow: "false()" + - IsColumnSilent: true() + replace: + - test: + - if: "$ClearSpeak_MultiLineOverview = 'Auto'" + then: + - x: "$LineCount" + - test: + - if: "($ClearSpeak_MultiLineLabel = 'Auto' and self::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" + then: [T: "分支"] # phrase(this is the first 'case' of three cases) + - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line' or $ClearSpeak_MultiLineLabel = 'None'" + then: [T: "行"] # phrase(this is the first 'line' of three lines) + - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" + then: [T: "约束"] # phrase(this is the first 'constraint' of three constraints) + - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" + then: [T: "方程"] # phrase(this is the first 'equation' of three equations) + - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" + then: [T: "行"] # phrase(this is the first 'row' of three rows) + - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" + then: [T: "步"] # phrase(this is the first 'step' of three steps) + - test: + - if: "$LineCount != 1" + then: [CT: ""] # Chinese has no plural suffix. + - pause: short + - x: "*" + - pause: long + +- name: ClearSpeak-default-multiline + tag: [mtr, mlabeledtr] + match: "parent::m:piecewise or parent::m:system-of-equations or parent::m:lines" + variables: [NextLineIsContinuedRow: "following-sibling::*[1][contains(@data-intent-property, ':continued-row:')]"] + replace: + - test: + if: "not($LineCount=1 or $ClearSpeak_MultiLineLabel='None' or contains(@data-intent-property, ':continued-row:'))" + then: + - pause: medium + - test: + - if: "($ClearSpeak_MultiLineLabel = 'Auto' and parent::m:piecewise) or $ClearSpeak_MultiLineLabel = 'Case'" + then: [T: "分支"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Auto' or $ClearSpeak_MultiLineLabel = 'Line'" + then: [T: "行"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Constraint'" + then: [T: "约束"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Equation'" + then: [T: "方程"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Row'" + then: [T: "行"] + - else_if: "$ClearSpeak_MultiLineLabel = 'Step'" + then: [T: "步"] + - x: "count(preceding-sibling::*[not(contains(@data-intent-property, ':continued-row:'))]) + 1" + - test: + if: "self::m:mlabeledtr" + then: + - T: "标签为" + - x: "*[1]/*" + - test: + - if: "$ClearSpeak_MultiLineLabel='None'" + then: [pause: xlong] + - else_if: "not(contains(@data-intent-property, ':continued-row:'))" + then: [pause: medium] + - test: + if: "self::m:mlabeledtr" + then: [x: "*[position()>1]"] + else: [x: "*"] + +- name: ClearSpeak_Functions_None + tag: mo + match: + - ".='⁡' and $ClearSpeak_Functions = 'None' and" + - "not(preceding-sibling::*[1][IsInDefinition(., 'TrigFunctionNames')])" # Functions=None does not apply to "trig" functions + replace: + test: + if: "$ClearSpeak_ImpliedTimes = 'None'" + then: [T: ""] + else: [T: "乘"] # phrase(5 'times' 3 equals 15) + +- name: no-times + tag: mo + match: + # Note: this rule is also part of the paren rule so that the parens speak + - ".='⁢' and $ClearSpeak_ImpliedTimes = 'None'" + replace: + - T: "" + +- name: ClearSpeak-times + tag: mo + match: + # say "times" when invisible times is followed by parens or a superscript that has a base with parens or "|"s + # if we aren't sure if it is times or not, don't say anything + - ".='⁢' and (not(@data-function-guess) or $ClearSpeak_Functions = 'None') and" + - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')]) and (" + - " $ClearSpeak_ImpliedTimes = 'MoreImpliedTimes'" + - " or " + - " 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')))" + - " )" + - " ]" + # other possibility is the preceding element has parens (but not the following) + # this is not mentioned in the ClearSpeak rules or examples but seems like it should say "times". E.g, |x| y + - " or " + - " preceding-sibling::*[1][" + - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')]" # followed by parens + - " )" + replace: + - T: "乘" # phrase(5 'times' 3 equals 15) + +- 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 " # rule out [x] standing alone + - "( IsBracketed(., '(', ')') or IsBracketed(., '[', ']') ) and " + - "not( $ClearSpeak_Functions = 'None' and " + - " (preceding-sibling::*[1][.='⁡'] or following-sibling::*[1][.='⁡']) ) and " + - "not( $ClearSpeak_ImpliedTimes = 'None' and " + - " (preceding-sibling::*[1][.='⁢'] or following-sibling::*[1][.='⁢']) ) and " + - "IsNode(*[2], 'simple') and" + - "not(preceding-sibling::*[1][.='⁢' and @data-function-guess]) and" + - "not(ancestor-or-self::*[contains(@data-intent-property, ':literal:')])" + # missing clause: 'a positive fraction that is spoken as an ordinal + # (either by the Ordinal preference or by the default rules)' + replace: + - x: "*[2]" + +- include: "SharedRules/geometry.yaml" +- include: "SharedRules/linear-algebra.yaml" +- include: "SharedRules/calculus.yaml" +- include: "SharedRules/general.yaml" +- include: "SharedRules/default.yaml" diff --git a/Rules/Languages/zh/SharedRules/calculus.yaml b/Rules/Languages/zh/SharedRules/calculus.yaml new file mode 100644 index 000000000..fc137c7bb --- /dev/null +++ b/Rules/Languages/zh/SharedRules/calculus.yaml @@ -0,0 +1,56 @@ +--- + +- name: laplacian + tag: laplacian + match: "count(*) <= 1" # can be on ∇^2 or on enclosing mrow + replace: + - T: "拉普拉斯算子" # phrase('laplacian' of x) + - test: + if: "count(*) = 1" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase(function 'of' one variable) -- note OneCore voices spell out "div" + - 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: "散度"] # phrase('div' is short for divergence) + else: [T: "散度"] # phrase('divergence of' this function from the mean) + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" + +- name: curl + tag: curl + match: "count(*) = 1" + replace: + - T: "旋度" # phrase(the 'curl of' a field) + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase(function 'of' one variable) + - 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: "梯度"] # phrase(the hill has a 'gradient of' five percent) + else: [T: "梯度"] # use the standard Chinese mathematical term in terse mode too + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [pause: short] + - x: "*[1]" diff --git a/Rules/Languages/zh/SharedRules/default.yaml b/Rules/Languages/zh/SharedRules/default.yaml new file mode 100644 index 000000000..f11cf57dd --- /dev/null +++ b/Rules/Languages/zh/SharedRules/default.yaml @@ -0,0 +1,765 @@ +--- +#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)" + + - MatchingPause: false() + # should be set at mtable level, but unknown intents make that impossible to know + - IsColumnSilent: false() + 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: [SPELL: "text()", pause: "short"] + else: [x: "translate(., $BlockSeparators, '')"] # 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 and text() != '_'" # need unicode.tdl to kick in for single letter tokens + then: [x: "text()"] + - else_if: "@data-chem-element or @data-roman-numeral" # NavMode=Character needs this + then: [SPELL: "text()", pause: "short"] + else: [x: "translate(., '-_\u00A0', ' ')"] # from intent literals or from extra spaces added (which get deleted) + +- name: default + tag: ms + match: "." + replace: + - T: "字符串" # phrase('the string' is long) + - 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: "*[2]" + - T: "分之" # phrase("the fraction x 'over' y") + - x: "*[1]" + - pause: short + +- name: literal-default + tag: mfrac + match: "." + replace: + - T: "分数" # phrase("'start' fraction x over y end of fraction") + - pause: short + - x: "*[2]" + - test: + if: "not(IsNode(*[2],'leaf'))" + then: [pause: short] + - T: "分之" # phrase("the fraction x 'over' y") + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [pause: short] + - x: "*[1]" + - pause: short + - test: + if: "$Impairment = 'Blindness'" + then: [T: "结束分数"] # 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: "根号" + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase("the root 'of' x") + - x: "*[1]" + - pause: short + - test: + if: "not(IsNode(*[1],'leaf')) or $Impairment = 'Blindness'" + then: [T: "结束根号", pause: medium] # 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: "根指数" # phrase("the root of x 'with index' 5") + - x: "*[2]" + - T: "根号" + - pause: short + - x: "*[1]" + - pause: short + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [T: "结束根号"] # phrase("root of x 'end root symbol'") + + +- name: simple-sub + tag: indexed-by + # invisible comma -- want "x 1 when subscript is an integer" + 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: "下标" # phrase(x 'sub' 2) + - x: "*[2]" + - pause: short + +- name: power-indexed-by + tag: power-indexed-by + match: "." + replace: + - x: "*[1]" + - T: "下标" # phrase(x 'sub' 2) + - x: "*[2]" + - pause: short + +# otherwise let definitions/default infix handle it + +- name: literal + tag: msub + match: "." + replace: + - x: "*[1]" + - test: + if: "not($Verbosity='Terse' and *[2][self::m:mn and not(translate(., '.,', '')!=.)])" # just say "x 1" for terse vs "x sub 1" + then: [T: "下标"] # phrase(x 'sub' 2) + - x: "*[2]" + + +- name: literal + tag: [msup, msubsup] + match: "." + replace: + - x: "*[1]" + - test: + if: "name(.)='msubsup'" + then: + - T: "下标" # phrase(x 'sub' 2) + - x: "*[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: + - T: "上标" # phrase(x 'super' 2) + - x: "*[last()]" + - test: + if: "not(IsNode(*[last()], 'simple')) or $Impairment = 'Blindness'" + then: [T: "结束上标"] # phrase(x super 2 'end of super') + else: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] + else: [T: "上标"] + - x: "*[last()]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "结束上标"] + else: [T: "结束上标"] + +- name: default + tag: munder + match: "." + replace: + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [T: ""] # phrase(phrase(x 'modified' with y above it) + - x: "*[1]" + - T: "下方有" # phrase(x 'with' z below it) + - x: "*[2]" + - T: "" # phrase(x with z 'below' it) + +- 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: ""] # phrase(phrase(the 'quantity' x plus 1 with y above it) + - x: "*[1]" + - T: "上方有" # phrase(x modified 'with' y above it) + - x: "*[2]" + - T: "" # phrase(x modified 'with' y above it) + +- name: default + tag: munderover + match: "." + replace: + - test: + if: "not(IsNode(*[1], 'leaf'))" + then: [T: ""] # phrase(the 'quantity' x plus 1 with y above it) + - x: "*[1]" + - T: "下方有" # phrase(x modified 'with' y above it) + - x: "*[2]" + - T: "上方有" # phrase(x modified with y 'below and' y above it) + - x: "*[3]" + - T: "" # phrase(x modified with y 'above' it) + +- 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', '前下标', '前下标')" + - PreSuperscript: "IfThenElse($Verbosity='Verbose', '前上标', '前上标')" + replace: + - test: # only bother announcing if there is more than one prescript + if: "count($Prescripts) > 2" + then: + - T: "有" # phrase(substitute x 'with' y) + - x: "count($Prescripts) div 2" + - T: "组前置上下标" # phrase(in this equation certain 'prescripts' apply) + - 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: "且"] # phrase(10 is greater than 8 'and' less than 15) + - 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: "且"] # phrase(10 is grater than 8 'and' less than 15) + - 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: "其余前置下标、上标依次交替" # phrase(in this case there are values 'and alternating prescripts') + - x: "$Prescripts[position() > 4]" + - T: "结束前置上下标" # phrase(This is where 'end prescripts' occurs) + - test: + if: "$Postscripts" + then: + - with: + variables: + - PostSubscript: "IfThenElse($Verbosity='Verbose', '下标', '下标')" + - PostSuperscript: "IfThenElse($Verbosity='Verbose', '上标', '上标')" + replace: + - test: # only bother announcing if there is more than one postscript + if: "count($Postscripts) > 2" + then: + - test: + if: "$Prescripts" + then: [T: "且"] # phrase(10 is greater than 8 'and' less than 15) + - T: "有" # phrase(substitute x 'with' y) + - x: "count($Postscripts) div 2" + - T: "组后置上下标" # phrase(this material includes several '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: "且"] # phrase(10 is greater than 8 'and' less than 15) + - 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: "且"] # phrase(10 is greater than 8 'and' less than 15) + - 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: "且"] # phrase(10 is greater than 8 'and' less than 15) + - 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: "且"] # phrase(10 is less than 15 'and' greater than 5) + - 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: "其余后置下标、上标依次交替" # phrase(this situation involves complexities 'and alternating scripts') + - x: "$Postscripts[position() > 8]" + - T: "结束后置上下标" # phrase(At this point 'end scripts' occurs) + +- name: default + tag: mtable + variables: + - IsColumnSilent: "false()" + - NumColumns: "count(*[1]/*) - IfThenElse(*/self::m:mlabeledtr, 1, 0)" + match: "." + replace: + - T: "表格有" # phrase(the 'table with' 3 rows) + - x: count(*) + - test: + if: count(*)=1 + then: [T: "行"] # phrase(the table with 1 'row') + else: [T: "行"] # phrase(the table with 3 'rows') + - T: "与" # phrase(the table with 3 rows 'and' 4 columns) + - x: "$NumColumns" + - test: + if: "NumColumns=1" + then: [T: "列"] # phrase(the table with 3 rows and 1 'column') + else: [T: "列"] # phrase(the table with 3 rows and 4 'columns') + - 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: + - pause: medium + - T: "行" # phrase(the first 'row' of a matrix) + - x: "count(preceding-sibling::*)+1" + - test: + if: self::m:mlabeledtr + then: + - T: "带有标签" # 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: "列" # phrase(the first 'column' of the matrix) + - x: "count(preceding-sibling::*)+IfThenElse(parent::m:mlabeledtr, 0, 1)" + - pause: medium + - x: "*" + - test: + if: "HasVisibleColumnLine(../.., count(preceding-sibling::*) + 1)" + then: + - pause: short + - T: "列分隔线" + - test: + # Announce a row separator only after the row's final cell, so it is spoken once per horizontal boundary. + if: "count(following-sibling::*) = 0 and HasVisibleRowLine(../.., count(../preceding-sibling::*) + 1)" + then: + - pause: short + - T: "行分隔线" + - 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 .=' ']" + replace: + - T: "空盒子" # 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: "盒子", pause: short] # phrase(the 'box' around the expression) + - test: + if: ".[contains(@notation,'roundedbox')]" + then: [T: "圆角框", pause: short] # phrase(the 'round box' around the expression) + - test: + if: ".[contains(@notation,'circle')]" + then: [T: "圆圈", pause: short] # phrase(the 'circle' around the expression) + - 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: "线" # phrase(draw a straight 'line' on the page) + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' left ')]" + then: [T: "左边", pause: short] # phrase(line on 'left' of the expression) + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' right ')]" + then: [T: "右边", pause: short] # phrase(line on 'right' of the expression) + - test: + if: ".[contains(@notation,'top')]" + then: [T: "顶部", pause: short] # phrase(line on 'top' of the expression) + - test: + if: ".[contains(@notation,'bottom')]" + then: [T: "底部", pause: short] # phrase(line on the 'bottom' of the expression) + - test: + if: ".[ contains(@notation,'updiagonalstrike') or contains(@notation,'downdiagonalstrike') or contains(@notation,'verticalstrike') or contains(@notation,'horizontalstrike') ]" + then: + - test: + if: ".[contains(@notation,'updiagonalstrike') and contains(@notation,'downdiagonalstrike')]" + then: [SPELL: "'叉叉'", pause: short] # seems better to say 'x cross out' than 'up diagonal, down diagonal cross out' + else: + - test: + if: ".[contains(@notation,'updiagonalstrike')]" + then: [T: "右上对角线", pause: short] # phrase(the line runs 'up diagonal') + - test: + if: ".[contains(@notation,'downdiagonalstrike')]" + then: [T: "右下对角线", pause: short] # phrase(the line runs 'down diagonal') + - test: + if: ".[contains(@notation,'verticalstrike')]" + then: [T: "垂直", pause: short] # phrase(the line is 'vertical') + - test: + if: ".[contains(@notation,'horizontalstrike')]" + then: [T: "水平", pause: short] # phrase(the line is 'horizontal') + - T: "划掉" # phrase(please 'cross out' the incorrect answer) + - pause: short + - test: + if: ".[contains(@notation,'uparrow')]" + then: [T: "向上箭头", pause: short] # phrase(direction is shown by the 'up arrow') + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' downarrow ')]" + then: [T: "向下箭头", pause: short] # phrase(the trend is shown by the 'down arrow') + - test: + if: ".[contains(@notation,'leftarrow')]" + then: [T: "左箭头", pause: short] # phrase(the 'left arrow' indicates going back) + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' rightarrow ')]" + then: [T: "右箭头", pause: short] # phrase(the 'right arrow' indicates moving forward) + - test: + if: ".[contains(@notation,'northeastarrow')]" + then: [T: "右上箭头", pause: short] # phrase(direction is indicated by the 'northeast arrow') + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southeastarrow ')]" + then: [T: "右下箭头", pause: short] # phrase(direction is shown by the 'southeast arrow') + - test: + if: ".[contains(concat(' ', normalize-space(@notation), ' '), ' southwestarrow ')]" + then: [T: "左下箭头", pause: short] # phrase(direction is shown by the 'southwest arrow') + - test: + if: ".[contains(@notation,'northwestarrow')]" + then: [T: "左上箭头", pause: short] # phrase(direction is shown by the 'northwest arrow') + - test: + if: ".[contains(@notation,'updownarrow')]" + then: [T: "垂直上下箭头", pause: short] # phrase(upward movement is indicated by the 'double ended vertical arrow') + - test: + if: ".[contains(@notation,'leftrightarrow')]" + then: [T: "水平左右箭头", pause: short] # phrase(progress is indicated by the 'double ended horizontal arrow') + - test: + if: ".[contains(@notation,'northeastsouthwestarrow')]" + then: [T: "左上两端箭头", pause: short] # phrase(trend is indicated by the 'double ended up diagonal arrow') + - test: + if: ".[contains(@notation,'northwestsoutheastarrow')]" + then: [T: "右下两端箭头", pause: short] # phrase(trend is indicated by the 'double ended down diagonal arrow') + - test: + if: ".[contains(@notation,'actuarial')]" + then: [T: "精算符号", pause: short] # phrase(the 'actuarial symbol' represents a specific quantity) + - test: + if: ".[contains(@notation,'madrub')]" + then: [T: "阿拉伯阶乘符号", pause: short] # phrase(the 'arabic factorial symbol' represents a factorial operation) + - test: + if: ".[contains(@notation,'phasorangle')]" + then: [T: "相角", pause: short] # phrase(the 'phasor angle' is used to measure electrical current) + - test: + if: ".[contains(@notation,'longdiv') or not(@notation) or normalize-space(@notation) ='']" # default + then: [T: "长除号", pause: short] # phrase(the 'long division symbol' indicates a long division calculation) + - test: + if: ".[contains(@notation,'radical')]" + then: [T: "平方根", pause: short] # phrase(5 is the 'square root' of 25) + - T: "包围" # phrase(parentheses are 'enclosing' part of the equation) + - test: + if: "*[self::m:mtext and .=' ']" + then: [T: "空格"] # otherwise there is complete silence # phrase(there is a 'space' between the words) + else: [x: "*"] + - test: + if: "$Impairment = 'Blindness' and ( $SpeechStyle != 'SimpleSpeak' or not(IsNode(*[1], 'leaf')) )" + then: [T: "结束包围"] # phrase(reached the 'end enclosure' point) + - 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: "" # phrase(the sine 'of' x ) + - pause: auto + - x: "*[position() > 1]" + +# Here are the intent hints that need to be handled: 'prefix' | 'infix' | 'postfix' | 'function' | 'silent' +- name: silent-intent + # uncaught intent -- the args have been inserted in the order of speech + tag: "*" + match: "count(*)>0 and (contains(@data-intent-property, ':silent:') or translate(name(.), '-_', '')='')" + replace: + - x: "*" + - test: + if: "IsNode(., '2D')" + then: [pause: short] + else: [pause: auto] + +- name: nofix-intent + # uncaught intent -- the args have been inserted in the order of speech + tag: "*" + match: "contains(@data-intent-property, ':nofix:') " + replace: + - x: "SpeakIntentName(name(.), $Verbosity, 'nofix')" + +- name: prefix-intent + # uncaught intent -- the args have been inserted in the order of speech + 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 + # uncaught intent -- the args have been inserted in the order of speech + 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 + # uncaught intent -- the args have been inserted in the order of speech + 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" # in cases such as continued-row, plus/minus might have just one child + 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')" # add (probably) a slightly longer pause if this came from a 2D node + then: [pause: short] + else: [pause: auto] + +- name: function-intent + # uncaught intent -- speak as foo of arg1 comma arg2 .... + # IntentMappings may add glue options after the name; helper functions select the arity-specific path. + tag: "*" + match: "count(*)>0" + replace: + - x: "SpeakIntentName(name(.), $Verbosity, 'function')" + - test: + - if: "IntentFunctionUseArityPath(name(.), 'function', count(*))" + then: + - x: "IntentFunctionGlueBefore(name(.), 'function', 1, count(*))" + - pause: auto + - insert: + nodes: "*" + replace: + - x: "IntentFunctionGlueBefore(name(.), 'function', $InsertIndex, count(*))" + - pause: auto + else: + - 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: "", pause: auto] # Chinese function names do not need an equivalent of "of". + - insert: + nodes: "*" + replace: + - test: + if: "not(contains(@data-intent-property, ':literal:'))" + then: [x: "IntentFunctionArgSeparator(name(.), 'function', count(*))"] + - pause: auto + - test: + # speak "end ..." if not bracketed or last child is not simple and not last node + 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: leaf-infix-intent + tag: "*" + match: "name(.)='dot-product' or name(.)='cross-product'" + replace: + - x: "SpeakIntentName(name(.), $Verbosity, 'infix')" + + +- name: default-text + # unknown leaf -- just speak the text -- could be a literal intent + tag: "*" + match: "." + replace: + - x: "translate(name(), '-_', ' ')" diff --git a/Rules/Languages/zh/SharedRules/general.yaml b/Rules/Languages/zh/SharedRules/general.yaml new file mode 100644 index 000000000..cf89c6094 --- /dev/null +++ b/Rules/Languages/zh/SharedRules/general.yaml @@ -0,0 +1,1161 @@ +--- + +# number-sets are a little messy in that the base was converted to a number-set, so we have to match that (simple) case last +- name: pos-neg-number-sets + tag: number-sets + match: "count(*)=2 and *[2][.='+' or .='-']" + replace: + - test: + if: "$Verbosity!='Terse'" + then: + - T: "" # phrase('the' square root of 25 equals 5) + - bookmark: "*[2]/@id" + - test: + - if: "*[2][.='+']" + then: [T: "正"] # phrase(set of all 'positive' integers less than 10) + else: [T: "负"] # phrase(set of all 'negative' integers less than minus 10) + - bookmark: "*[1]/@id" + - test: + - if: "*[1][.='ℂ']" + then: [T: "复数集"] # phrase('complex numbers' consist of two parts) + - else_if: "*[1][.='ℕ']" + then: [T: "自然数集"] # phrase('natural numbers' are numbers from 1 to infinity) + - else_if: "*[1][.='ℚ']" + then: [T: "有理数集"] # phrase('rational numbers' are the fraction of 2 integers) + - else_if: "*[1][.='ℝ']" + then: [T: "实数集"] # phrase('real numbers' can be both positive and negative) + - else_if: "*[1][.='ℤ']" + then: [T: "整数集"] # phrase(positive 'integers' are natural numbers above 0) + else: [x: "*[1][text()]"] # shouldn't happen + +- name: dimension-number-sets + + # should be single digit integer at this point (e.g, R^3) + tag: number-sets + match: "count(*)=2" + replace: + - bookmark: "*[1]/@id" + - test: + - if: "*[1][.='ℂ']" + then: [T: "C"] # phrase(the letter 'C' used to represent complex number) + - else_if: "*[1][.='ℕ']" + then: [T: "N"] # phrase(the letter 'N' may represent natural numbers) + - else_if: "*[1][.='ℚ']" + then: [T: "Q"] # phrase(the letter 'Q' may represent rational numbers) + - else_if: "*[1][.='ℝ']" + then: [T: "R"] # phrase(the letter 'R' may represent real numbers) + - else_if: "*[1][.='ℤ']" + then: [T: "Z"] # phrase(the letter 'Z' may represent integers) + else: [x: "*[1][text()]"] # shouldn't happen + - bookmark: "*[2]/@id" + - x: "*[2]" + +- name: simple-number-sets + tag: number-sets + match: "count(*)=0" + replace: + - bookmark: "@id" + - test: + - if: ".='ℂ'" + then: [T: "复数集"] # phrase('the complex numbers' include 2 parts) + - else_if: ".='ℕ'" + then: [T: "自然数集"] # phrase('the natural numbers' begin at 1) + - else_if: ".='ℚ'" + then: [T: "有理数集"] # phrase('the rational numbers' are the fraction of 2 integers) + - else_if: ".='ℝ'" + then: [T: "实数集"] # phrase('the real numbers' can be both positive and negative) + - else_if: ".='ℤ'" + then: [T: "整数集"] # phrase('the integers' are natural numbers above 0) + else: [x: "text()"] # shouldn't happen + +- name: real-part + tag: real-part + match: "count(*)=0" + replace: + - bookmark: "@id" + - T: "实部" # phrase('the real part' of a complex number does not include the imaginary part) + +- name: imaginary-part + tag: imaginary-part + match: "count(*)=0" + replace: + - bookmark: "@id" + - T: "虚部" # phrase('the imaginary part' is part of a complex number) + +# rules on scripted vertical bars ('evaluated at') +- name: evaluated-at-2 + tag: evaluate + match: "count(*)=2" + replace: + - x: "*[1]" + - pause: auto + - T: "在" # phrase(results were 'evaluated at' a given point) + - pause: auto + - x: "*[2]" + - T: "处的值" + +- name: evaluated-at-3 + tag: evaluate + match: "count(*)=3" + replace: + - x: "*[1]" + - pause: auto + - T: "在" # phrase(results were 'evaluated at' this point) + - pause: auto + - x: "*[3]" + - T: "处的值减去同一表达式在" # phrase(this result is 'minus the same expression evaluated at' an earlier point) + - x: "*[2]" + - T: "处的值" + +# Chinese places the result noun after both operands. +- name: quotient # audit-ignore: Chinese places the result noun after both operands. + tag: quotient + match: "count(*)=2" + replace: + - x: "*[1]" + - T: "除以" + - x: "*[2]" + - T: "的商" + +- name: remainder # audit-ignore: Chinese places the result noun after both operands. + tag: remainder + match: "count(*)=2" + replace: + - x: "*[1]" + - T: "除以" + - x: "*[2]" + - T: "的余数" + +- name: permutation + # Not a default because the order of the args is reversed + tag: pochhammer + match: "count(*)=2 and contains(@data-intent-property, ':infix:')" + replace: + - x: "*[1]" + - T: "排列" # phrase(the solution involves several 'permutations of' values) + - x: "*[2]" + +- name: intervals + tag: [open-interval, open-closed-interval, closed-interval, closed-open-interval] + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then: + - T: "" # phrase('the' square root of 25 equals 5) + #- x: "translate(name(.),'-',' ')" + - test: + if: name(.) = 'open-interval' + then: + - T: "开区间" + else_test: + if: name(.) = 'closed-interval' + then: + - T: "闭区间" + else_test: + if: name(.) = 'open-closed-interval' + then: + - T: "左开右闭区间" + else_test: + if: name(.) = 'closed-open-interval' + then: + - T: "左闭右开区间" + - test: + if: "$Verbosity='Verbose'" + then: + - T: "从" # phrase(subtracting 5 'from' 10 gives 5) + - x: "*[1]" + - T: "到" # phrase(adding 6 'to' 6 equals 12) + - x: "*[2]" + else: + - x: "*[1]" + - T: "逗号" # phrase(use a 'comma' to divide large numbers or as a decimal point) + - x: "*[2]" + +- name: default-point + tag: point + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then: + - T: "" # phrase('the' square root of 25 equals 5) + - T: "点" # phrase(a decimal 'point' indicates the fraction component of a number) + - x: "*[1]" + - T: "逗号" # phrase(use a 'comma' to divide large numbers or as a decimal point) + - x: "*[2]" + +- name: absolute-value # audit-ignore: Chinese places the function name after its argument. + # order of speech is different from 'function' + tag: absolute-value + match: "count(*)=1" + replace: + - x: "*[1]" + - T: "的绝对值" + +- name: bigop-both + tag: large-op + match: "count(*) = 3" + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' square root of 25 equals 5) + - x: "*[1]" + - T: "从" # phrase(subtracting 5 'from' 10 gives 5) + - x: "*[2]" + - T: "到" # phrase(adding 6 'to' 6 equals 12) + - x: "*[3]" + - test: + if: "following-sibling::*" + then: [T: ""] # Chinese does not need an equivalent of "of" here. + +- name: bigop-under + tag: large-op + match: "count(*)=2" + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' square root of 25 equals 5) + - x: "*[1]" + - T: "下限为" # phrase(2 'over' 3 equals two thirds) + - x: "*[2]" + - test: + if: "following-sibling::*" + then: [T: ""] # Chinese does not need an equivalent of "of" here. + +- name: largeop + tag: mrow + match: "count(*)=2 and IsInDefinition(*[1], 'LargeOperators')" + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' square root of 25 equals 5) + - x: "*[1]" + - T: "" # Chinese does not need an equivalent of "of" here. + - x: "*[2]" + +- name: repeating-decimal + tag: repeating-decimal + match: "." + replace: + - x: "*[1]" + - T: "循环节为" # phrase('with repeating digits') + - SPELL: "*[2]" + +- name: angle-measure # audit-ignore: Chinese places the degree phrase after the angle name. + tag: [angle-measure, measured-angle] + match: "count(*)=1 and contains(@data-intent-property, ':prefix:')" + replace: + - T: "角" + - x: "*[1]" + - T: "的度数" + +- name: msubsup-skip-super + # handles single, double, etc., prime + tag: [skip-super, say-super] + match: "count(*)=3" + replace: + - x: "*[1]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(a 'subscript' may be used to indicate an index) + else: [T: "下标"] # phrase(the result is 'sub' optimal) + - x: "*[2]" + - test: + if: "not(IsNode(*[2],'leaf') and $Impairment = 'Blindness')" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "结束下标"] # phrase(this is the 'end subscript' position) + else: [T: "结束下标"] # phrase(this is the 'end sub' position) + - pause: short + else_test: + if: "*[2][self::m:mi]" # need a pause in "x sub k prime" so the prime is not associated with the 'k' + then: [pause: short] + - test: + if: "name(.)='say-super'" + then_test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(a 'superscript' number indicates raised to a power) + else: [T: "上标"] # phrase(this is a 'super' set of numbers) + - x: "*[3]" + - pause: short + +# in terse mode, we just say "m" or "s", etc., not meters or seconds +- name: unit-terse + tag: unit + match: "$Verbosity = 'Terse' and string-length(.)=1" + replace: + - bookmark: "@id" + - SPELL: "text()" + +# the order of matching is +# 1. does it match the base of an SI unit +# 2. does it match an English unit (if in an English language) +# 3. does it match an SI prefix followed by an SI that accepts SI prefixes +# Due to this order, some things like "ft" and "cd" mean "feet" vs "femto-tonnes" and "pints" vs "pico-tonnes" +- name: unit + tag: unit + match: "." + variables: + # If the coefficient is singular, we don't add the plural ending. Finding the coefficient is tricky + # Normal case (A) "3m" (parents is mrow), but could also be (B) "3 m^2" (etc.) (parent is power/mrow) + # But it might be in a fraction as (C) "3 m/s" (parent is fraction/mrow) or (D) "3 m^2/s^2" (parent is power/fraction/mrow) + # or even (E) {3 m^2}/s (parent is power/mrow) + # If in a fraction, only look in the numerator to find the coefficient + # Note: we have a special case for pseudo-scripts like "°" (degrees) which are not powers -- they are essentially "1^°" + # The following "IfThenElse" logic returns the mrow that potentially contains the coefficient, if it exists + # The tests are in the order A, B & E, C, D + - MRowForCoefficient: "IfThenElse(parent::m:mrow, parent::m:mrow, + IfThenElse(parent::m:power, ancestor::*[2][self::m:mrow], + IfThenElse(parent::m:fraction and not(preceding-sibling::*), ancestor::*[2][self::m:mrow], + IfThenElse(parent::m:power[parent::m:fraction and not(preceding-sibling::*)], ancestor::*[3][self::m:mrow], false()) ) ) )" + - IsSingular: "(not($MRowForCoefficient) and parent::*[name(.)!='skip-super' or *[1][.=1]]) or + ($MRowForCoefficient and $MRowForCoefficient[(count(*) = 3 and *[1][self::m:mn and .=1] and *[2]='\u2062')])" + - Prefix: "''" + - Word: "''" + replace: + - bookmark: "@id" + - test: + # is the whole string match a SI Unit without a prefix? + - if: "DefinitionValue(., 'Speech', 'SIUnits') != ''" + then: + - set_variables: [Word: "DefinitionValue(., 'Speech', 'SIUnits')"] + - else_if: "DefinitionValue(., 'Speech', 'UnitsWithoutPrefixes') != ''" + then: + - set_variables: [Word: "DefinitionValue(., 'Speech', 'UnitsWithoutPrefixes')"] + - else_if: "DefinitionValue(., 'Speech', 'EnglishUnits') != ''" + then: + - set_variables: [Word: "DefinitionValue(., 'Speech', 'EnglishUnits')"] + + # do the first two chars match "da" and the remainder match an SIUnit + - else_if: "string-length(.) >= 3 and + substring(., 1, 2) = 'da' and + DefinitionValue(substring(., 3), 'Speech', 'SIUnits') != ''" + then: + - set_variables: + - Prefix: "DefinitionValue('da', 'Speech', 'SIPrefixes')" + - Word: "DefinitionValue(substring(., 3), 'Speech', 'SIUnits')" + + # does the first char match a prefix and the remainder match an SIUnit + - else_if: "string-length(.) >= 2 and + DefinitionValue(substring(., 1, 1), 'Speech', 'SIPrefixes') != '' and + DefinitionValue(substring(., 2), 'Speech', 'SIUnits') != ''" + then: + - set_variables: + - Prefix: "DefinitionValue(substring(., 1, 1), 'Speech', 'SIPrefixes')" + - Word: "DefinitionValue(substring(., 2), 'Speech', 'SIUnits')" + + # not a known unit -- just speak the text, possibly as a plural + - else: + - set_variables: + - Word: "text()" + + # somewhat complicated logic to avoid spaces around "-" as in "centi-grams" vs "centi - grams" -- probably doesn't matter + - test: + if: "$Prefix = ''" + then: + - test: + - if: "$IsSingular" + # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatinated to the base + then: [x: "$Word"] + - else_if: "DefinitionValue($Word, 'Speech', 'PluralForms') != ''" + then: [x: "DefinitionValue($Word, 'Speech', 'PluralForms')"] + else: [x: "$Word"] + else: + - x: "$Prefix" + - CT: "" + - test: + - if: "$IsSingular" + # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets the prefix concatinated to the base + then: [x: "concat('\uF8FE', $Word)"] + - else_if: "DefinitionValue($Word, 'Speech', 'PluralForms') != ''" + then: [x: "concat('\uF8FE', DefinitionValue($Word, 'Speech', 'PluralForms'))"] + else: [x: "concat('\uF8FE', $Word)"] + +# need to reverse the order of speech: $ 3 -> 3 dollars +- name: currency + tag: mrow + match: "count(*)=3 and DefinitionValue(*[1], 'Speech', 'CurrencySymbols') != ''" + variables: + # If the amount is singular, we don't add the plural ending. + - IsSingular: "*[3][self::m:mn and .=1] and *[2]='\u2062'" + - CurrencyWord: "DefinitionValue(*[1], 'Speech', 'CurrencySymbols')" + replace: + - bookmark: "*[3]/@id" + - x: "*[3]" + - test: + - if: "$IsSingular" + then: [x: "$CurrencyWord"] + - else_if: "DefinitionValue($CurrencyWord, 'Speech', 'PluralForms') != ''" + then: [x: "DefinitionValue($CurrencyWord, 'Speech', 'PluralForms')"] + else: [x: "DefinitionValue(*[1], 'Speech', 'CurrencySymbols')", CT: ""] + +- name: sin + tag: mi + match: ".='sin'" + replace: + - bookmark: "@id" + - T: "正弦" # phrase(the 'sine' of the angle) +- name: cos + tag: mi + match: ".='cos'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "余弦"] # phrase('cos' is the abbreviation for cosine) + else: [T: "余弦"] # phrase(find the 'cosine' in a right-angle triangle) +- name: tan + tag: mi + match: ".='tan' or .='tg'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "正切"] # phrase(the 'tan' is the ratio of the opposite to the adjacent side of a right-angled triangle) + else: [T: "正切"] # phrase(a 'tangent' is a straight line that touches a curve) +- name: sec + tag: mi + match: ".='sec'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "正割"] # phrase(to 'seek' a solution) + else: [T: "正割"] # phrase(a 'secant' intersects a curve at two or more points) +- name: csc + tag: mi + match: ".='csc'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "余割"] # phrase(we will 'cosecant' a solution) + else: [T: "余割"] # phrase(the 'cosecant' is the reciprocal of the secant) +- name: cot + tag: mi + match: ".='cot'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "余切"] # phrase(find the 'cotangent' in a right-angle triangle) + else: [T: "余切"] # phrase(the 'cotangent' is the reciprocal of the tangent) + +- name: sinh + tag: mi + match: ".='sinh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲正弦"] # phrase(the word 'sinch' is an abbreviation for hyperbolic sine) + else: [T: "双曲正弦"] # phrase(the 'hyperbolic sine' is used in mathematics) +- name: cosh + tag: mi + match: ".='cosh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲余弦"] # phrase('cosh' is an abbreviation of hyperbolic cosine) + else: [T: "双曲余弦"] # phrase(the 'hyperbolic cosine' is a mathematical function) +- name: tanh + tag: mi + match: ".='tanh'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲正切"] # phrase('tanch' is shorthand for hyperbolic tangent) + else: [T: "双曲正切"] # phrase('hyperbolic tangent' is a mathematical function) +- name: sech + tag: mi + match: ".='sech'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲正割"] # phrase('sheck' is shorthand for hyperbolic secant) + else: [T: "双曲正割"] # phrase('hyperbolic secant' is a mathematical function) +- name: csch + tag: mi + match: ".='csch'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲余割"] # phrase('cosheck' is shorthand for hyperbolic cosecant) + else: [T: "双曲余割"] # phrase('hyperbolic cosecant' is a mathematical function) +- name: coth + tag: mi + match: ".='coth'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "双曲余切"] # phrase('cotanch' is shorthand for hyperbolic cotangent) + else: [T: "双曲余切"] # phrase(the 'hyperbolic cotangent' is a mathematical function) + +- name: exponential + tag: mi + match: ".='exp'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "指数函数"] # phrase('exp' means exponential function) + else: [T: "指数函数"] # phrase('exponential' function) + +- name: covariance + tag: mi + match: ".='Cov'" + replace: + - bookmark: "@id" + - test: + if: "$Verbosity='Terse'" + then: [T: "协方差"] # phrase('Cov' is shorthand for the covariance function) + else: [T: "协方差"] # phrase('covariance' function) + +- name: statistics + tag: [mean, standard-deviation, variance, median, mode] + match: "count(*)=1" + replace: + - x: "*[1]" + - test: + - if: "self::m:mean" + then: [T: "的平均值"] + - else_if: "self::m:standard-deviation" + then: [T: "的标准差"] + - else_if: "self::m:variance" + then: [T: "的方差"] + - else_if: "self::m:median" + then: [T: "的中位数"] + else: [T: "的众数"] + + +- + name: log # handle log, principal Log, and ln (if in an mrow, 'intents' are used) + tag: mi + match: ".='log' or .='Log' or .='ln'" + replace: + - bookmark: "@id" + - test: + - if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' logarithm function is used in mathematics) + - test: + - if: ".= 'log'" + then: [T: "对数"] # phrase(the 'log' function is used in mathematics) + - else_if: ".= 'Log'" + then: [T: "主值对数"] + - else_if: "$Verbosity='Terse'" + then: [SPELL: "'ln'"] + else: [T: "自然对数"] # phrase(the 'natural log' function is used in mathematics) + +- name: multi-line + # that eliminates the need for the if: else_if: ... + # IDEA: set a variable with the word to say for the row (e.g., RowLabel = Row/Case/Line/...) + tag: [piecewise, system-of-equations, lines] + match: "." + variables: + # Wikipedia has some tables where all the entire first column is empty (e.g., https://en.wikipedia.org/wiki/List_of_trigonometric_identities) + - LineCount: "count(*[not(contains(@data-intent-property, ':continued-row:'))])" + - NextLineIsContinuedRow: "false()" # default value + - IsColumnSilent: true() + replace: + - x: "$LineCount" + - test: + - if: "self::m:piecewise" + then: [T: "分支"] # phrase(this is the first 'case' of three cases) + - else_if: "self::m:system-of-equations" + then: [T: "方程"] # phrase(this is the first 'equation' of three equations) + else: [T: "行"] # phrase(this is the first 'line' of three lines) + - test: + - if: "$LineCount != 1" + then: [CT: ""] # Chinese has no plural suffix. + - pause: short + - x: "*" + - pause: long + +- name: default-multiline + tag: [mtr, mlabeledtr] + match: "parent::m:piecewise or parent::m:system-of-equations or parent::m:lines" + variables: [NextLineIsContinuedRow: "following-sibling::*[1][contains(@data-intent-property, ':continued-row:')]"] + replace: + - test: + if: "not($LineCount=1 or contains(@data-intent-property, ':continued-row:'))" + then: + - pause: medium + - test: + - if: "parent::m:piecewise" + then: [T: "分支"] # phrase('case' 1 of 10 cases) + - else_if: "parent::m:system-of-equations" + then: [T: "方程"] # phrase('equation' 1 of 10 equations) + else: [T: "行"] # phrase('line 1 of 10 lines) + - x: "count(preceding-sibling::*[not(contains(@data-intent-property, ':continued-row:'))]) + 1" + - test: + if: "self::m:mlabeledtr" + then: + - T: "带有标签" # phrase(the diagram is complete 'with label') + - x: "*[1]/*" + - test: + if: "not(contains(@data-intent-property, ':continued-row:'))" + then: [pause: medium] + - test: + if: "self::m:mlabeledtr" + then: [x: "*[position()>1]"] + else: [x: "*"] + +- name: default-multiline + tag: mtd + match: "parent::*[parent::m:piecewise or parent::m:system-of-equations or parent::m:lines]" + variables: [LongPause: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_MultiLinePausesBetweenColumns = 'Long'"] + replace: + - test: + if: "IsInDefinition(*[1], 'ComparisonOperators')" + then: [pause: short] + - test: + if: "*[1][@data-added!='missing-content']" + then: [x: "*"] + - test: + # no pause after each element; medium pause if last element in a row; long pause for last element in matrix unless ClearSpeak override + - if: "count(following-sibling::*) = 0 and not($NextLineIsContinuedRow)" + then_test: + if: "count(../following-sibling::*) > 0" + then_test: + if: "$LongPause" + then: [pause: long] + else: [pause: medium] + else_test: + if: "IsInDefinition(*[1], 'ComparisonOperators')" + then: [pause: short] + else: [pause: auto] + +# Matrix/Determinant rules +# matrix and determinant are the same other than "matrix"/"determinant" based on the bracketing chars +# the pausing logic is pushed down to the +# the rules either speak the s (to get "row n") or the s. "column n" spoken if $IsColumnSilent is false +- name: 1x1-matrix + tag: [matrix, determinant] + variables: [IsColumnSilent: true()] + match: "count(*)=1 and *[self::m:mtr][count(*) = 1]" + replace: + - OT: "" # phrase('the' 1 by 1 matrix M) + - T: "1 乘 1" # phrase(the '1 by 1' matrix) + - test: + if: "self::m:determinant" # just need to check the first bracket since we know it must be (, [, or | + then: [T: "行列式"] # phrase(the 2 by 2 'determinant')) + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + + - T: "元素为" # phrase(the 2 by 2 matrix 'with entry' x) + - x: "*[1]/*" + +# simpler reading methods for special case matrices +- name: zero-matrix + tag: matrix + # select all the non-zero entries -- if there are none of them, then it is a zero matrix + match: "not( */*/*[not(self::m:mn and .= 0)] )" + replace: + - T: "" # phrase('the' 1 by 2 matrix M) + - x: count(*) + - T: "乘" # phrase(the 1 'by' 2 matrix) + - x: count(*[self::m:mtr][1]/*) + - T: "零矩阵" # phrase(the 2 by 2 'zero matrix') + - pause: long + +- name: identity-matrix + tag: matrix + # every diagonal entry must be a literal 1, and every off-diagonal entry must be a literal 0 + match: + - "count(*) = count(*[1]/*) and " # matrix is square + - "not( */*[count(preceding-sibling::*) = count(../preceding-sibling::*)]/*[not(self::m:mn and .= 1)] ) and " # on-diagonal + - "not( */*[count(preceding-sibling::*) != count(../preceding-sibling::*)]/*[not(self::m:mn and .= 0)] )" # off-diagonal + replace: + - T: "" # phrase('the' 1 by 2 matrix M) + - x: count(*) + - T: "乘" # phrase(the 1 'by' 2 matrix) + - x: count(*[self::m:mtr][1]/*) + - T: "单位矩阵" # phrase(the 2 by 2 'identity matrix') + - pause: long + +- name: diagonal-matrix + tag: matrix + # select all the non-zero entries...if they are not on the diagonal + # if there are any of them, then this isn't an identity matrix + match: + - "count(*) = count(*[1]/*) and " + - "not( */*/*[not(self::m:mn and .= 0)]" + - " [count(../preceding-sibling::*)!=count(../../preceding-sibling::*)]" + - " )" + replace: + - T: "" # phrase('the' 1 by 2 matrix) + - x: count(*) + - T: "乘" # phrase(the 1 'by' 2 matrix) + - x: count(*[self::m:mtr][1]/*) + - T: "对角矩阵" # phrase(the 2 by 2 'diagonal matrix') + - pause: long + - insert: + # this lists the diagonal 'mtd's to be read, and they say "column nnn" before reading the contents + # there seems to be an xpath bug -- without the parens, the match fails for the + # test Languages::en::mtable::diagonal_matrix due to match failure (the third matching element seems to be missing) + nodes: "(*/*/*[not(self::m:mn and .= 0)]/..)" + replace: [pause: auto] + - pause: long + +# simpler reading methods for smaller matrices if the entries are simple +- name: 2-or-3x1-matrix + tag: matrix + variables: [IsColumnSilent: true()] + match: + - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference + - "*[self::m:mtr][count(*) = 1] and " # one column + - count(*)<=3 and # at least two rows + - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple + replace: + - T: "" # phrase('the' 2 by 2 matrix M) + - x: count(*) + - T: "乘 1" # phrase(the 2 'by 1 column' matrix) + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "向量"] # phrase(the 2 by 2 'vector') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + - pause: long + - x: "*/*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "结束" # phrase('end' of matrix) + - test: + if: $ClearSpeak_Matrix = 'EndVector' + then: [T: "向量"] # phrase(the 2 column 'vector') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + +- name: default-column-matrix + tag: matrix + variables: [IsColumnSilent: true()] + match: "*[self::m:mtr][count(*) = 1]" + replace: + - T: "" # phrase('the' 2 by 2 matrix M) + - x: "count(*)" + - T: "乘 1" # phrase(the 2 'by 1 column' matrix) + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "向量"] # phrase(the 2 column 'vector') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + - pause: long + - x: "*" # select the rows (mtr) + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "结束"] # phrase(the 'end of matrix' has been reached) + +- name: 1x2-or-3-matrix + tag: matrix + variables: [IsColumnSilent: "$SpeechStyle = 'SimpleSpeak' or ($SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix != 'SpeakColNum')"] + match: + - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference + - count(*)=1 and # one row + - count(*[1]/*)<=3 and # at least two cols + - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple + replace: + - T: "1 乘" # phrase('the 1 by' 2 matrix) + - x: count(*/*) + - T: "" # phrase(the 1 by 4 'row' matrix) + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "向量"] # phrase('the 1 by' 2 row 'vector') + else: [T: "矩阵"] # phrase('the 1 by' 2 'matrix') + - pause: long + - x: "*/*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "结束" # phrase(the 'end' of matrix has been reached) + - test: + if: $ClearSpeak_Matrix = 'EndMatrix' + then: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + else: [T: "向量"] # phrase(the 2 by 1 'vector') + +- name: default-row-matrix + tag: matrix + variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] + match: "count(*)=1" # one row + replace: + - T: "1 乘" # phrase('the 1 by' 2 matrix) + - x: "count(*/*)" + - T: "" # phrase(the 1 by 2 'row' matrix) + - test: + if: "$ClearSpeak_Matrix = 'Vector' or $ClearSpeak_Matrix = 'EndVector'" + then: [T: "向量"] # phrase(the 2 by 1 'vector') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + - pause: long + - pause: medium + - x: "*/*" # select the cols (mtd) + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "结束" # phrase(the 'end' of matrix has been reached) + - test: + if: $ClearSpeak_Matrix = 'EndMatrix' + then: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + else: [T: "向量"] # phrase(the 2 by 1 'vector') + +- name: simple-small-matrix + tag: [matrix, determinant] + match: + - "$ClearSpeak_Matrix != 'SpeakColNum' and " # "simple" isn't used for this preference + - (count(*)<=3 and count(*[1]/*)<=3) and # no bigger than a 3x3 matrix + - IsNode(*/*/*,'simple') # IsNode() returns true if all the nodes are simple + variables: [IsColumnSilent: "$SpeechStyle = 'SimpleSpeak' or ($SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix != 'SpeakColNum')"] + replace: + - T: "" # phrase('the' 1 by 2 matrix M) + - x: count(*) + - T: "乘" # phrase(the 1 'by' 2 matrix) + - x: count(*[self::m:mtr][1]/*) + - test: + if: "self::m:determinant" + then: [T: "行列式"] # phrase(the 2 by 2 'determinant') + else: + - test: + if: "@columnlines and (contains(normalize-space(@columnlines), 'solid') or contains(normalize-space(@columnlines), 'dashed'))" + then: [T: "增广矩阵"] # phrase(the 2 by 2 'augmented matrix') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + - pause: long + - x: "*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "结束" # phrase(the 'end' of matrix has been reached) + - test: + if: "self::m:determinant" + then: [T: "行列式"] # phrase(the 2 by 2 'determinant') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + +- name: default-matrix + tag: [matrix, determinant] + variables: [IsColumnSilent: "$SpeechStyle = 'ClearSpeak' and $ClearSpeak_Matrix = 'SilentColNum'"] + match: "." + replace: + - T: "" # phrase('the' 1 by 2 matrix M) + - x: "count(*)" + - T: "乘" # phrase(the 1 'by' 2 matrix) + - x: "count(*[self::m:mtr][1]/*)" + - test: + if: "self::m:determinant" + then: [T: "行列式"] # phrase(the 2 by 2 'determinant') + else: + - test: + if: "@columnlines and (contains(normalize-space(@columnlines), 'solid') or contains(normalize-space(@columnlines), 'dashed'))" + then: [T: "增广矩阵"] # phrase(the 2 by 2 'augmented matrix') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix') + - pause: long + - x: "*" + - test: + if: "$ClearSpeak_Matrix = 'EndMatrix' or $ClearSpeak_Matrix = 'EndVector'" + then: + - T: "结束" # phrase(the 'end' of matrix has been reached) + - test: + if: "self::m:determinant" + then: [T: "行列式"] # phrase(the 2 by 2 'determinant') + else: [T: "矩阵"] # phrase(the 2 by 2 'matrix's) + +- name: chemistry-msub + tag: [chemical-formula] + match: "*[1][.='msub']" + replace: + - x: "*[2]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(H 'sub' 2) + - x: "*[3]" + +- name: dimension-by + tag: mrow + match: dimension-product + replace: + - insert: + nodes: "*" + replace: [T: "乘", pause: auto] # phrase(3 'by' 5 matrix) + + + +- name: chemistry-msup + tag: [chemical-formula] + match: "count(*)=3 and *[1][.='msup']" + replace: + - x: "*[2]" + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "*[3]" + - test: + if: "following-sibling::*[1][.='+' or .='-']" # a little lazy -- assumes chemistry superscripts end with + or - + then: [pause: medium] + +- + # There currently is no way to do sub/super for n-ary number of args + # Instead, we just deal with up to two prescripts and up to four postscripts (repeating blocks of similar code [UGLY!]) + # This hopefully covers all reasonable cases... + name: chemistry-scripts + tag: [chemical-formula, chemical-nuclide] + 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))]" + match: . # should only be msubsup or mmultiscripts at this point + replace: + - test: + if: "$Prescripts" # we have at least one pre sub/super + then: + # nuclide: speak the superscript first + - test: + if: "not($Prescripts[2][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Prescripts[2]" + - pause: "short" + - test: + if: "not($Prescripts[1][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(a 'subscript' may be used to indicate an index) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(here is a 'sub' total) + - x: "$Prescripts[1]" + - pause: "short" + - test: + if: "count($Prescripts) > 2" # can this happen for chemistry??? we allow for one *extra* pre sub/super pair + then: + - test: + if: "not($Prescripts[4][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Prescripts[4]" + - pause: "short" + - test: + if: "not($Prescripts[3][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(H 'sub' 2) + - x: "$Prescripts[3]" + - pause: "short" + - x: "*[1]" # base + - test: + if: "$Postscripts" + then: + - test: + if: "not($Postscripts[1][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(phrase(H 'sub' 2) + - x: "$Postscripts[1]" + - pause: "short" + - test: + if: "not($Postscripts[2][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Postscripts[2]" + - pause: "short" + - test: + if: "count($Postscripts) > 2" + then: + - test: + if: "not($Postscripts[3][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(H 'sub' 2) + - x: "$Postscripts[3]" + - pause: "short" + - test: + if: "not($Postscripts[4][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Postscripts[4]" + - pause: "short" + - test: + if: "count($Postscripts) > 4" + then: + - test: + if: "not($Postscripts[5][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(H 'sub' 2) + - x: "$Postscripts[5]" + - pause: "short" + - test: + if: "not($Postscripts[6][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Postscripts[6]" + - pause: "short" + - test: + if: "count($Postscripts) > 6" + then: + - test: + if: "not($Postscripts[7][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "下标"] # phrase(H 'subscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "下标"] # phrase(H 'sub' 2) + - x: "$Postscripts[7]" + - pause: "short" + - test: + if: "not($Postscripts[8][self::m:none])" + then: + - test: + if: "$Verbosity='Verbose'" + then: [T: "上标"] # phrase(H 'superscript' 2) + else_test: + if: "$Verbosity='Medium'" + then: [T: "上标"] # phrase(H 'super' 2) + - x: "$Postscripts[8]" + - pause: "short" + - test: + if: "$Postscripts[last()][not(self::m:none)] and following-sibling::*[1][.='+' or .='-']" + then: [pause: medium] + +- name: chemistry + + + tag: chemical-equation + match: "." + replace: + - x: "*" + +- name: chemical-element + tag: chemical-element + match: "." + replace: + - bookmark: "@id" + - SPELL: text() + - pause: short + +- name: chemical-state + tag: chemical-state + match: "count(*)=1" + replace: + - bookmark: "*[1]/@id" + - test: + - if: ".='s'" + then: [T: "固体"] # phrase(Boron is a 'solid' in its natural state) + - else_if: ".='l'" + then: [T: "液体"] # phrase(water is a 'liquid') + - else_if: ".='g'" + then: [T: "气体"] # phrase(hydrogen is a 'gas' ) + else: [T: "水溶液"] # phrase(an 'aqueous' solution is contained in water) + - pause: short + +- name: chemical-formula-operator-bond + tag: chemical-formula-operator + match: "@data-chemical-bond" + replace: + # FIX: this might be better/more efficient if in unicode.yaml + - bookmark: "@id" + - test: + - if: ".='-' or .=':'" + then: [T: "单键"] # phrase(a 'single bond' is formed when two atoms share one pair of electrons) + - else_if: ".='=' or .='∷'" + then: [T: "双键"] # phrase(a 'double bond' may occur when two atoms share two pairs of electrons) + - else_if: ".='≡'" + then: [T: "三键"] # phrase(a 'triple bond' occurs when two atoms share three pairs of electrons) + - else_if: ".='≣'" + then: [T: "四键"] # phrase(a 'quadruple bond' occurs when two atoms share four pairs of electrons) + else: [x: "text()"] + +- name: chemical-formula-operator + tag: chemical-formula-operator + match: "." + replace: + x: "text()" + +- name: chemical-arrow-operator + tag: chemical-arrow-operator + match: "." + replace: + # FIX: this might be better/more efficient if in unicode.yaml + - bookmark: "@id" + - test: + - if: ".='→' or .='⟶'" + then_test: + if: "$Verbosity='Terse'" + then: [T: "形成"] # phrase(hydrogen and oxygen 'forms' water ) + else: [T: "反应形成"] # phrase(hydrogen and oxygen 'reacts to form' water) + - else_if: ".='⇌' or .='🣑'" # U+01F8D1 + then: [T: "可逆反应"] # phrase(a reactant 'is in equilibrium with' a product) + - else_if: ".='🣓'" # U+1F8D3 + then: [T: "平衡偏左"] # phrase(the reactant 'is in equilibrium biased to the left with' the product) + - else_if: ".='🣒'" # U+1F8D2 + then: [T: "平衡偏右"] # phrase(the reactant 'is in equilibrium biased to the right with' the product) + else: [x: "*"] + +- name: chemical-equation-operator + tag: chemical-equation-operator + match: "." + replace: + - bookmark: "@id" + - x: "text()" + +- name: none + tag: none + match: "../../*[self::m:chemical-formula or self::m:chemical-nuclide]" + replace: + - T: "" # don't say anything + +- name: ignore-intent-wrapper + tag: intent-wrapper + match: "." + replace: + - x: "*" diff --git a/Rules/Languages/zh/SharedRules/geometry.yaml b/Rules/Languages/zh/SharedRules/geometry.yaml new file mode 100644 index 000000000..082105c61 --- /dev/null +++ b/Rules/Languages/zh/SharedRules/geometry.yaml @@ -0,0 +1,76 @@ +--- + +- name: line-segment + tag: line-segment + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "线段" # phrase('the line segment from' A to B) + - x: "*[1]" + - T: "到" # phrase(the line segment from A 'to' B) + - x: "*[2]" + else: + - T: "线段" # phrase(the 'line segment' A B) + - x: "*[1]" + - x: "*[2]" + +- name: geometry-ray + tag: ray + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "射线" # phrase('the ray from' A to B) + - x: "*[1]" + - T: "到" # phrase(the ray from A 'to' B) + - x: "*[2]" + else: + - T: "射线" # phrase(the 'ray'A B) + - x: "*[1]" + - x: "*[2]" + +- name: geometry-arc + tag: arc + match: "count(*)=2" + replace: + - test: + if: "$Verbosity='Verbose'" + then: [T: ""] # phrase('the' arc A B C) + - T: "弧" # phrase(the 'arc' A B C) + - x: "*[1]" + - x: "*[2]" + +- name: measure-of-angle + tag: measure-of-angle + match: "count(*)=3" + replace: + - T: "角" + - x: "*[1]" + - x: "*[2]" + - x: "*[3]" + - T: "的度数" + + +- name: coordinate + tag: coordinate + # Intent forms coordinate($x,...) use @arg and fall through to function-intent / IntentMappings. + match: "not(*[@arg])" + replace: + - test: + if: "$Verbosity='Verbose'" + then: [OT: ""] # phrase('the' point at 1, 2) + - T: "点" # phrase(the 'point' at 1, 2) + - test: + if: "$Verbosity='Verbose'" + then: [T: ""] # Chinese does not need an equivalent of "at" here. + - pause: short + - insert: + nodes: "*" + replace: [T: "逗号", pause: auto] # phrase(f of x 'comma' y) + - pause: short + - test: + if: "($SpeechStyle='ClearSpeak' and $Verbosity='Verbose') or not(IsNode(*[last()],'leaf'))" + then: [T: "结束点"] # phrase(start point, 'end point') diff --git a/Rules/Languages/zh/SharedRules/linear-algebra.yaml b/Rules/Languages/zh/SharedRules/linear-algebra.yaml new file mode 100644 index 000000000..2d28e8ae8 --- /dev/null +++ b/Rules/Languages/zh/SharedRules/linear-algebra.yaml @@ -0,0 +1,36 @@ +--- + +- name: scalar-determinant + tag: determinant + match: "count(*)=1 and not(*[1][self::m:mtr])" + replace: + - test: + if: "$Verbosity='Verbose'" + then: + - T: "" # phrase('the' square root of 25 equals 5) + - T: "行列式" # phrase(the 'determinant' of a matrix) + - test: + if: "$Verbosity!='Terse'" + then: + - T: "" # phrase(systems 'of' linear equations) + - x: "*[1]" + - test: + if: "not(IsNode(*[1], 'simple')) and $Impairment = 'Blindness'" + then: [T: "结束行列式"] # phrase('end determinant' of a matrix) + + +- name: subscripted-norm + tag: subscripted-norm + match: "count(*)=2" + replace: + - x: "*[1]" + - T: "的" + - x: "*[2]" + - T: "范数" + +- name: norm + tag: norm + match: "count(*)=1" + replace: + - x: "*[1]" + - T: "的范数" diff --git a/Rules/Languages/zh/SimpleSpeak_Rules.yaml b/Rules/Languages/zh/SimpleSpeak_Rules.yaml new file mode 100644 index 000000000..7d09e49a9 --- /dev/null +++ b/Rules/Languages/zh/SimpleSpeak_Rules.yaml @@ -0,0 +1,358 @@ +--- +- 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:')" + # say nothing + replace: [] + +# handling of negative numbers that come from 'intent' is hard -- we do something that is close to right here +- name: intent-literal-negative-number + tag: mn + match: "starts-with(text(), '-')" + replace: + - T: "负" # A leading minus sign makes this a negative number. + - x: "translate(text(), '-_', '')" + +- name: default + tag: square-root + match: "." + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' square root of x) + - T: "根号" # phrase(the 'square root' of x) + - x: "*[1]" + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase(the square root 'of' x) + else: [pause: short] # pause: short + - test: + if: "not(IsNode(*[1], 'leaf')) and $Impairment = 'Blindness'" + then: [T: "结束根号", pause: medium] # phrase(start the square root of x 'end of root') + else: [pause: short] + +- name: default + tag: root + match: "." + replace: + - x: "*[1]" + - test: + if: "$Verbosity!='Terse'" + then: [T: "的"] # phrase(the square root 'of' x) + - test: + if: "*[2][self::m:mn and not(contains(., '.'))]" + then_test: + - if: "*[2][.='2']" + then: [T: "平方根"] # phrase(the 'square root' of x) + - else_if: "*[2][.='3']" + then: [T: "立方根"] # phrase(the 'cube root' of x) + - else: [x: "*[2]", T: "次方根"] # phrase(the square 'root' of 25) + else: + - test: + if: "*[2][self::m:mi][string-length(.)=1]" + then: + - x: "*[2]" + #- pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] + else: [x: "*[2]"] + - T: "次方根" # phrase(the square 'root' of) + - pause: short + - test: + if: "not(IsNode(*[1], 'leaf')) and $Impairment = 'Blindness'" + then: [T: "结束根号", pause: medium] + +# Fraction rules +# Mixed numbers mostly "just work" because the invisible char reads as "and" and other parts read properly on their own +- 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: "*[2]" + - T: "分之" + - x: "*[1]" # extra args specify fractional ordinal and whether it is plural + +- name: common-fraction-mixed-number + tag: fraction + match: + - "preceding-sibling::*[1][self::m:mo][.='⁤'] and" # preceding element is invisible plus + - "*[1][self::m:mn][not(contains(., '.'))] and" + - "*[2][self::m:mn][not(contains(., '.'))]" + variables: [IsPlural: "*[1]!=1"] + replace: + - x: "*[2]" + - T: "分之" + - x: "*[1]" # extra args specify fractional ordinal and whether it is plural + + +# Units (e.g., meters per second, m^2/s^2, (3m^2)/s) +- name: per-fraction + tag: fraction + match: + - "BaseNode(*[1])[contains(@data-intent-property, ':unit') or" + - " ( self::m:mrow and count(*)=3 and" # maybe a bit paranoid checking the structure... + - " *[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: "每" # phrase('5 meters 'per' second) + - x: "*[2]" + +- name: 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: fraction + 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: "*[2]" + - T: "分之" # phrase(the fraction 3 'over' 4) + - x: "*[1]" + - pause: short + +- name: default + tag: fraction + match: "." + replace: + - test: + if: "$Impairment = 'Blindness'" + then: [T: "分数"] # phrase(the 'fraction' 3 over 4) + - pause: short + - x: "*[2]" + - test: + if: "not(IsNode(*[2],'leaf'))" + then: [pause: short] + - T: "分之" # phrase(the fraction 3 'over' 4) + - test: + if: "not(IsNode(*[1],'leaf'))" + then: [pause: short] + - x: "*[1]" + - pause: short + - test: + if: "$Impairment = 'Blindness'" + then: [T: "结束分数"] # phrase(start 7 over 8 'end of fraction') + - pause: medium + +# rules for functions raised to a power +# these could have been written on 'mrow' but putting them on msup seems more specific +# to see if it is a function, we look right to see if the following sibling is apply-function +- name: inverse-function + tag: inverse-function + match: "." + replace: + - x: "*[1]" + - T: "的反函数" # phrase(the 'inverse' of f) + +- name: function-squared-or-cubed + tag: power + match: + - "*[2][self::m:mn][.='2' or .='3'] and" + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - x: "*[1]" + - bookmark: "*[2]/@id" + - test: + if: "*[2][.=2]" + then: [T: "平方"] # phrase(5 'squared' equals 25) + else: [T: "立方"] # phrase(5 'cubed' equals 125) +- name: function-power + tag: power + match: + - "following-sibling::*[1][self::m:mo][.='⁡']" #invisible function apply + replace: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # # phrase('the' fourth power of 10) + - bookmark: "*[2]/@id" + - x: "*[1]" + - T: "的" + - test: + if: "*[2][self::m:mn][not(contains(., '.'))]" + then: [x: "*[2]"] + else: [x: "*[2]"] + - T: "次方" # phrase(the fourth 'power of' 2) + - pause: short + +- name: SimpleSpeak-log-with-base + tag: mrow + match: "count(*)=3 and *[1][self::m:logarithm-with-base] and *[2][.='⁡']" + replace: + - T: "以" + - x: "*[1]/*[1]" + - T: "为底" + - pause: short + - x: "*[3]" + - T: "的对数" + +# non-function rules for power +- 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: "平方"] # phrase(5 'squared' equals 25) + else: [T: "立方"] # phrase(5 'cubed' equals 125) + +- name: simple-integer + tag: power + match: "*[2][self::m:mn][not(contains(., '.'))]" + replace: + - x: "*[1]" + - T: "的" # phrase(15 raised 'to the' second power equals 225) + - test: + if: "*[2][.>0]" + then: [x: "*[2]"] + else: [x: "*[2]"] + - T: "次方" +- 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: "的" # phrase(15 raised 'to the' second power equals 225) + - x: "*[2]" + - T: "次方" +- name: simple-var + tag: power + match: "*[2][self::m:mi][string-length(.)=1]" + replace: + - x: "*[1]" + - T: "的" # phrase(15 raised 'to the' second power equals 225) + - x: "*[2]" + #- pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] + - T: "次方" +- name: simple + tag: power + match: "IsNode(*[2], 'leaf')" + replace: + - x: "*[1]" + - T: "的" # phrase(15 raised 'to the' second power equals 225) + - x: "*[2]" + - T: "次方" +- name: nested + # it won't end in "power" if the exponent is simple enough + # FIX: not that important, but this misses the case where the nested exp is a negative integer (change test if this is fixed) + # ending nested exponents with "...power power" sounds bad + tag: power + match: + - "*[2][" + - " (self::m:power and not(IsNode(*[2], 'leaf'))) or" # non-simple nested superscript + - " self::m:mrow[*[last()][self::m:power[not(IsNode(*[2], 'leaf'))]]]" # same as above but at the end of an mrow # FIX: need to figure out linear replacement + - " ]" + replace: + - x: "*[1]" + - T: "的" # phrase(15 'raised to the' second power equals 225) + - x: "*[2]" + - T: "次方" + - pause: short + - test: + if: "$Impairment = 'Blindness'" + then: + - T: "结束指数" # phrase(start 2 raised to the exponent 4 'end of exponent') + - pause: short + else: + - pause: medium +- name: default + tag: power + match: "." + replace: + - x: "*[1]" + - T: "的" # phrase(15 'raised to the' second power equals 225) + - x: "*[2]" + - T: "次方" # phrase(15 raised to the second 'power' equals 225) + +# +# Some rules on mrows +# +- name: set + tag: set + match: "." + replace: + - test: + - if: "count(*)=0" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] # phrase('the' empty set) + - T: "空集" # phrase(when a set contains no value it is called an 'empty set' and this is valid) + - else_if: "count(*[1]/*)=3 and *[1]/*[2][self::m:mo][.=':' or .='|' or .='∣']" + then: + - T: "所有满足" # phrase(x 'such that' x is less than y) + - x: "*[1]/*[3]" + - T: "的" + - x: "*[1]/*[1]" + - T: "的集合" # phrase(the 'set of all' positive integers less than 10) + else: + - x: "*[1]" + - T: "的集合" # phrase(here is a 'set' of numbers) + +- name: times + tag: mo + match: + # say "times" when invisible times is followed by parens or a superscript that has a base with parens or "|"s + # added: say times is the superscript is not simple + # if we aren't sure if it is times or not, don't say anything + - ".='⁢' 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" # followed by parens + - " 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 " # followed by msup, etc. + - " (*[1][self::m:mrow[IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')] or " + - " self::m:matrix or self::m:determinant] or" # base has parens + - " not(IsNode(*[2], 'simple')) or " + - " (self::m:msubsup and not(IsNode(*[3], 'simple')))" + - " )" + - " ]" # other possibility is the preceding element has parens (but not the following) + - " or " + - " preceding-sibling::*[1][" + - " IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|')]" # followed by parens + - " )" + replace: + - T: "乘" # phrase(7 'times' 5 equals 35) + +- 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 " # rule out [x] standing alone + - "( 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:')])" + # missing clause: 'a positive fraction that is spoken as an ordinal + # (either by the Ordinal preference or by the default rules_' + replace: + - x: "*[2]" + +- include: "SharedRules/geometry.yaml" +- include: "SharedRules/linear-algebra.yaml" +- include: "SharedRules/calculus.yaml" +- include: "SharedRules/general.yaml" +- include: "SharedRules/default.yaml" diff --git a/Rules/Languages/zh/definitions.yaml b/Rules/Languages/zh/definitions.yaml new file mode 100644 index 000000000..abeee966f --- /dev/null +++ b/Rules/Languages/zh/definitions.yaml @@ -0,0 +1,687 @@ +--- +- include: "../../definitions.yaml" + +# IntentMappings Syntax: +# +# This section defines how each "intent" is spoken for various grammatical forms (fixities such as "prefix", "infix", "postfix", and "function"), +# along with supporting different verbosity levels. +# The syntax is a little bit complicated, but allows a declarative syntax so you don't have to write as many rules. +# +# For each intent, you specify spoken forms for one or more fixities using the syntax below. +# +# - Each mapping has the form: +# "": "= || = || ...", +# or just +# "": "", +# where is used for all forms if fixity and '=' are omitted. +# +# - If an intent has multiple fixities, separate them with "||". Spaces are allowed around delimiters for readability. +# - If only one ":" is present in **, it provides the 'terse' form, and the following string is used for both 'medium' and 'verbose'. +# - If more than one ":" is present, the string is split into 'terse', 'medium', and 'verbose' forms, in that order (each separated by ":"). +# - If no ":" is present, the same spoken form is used for all styles. +# +# - Use semicolons ";" to divide opening, main, and closing bracketing words within a speech string, where such words make sense. +# - If an intent has multiple fixities but a definition does not specify one, the first fixity listed is chosen by default. +# +# Functions and argument glue: +# - The "|" character delimits options describing how to glue arguments together when speaking function arguments. +# - Arity templates (multiple "|" options, or one option with ','-separated glue words): glue-word count +# implies arity; the final argument is preceded by "of". Example: "sum | over | from,to" +# - 1 arg: "sum of x" +# - 2 args: "sum over i of x" +# - 3 args: "sum from i to n of x" +# If no option matches the argument count, separators fall back to "comma". +# - Binary separator (exactly one "|" option, no commas in it): after "of", that word joins the two args. +# Example: "quotient": "function=integer part | divided by" → "integer part of x divided by y" +# If there are not exactly two args, separators fall back to "comma" (no real n-ary use of these words). +# +# BNF-like description: +# +# IntentMappingsEntry ::= ":" +# +# ::= ( "||" )* +# | +# +# ::= "=" +# +# ::= "prefix" | "infix" | "postfix" | "function" | "nofix" | "silent" +# +# ::= +# | "|" ( "|" )* +# +# ::= text +# # May use ":" to separate terse:medium:verbose forms. +# # May use ";" for bracketing (opening; main; closing). +# +# ::= text +# # One option, no commas: binary separator after "of" (e.g. "divided by"). +# # One option with "," or several "|" options: arity templates +# # (glue-word count = arg_count - 1; last arg preceded by "of"). +# # Unmatched arity (or binary glue with ≠2 args): fall back to "comma". +# +# ::= (applies to all fixities) +# +# Example: +# "absolute-value": "function= ; absolute value: the absolute value: the absolute value; end absolute value" +# "sum": "function=sum | over | from,to" +# "quotient": "function=integer part | divided by" +# +- IntentMappings: { + "binomial": "infix=二项式系数; 取; 结束二项式系数", + + ### Functions and Inverses + # Interval intents (open-/closed-interval, etc.) are handled by rules due to special cases involving ∞. + "inverse": "function=逆 || postfix=的逆", + + "domain": "function= ; 定义域", + "codomain": "function= ; 陪域", + + "image": "function=像", + #"fraction":"function=fraction; over; end fraction", # NOTE: Fails + "mixed-fraction": "infix=又", # NOTE: in website says function, but follow infix speech pattern. + # Binary quotient/remainder intents use dedicated rules in SharedRules/general.yaml + # so that the result noun can follow both operands in natural Chinese word order. + "quotient": "function=商", + "evaluated-at": "infix=取值于", + "remainder": "function=余数", + + "max": "function=最大值", + "min": "function=最小值", + + "power": "infix=的指数为", + "root": "function=根", + + "greatest-common-divisor": "function=最大公约数", + "least-common-multiple": "function=最小公倍数", # In webpage typo "lest common" + + "absolute-value": "function= ; 绝对值: 绝对值: 绝对值; 结束绝对值", + "complex-conjugate": "function=共轭复数", + "complex-arg": "function=辐角", + "real-part": "function=实部", + "imaginary-part": "function=虚部", + + "polar-coordinate": "function=极坐标", + "spherical-coordinate": "function=球坐标", + "cartesian-coordinate": "function=直角坐标", + "coordinate": "function=坐标", + "floor": "function=向下取整", + "ceiling": "function=向上取整", + "round": "function=四舍五入值", + "fractional-part": "function=小数部分", + + + ### Calculus + # "definite-integral":"function=integral over || function=integral from; to", # Property ??? + # "derivative":"function=the derivative; with respect to", # Property ??? + # "partial-derivative": "prefix=偏导数", ## Note, included with infix, but separately under calculus tab has ??? for functionality + + "limit": "prefix=极限,当", + "tends-to": "infix=趋于", + "tends-to-from-above": "infix=从右侧趋于", + "tends-to-from-below": "infix=从左侧趋于", + + + ### Sets + "set": "function= ; 集合: 集合", + "set-difference": "function=差集 | 与 || infix=减去", + "complement": "function=补集", + "empty-set": "nofix=空集", + "cardinality": "function=基数", # NOTE: does not have a defined speech template in website + "list": "function=列表", + "tuple": "function= ; 元组: 元组", + + + ### Sequence and Series + "sum": "function=求和 | 遍历 | 从,到", + "product": "function=乘积 | 遍历 | 从,到", + + + ### Elementary classical functions + "sine": "function=正弦", + "cosine": "function=余弦", + "tangent": "function=正切", + "secant": "function=正割", + "cosecant": "function=余割", + "cotangent": "function=余切", + + "arcsine": "function=反正弦", + "arccosine": "function=反余弦", + "arctangent": "function=反正切", + "arcsecant": "function=反正割", + "arccosecant": "function=反余割", + "arccotangent": "function=反余切", + + "hyperbolic-sine": "function=双曲正弦", + "hyperbolic-cosine": "function=双曲余弦", + "hyperbolic-tangent": "function=双曲正切", + "hyperbolic-secant": "function=双曲正割", + "hyperbolic-cosecant": "function=双曲余割", + "hyperbolic-cotangent": "function=双曲余切", + + "arc-hyperbolic-sine": "function=反双曲正弦", + "arc-hyperbolic-cosine": "function=反双曲余弦", + "arc-hyperbolic-tangent": "function=反双曲正切", + "arc-hyperbolic-secant": "function=反双曲正割", + "arc-hyperbolic-cosecant": "function=反双曲余割", + "arc-hyperbolic-cotangent": "function=反双曲余切", + + "exponential": "function=指数函数", + "natural-logarithm": "function=自然对数", + "logarithm": "function=对数", ##Check arity 2 + + + ### Statistics and Probability + "mean": "function=平均值", + "standard-deviation": "function=标准差", + "variance": "function=方差", + "median": "function=中位数", + "mode": "function=众数", + + "conditional-probability": "function=条件概率 | 条件为", + + + ### Linear Algebra + "vector": "function= ; 向量 || prefix=向量", # prefix not tested, also prefix not on webpage + "matrix": "function=矩阵", # NOTE: Failing test, recheck + "determinant": "function=行列式", + "adjugate": "function=伴随矩阵", + "magnitude": "function=模", + "norm": "function=; 范数: 范数: 范数; 结束范数", + "span": "function=张成空间", + + "unit-vector": "prefix=单位向量", + + "identity-matrix": "nofix=单位矩阵", # NOTE: no function specified + "transpose": "postfix=转置 || function=转置", + "dimensional-product": "infix=乘", # INFIX + + + ### Constants and Sets + "set-of-integers": "nofix=整数集: 全体整数的集合", + "set-of-reals": "nofix=实数集: 全体实数的集合", + "set-of-rationals": "nofix=有理数集: 全体有理数的集合", + "set-of-natural-numbers": "nofix=自然数集: 全体自然数的集合", + "set-of-complex-numbers": "nofix=复数集: 全体复数的集合", + "set-of-primes": "nofix=素数集: 全体素数的集合", + + + ### Geometry + "line-segment": "prefix=线段", + "directed-line-segment": "prefix=有向线段", + "line": "prefix=直线", + "ray": "prefix=射线", + "arc": "prefix=弧", + + "length": "function=长度", + "area": "function=面积", + + "point": "prefix=点", ## NOTE: Has ??? for property in site. Should it be prefix? Or something else. + + ### Separators + "time-separator": "infix=", + + ### General Concepts + "fenced-group": "function=括号组", # appears both under general concepts and grouping + # NOTE: in site mentions "the pair x and y", due to being defined as function, needs the "of" keyword + "ordered-pair": "function=有序对 | 和", # Needs to be tested + # Simple last arg: "x sub k". Complex: "… end sub" (terse/medium) / "end subscript" (verbose) via infix-intent. + "indexed-by": "infix= ; 下标; 结束下标: 结束下标: 结束下标", + + "highlight": "postfix=已突出显示", + "least-common-denominator": "function=最小公分母", + "rate": "infix=每", + "translation": "function=平移", + "constraint": "infix= ; 条件为; ", + + "binomial-coefficient": "infix=取", + "pochhammer": "function=升阶乘", + "permutation-cycle": "function=轮换", + "embellished-name": "infix=带修饰", + + ### Grouping + "annotation": "infix= ; 即; ", # NOTE: Follows the same order as indexed-by, in site listed as function. Should it be infix? + "braced-group": "function=分组; 结束分组", # NOTE: not tested, site is missing "of" keyword function introduces + #"repeating-decimal":"function=repeating decimal", # NOTE: BREAKS TEST. Check again. Site is missing "of" keyword function introduces. + + + ### Other + ## Default fixity function + "curl": "function=旋度", + "divergence": "function=散度", + "gradient": "function=梯度", + "laplacian": "function=拉普拉斯算子", + + ## Default fixity prefix + "angle": "prefix=角", + "angle-measure": "prefix=角的度数", + "change": "prefix=变化量", + "for-all": "prefix=任意", + "measured-angle": "prefix=角的度数", + "not": "prefix=非", + "number-of": "prefix=数量", + "partial-derivative": "prefix=偏导数", + "right-angle": "prefix=直角", + "square-root-of": "prefix=平方根", + "there-does-not-exist": "prefix=不存在", + "there-exists": "prefix=存在", + + + ## Default fixity infix + "and": "infix=且", + "applied-to": "infix=作用于", + "approximately": "infix=约等于", + "congruent": "infix=全等于", + "cartesian-product": "infix=笛卡尔积", + "composed-with": "infix=复合", + "cross-product": "infix=叉乘: 向量积: 向量积", + "defined-as": "infix=定义为", + "divided-by": "infix=除以", + "divides": "infix=整除", + "does-not-belong-to": "infix=不属于", + "does-not-divide": "infix=不能整除", + "dot-product": "infix=点乘", + "downwards-diagonal-ellipsis": "infix=右下省略号", + "direct-product": "infix=直积", + "element-of": "infix=属于", + "ellipsis": "infix=省略号", + "equals": "infix=等于", + "equivalent-to": "infix=等价于", + "evaluates-to": "infix=结果为", + "given": "infix=已知", + "greater-than": "infix=大于", + "greater-than-or-equal-to": "infix=大于等于", + "identically-equals": "infix=恒等于", + "if-and-only-if": "infix=当且仅当", + "implies": "infix=推出", + "inner-product": "infix=内积", + "intersection": "infix=交", + "less-than": "infix=小于", + "less-than-or-equal-to": "infix=小于等于", + "list-separator": "infix=逗号", + "maps-to": "infix=映射到", + "member-of": "infix=属于", + "minus-or-plus": "infix=负或正", + "not-subset": "infix=不真包含于", + "not-superset": "infix=不真包含", + "not-equal-to": "infix=不等于", + "not-member-of": "infix=不属于", + "not-parallel-to": "infix=不平行于", + "obtained-from": "infix=由此得到", + "or": "infix=或", + "outer-product": "infix=外积", + "parallel-to": "infix=平行于", + "perpendicular": "infix=垂直于", + "plus": "infix=加 || prefix=正", # Prefix not tested + "minus": "infix=减 || prefix=负", # Prefix not tested + "plus-or-minus": "infix=正负", + "precedes": "infix=先于", + "proportional": "infix=正比于", + "range-separator": "infix=到", + "ratio": "infix=比", + "similar": "infix=相似于", + "subset": "infix=真包含于", + "subset-or-equal": "infix=包含于", + "succeeds": "infix=后于", + "such-that": "infix=满足", + "superset": "infix=真包含", + "superset-or-equal": "infix=包含", + "tilde": "infix=波浪号", + "times": "infix=乘", + "union": "infix=并", + "upwards-diagonal-ellipsis": "infix=右上省略号", + "vertical-ellipsis": "infix=竖省略号", + "xor": "infix=异或", + + ## Default fixity postfix + "factorial": "postfix=阶乘", + "percent": "postfix=百分号", + + ## Default fixity nofix + "diameter": "nofix=d: 直径", + "distance": "nofix=d; D: 距离", + "probability": "nofix=P: 概率 || function=概率", + "radius": "nofix=r: 半径", + "volume": "nofix=V: 体积 || function=体积", + "exponential-e": "nofix=e", + "imaginary-i": "nofix=i", + "differential-d": "nofix=d", + "golden-ratio": "nofix=黄金分割比", + + + ## Other : Not tested, don't appear in https://w3c.github.io/mathml-docs/intent-core-concepts/ + "modified-variable": "silent= ", + "say-super": "infix=上标", # used with 'mo' for superscripts (e.g, "<") + "skip-super": "silent=", # used with 'mo' for superscripts (e.g, "*") + # "large-op": "infix=over || other=from,to", + "lim-sup": "prefix=上极限,当", + "lim-inf": "prefix=下极限,当", + "logarithm-with-base": "prefix=对数,底数为", + # "pochhammer": "function=升阶乘", # arguments are in reverse order, so can't work here + "trace": "function= ; 迹: 迹: 迹; 结束迹", + "dimension": "function=; 维数: 维数: 维数; 结束维数", + "homomorphism": "function= ; 同态: 同态: 同态; 结束同态", + "kernel": "function= ; 核: 核: 核; 结束核", + + "chemistry-concentration": "function= ; 浓度: 浓度: 浓度; 结束浓度", + } + + # Names of functions that in terse mode don't say "of" (or it's equivalent in other languages) +- TerseFunctionNames: { + "divergence", "del:gradient", "curl" + } + +- NavigationParts: { + # These are the parts of a formula that can be navigated to + "large-op": "主体; 下限; 上限", + "mfrac": "分子; 分母", + "fraction": "分子; 分母", + "msqrt": "根号内", + "square-root": "根号内", + "mroot": "根号内; 根指数", + "root": "根号内; 根指数", + "msub": "底数; 下标", + "sub": "底数; 下标", + "logarithm-with-base": "底数", + "indexed-by": "底数; 下标", + "msup": "底数; 上标", + "say-super": "底数; 上标", + "skip-super": "底数; 上标", + "power": "底数; 指数", + "msubsup": "底数; 下标; 上标", + "munder": "主体; 下限", + "mover": "主体; 上限", + "munderover": "主体; 下限; 上限", + + # words for moving into and out of one of the parts (e.g., "move right 'out of' numerator, 'in' denominator") + # it's a hack to put them here, but at least they are grouped with the other navigation parts + "in": "进入", + "out": "离开", + } + +- KnownWords: { + # MathCAT will put together some runs of three or more mi's (a common mistake), but skips those in alphabetical order. + # This is a list of exceptions so that they do get put together. Don't list words with repeated letters. + + # I asked bard and chatgpt for formula words that are alphabetical, and they failed. + # Here are the words I managed to find, but it almost certainly not complete. Change the list for other languages. + "Abel", "aery", "ails", "airy", "amps", "ceil", "cents", "chop", "flux", "flow", "knot", "most" + } + +- SIPrefixes: { + "Q": "昆", "R": "容", "Y": "尧", "Z": "泽", "E": "艾", "P": "拍", "T": "太", "G": "吉", "M": "兆", "k": "千", "h": "百", "da": "十", + "d": "分", "c": "厘", "m": "毫", "µ": "微", "n": "纳", "p": "皮", "f": "飞", "a": "阿", "z": "仄", "y": "幺", "r": "柔", "q": "亏" + } + +# this is a list of all units that accept SIPrefixes +# from www.bipm.org/documents/20126/41483022/SI-Brochure-9-EN.pdf +# Prefixes may be used with any of the 29 SI units with special names +# The SI prefixes can be used with several of accepted units, but not, for example, with the non-SI units of time. +- SIUnits: { + # base units + "A": "安培", + "cd": "坎德拉", + "K": "开尔文", "K": "开尔文", # U+212A + "g": "克", + "m": "米", # British spelling works for US also + "mol": "摩尔", + "s": "秒", "sec": "秒", # "sec" not actually legal + + # derived units + "Bq": "贝可勒尔", + "C": "库仑", + "°C": "摄氏度", "℃": "摄氏度", # should only take negative powers + "F": "法拉", + "Gy": "戈瑞", + "H": "亨利", + "Hz": "赫兹", + "J": "焦耳", + "kat": "开特", + "lm": "流明", + "lx": "勒克斯", + "N": "牛顿", + "Ω": "欧姆", "Ω": "欧姆", # Greek Cap letter, U+2126 OHM SIGN + "Pa": "帕斯卡", + "S": "西门子", + "Sv": "希沃特", + "T": "特斯拉", + "V": "伏特", + "W": "瓦特", + "Wb": "韦伯", + + # accepted (plus a few variants) that take SI prefixes + "l": "升", "L": "升", "ℓ": "升", # Should only take negative powers; British spelling works for US also + "t": "吨", # should only take positive powers + "Da": "道尔顿", + "Np": "奈培", # should only take negative powers + "u": "统一原子质量单位", # 'u' is correct: https://en.wikipedia.org/wiki/Dalton_(unit) + "eV": "电子伏特", + "rad": "弧度", # should only take negative powers + "sr": "球面度", # should only take negative powers + + # others that take a prefix + "a": "年", # should only take positive powers + "as": "角秒", # see en.wikipedia.org/wiki/Minute_and_second_of_arc + + # technically wrong, but used in practice with SI Units + "b": "比特", # should only take positive powers + "B": "字节", # should only take positive powers + "Bd": "波特", # should only take positive powers + } + + +- UnitsWithoutPrefixes: { + # time + "″": "秒", "\"": "秒", + "′": "分", "'": "分","min": "分", + "h": "小时", "hr": "小时", "Hr": "小时", + "d": "天", "dy": "天", + "w": "周", "wk": "周", + "y": "年", "yr": "年", + + # angles + "°": "度", "deg": "度", # should only take negative powers + "arcmin": "角分", + "amin": "角分", + "am": "角分", + "MOA": "角分", + "arcsec": "角秒", + "asec": "角秒", + + # distance + "au": "天文单位", "AU": "天文单位", + "ltyr": "光年", "ly": "光年", + "pc": "秒差距", + "Å": "埃", "Å": "埃", # U+00C5 and U+212B + "fm": "费米", + + # others + "ha": "公顷", + # "B": "bel", # "B" more commonly means bytes + "dB": "分贝", # already logarithmic, so not used with SI prefixes + + "amu": "原子质量单位", + "atm": "标准大气压", + "bar": "巴", + "cal": "卡路里", + "Ci": "居里", + "grad": "百分度", + "M": "摩尔浓度", + "R": "伦琴", + "rpm": "转每分", + "℧": "姆欧", + "dyn": "达因", + "erg": "尔格", + + # powers of 2 used with bits and bytes + "Kib": "基比比特", "Mib": "梅比比特", "Gib": "吉比比特", "Tib": "太比比特", "Pib": "拍比比特", "Eib": "艾比比特", "Zib": "泽比比特", "Yib": "尧比比特", + "KiB": "基比字节", "MiB": "梅比字节", "GiB": "吉比字节", "TiB": "太比字节", "PiB": "拍比字节", "EiB": "艾比字节", "ZiB": "泽比字节", "YiB": "尧比字节", + } + + # this will only be used if the language is English, so it can be empty for other countries +- EnglishUnits: { + # length + "in": "英寸", + "ft": "英尺", + "mi": "英里", + "rd": "杆", + "li": "链节", + "ch": "链", + + # area + "sq in": "平方英寸", "sq. in": "平方英寸", "sq. in.": "平方英寸", + "sq ft": "平方英尺", "sq. ft": "平方英尺", "sq. ft.": "平方英尺", + "sq yd": "平方码", "sq. yd": "平方码", "sq. yd.": "平方码", + "sq mi": "平方英里", "sq. mi": "平方英里", "sq. mi.": "平方英里", + "ac": "英亩", + "FBM": "板英尺", + + # volume + "cu in": "立方英寸", "cu. in": "立方英寸", "cu. in.": "立方英寸", + "cu ft": "立方英尺", "cu. ft": "立方英尺", "cu. ft.": "立方英尺", + "cu yd": "立方码", "cu. yd": "立方码", "cu. yd.": "立方码", + "bbl": "桶", "BBL": "桶", + "pk": "配克", + "bu": "蒲式耳", + "tsp": "茶匙", + "tbl": "汤匙", + + # liquid + "fl dr": "液量打兰", + "fl oz": "液量盎司", + "gi": "及耳", + "cp": "杯", "cup": "杯", + "pt": "品脱", + "qt": "夸脱", + "gal": "加仑", + + # weight + "gr": "格令", + "dr": "打兰", + "oz": "盎司", "℥": "盎司", + "lb": "磅", + "cwt": "英担", + "dwt": "本尼威特", + "oz t": "金衡盎司", + "lb t": "金衡磅", + + # energy + "hp": "马力", + "BTU": "英热单位", + "°F": "华氏度", "℉": "华氏度", + + # other + "mph": "英里每小时", + "mpg": "英里每加仑", + } + +- CurrencySymbols: { + "$": "美元", "¢": "美分", "€": "欧元", "£": "英镑", "₡": "科朗", "₤": "里拉", "₨": "卢比", + "₩": "韩元", "₪": "新谢克尔", "₱": "比索", "₹": "卢比", "₺": "里拉", "₿": "比特币", + # could add more currencies... + } + +- PluralForms: { + # FIX: this needs to be flushed out + "inch": "英寸", "square inch": "平方英寸", "cubic inch": "立方英寸", + "foot": "英尺", "square foot": "平方英尺", "cubic foot": "立方英尺", + "board foot": "板英尺", + "degree celsius": "摄氏度", + "degree fahrenheit": "华氏度", + "henry": "亨利", + "hertz": "赫兹", + "lux": "勒克斯", + "siemens": "西门子", + "revolution per minute": "转每分", + "mile per hour": "英里每小时", + "mile per gallon": "英里每加仑", + "colón": "科朗", "lira": "里拉", "won": "韩元", "bitcoin": "比特币" + } + +# Lines starting with "#" are a comment +# Each definition in this file is of the form +# - name: { "...", "..." "..." } +# For numbers, +# - name: [] "...", "..." "..." ] + + +# ---------------- Cardinal and Ordinal Numbers -------------------------- +# The following definitions are used to convert numbers to words +# The are mainly used for ordinals, of which there are two cases: +# 1. Regular ordinals: first, second, third, ... +# 2. Ordinals used in the denominator of fractions (e.g, one half, one third) +# When used in the denominator of fractions, a plural version might be +# used (e.g., two halves, two thirds) +# Although a lot of languages are regular after a few entries, for generality, +# the following lists should be filled out even though they are the same +# or easily derived from others in many languages (e.g, an 's' is added for plurals). +# The larger ordinal numbers (e.g, millionth) is used when there are only +# '0's after that decimal place (e.g., 23000000).:w + +# All definitions start 0, 10, 100, etc. + +# The definitions for the "ones" should extend until a regular pattern begins +# The minimum length is 10. + +# For English, a regular pattern starts at twenty +- NumbersOnes: [ + "零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十", "十一", "十二", "十三", "十四", "十五", "十六", "十七", "十八", "十九" + ] + +- NumbersOrdinalOnes: [ + "第零", "第一", "第二", "第三", "第四", "第五", "第六", "第七", "第八", "第九", "第十", "第十一", "第十二", "第十三", "第十四", "第十五", "第十六", "第十七", "第十八", "第十九" + ] + +- NumbersOrdinalPluralOnes: [ + "零分", "一分", "二分", "三分", "四分", "五分", "六分", "七分", "八分", "九分", "十分", "十一分", "十二分", "十三分", "十四分", "十五分", "十六分", "十七分", "十八分", "十九分" + ] + + # stop when regularity begins +- NumbersOrdinalFractionalOnes: [ + "零", "一", "二分之一" + ] + + # stop when regularity begins +- NumbersOrdinalFractionalPluralOnes: [ + "零", "一", "二分之一" + ] + + + # What to use for multiples of 10 +- NumbersTens: [ + "", "十", "二十", "三十", "四十", "五十", "六十", "七十", "八十", "九十" + ] + +- NumbersOrdinalTens: [ + "", "第十", "第二十", "第三十", "第四十", "第五十", "第六十", "第七十", "第八十", "第九十" + ] + +- NumbersOrdinalPluralTens: [ + "", "十分", "二十分", "三十分", "四十分", "五十分", "六十分", "七十分", "八十分", "九十分" + ] + + +- NumbersHundreds: [ + "", "一百", "二百", "三百", "四百", "五百", "六百", "七百", "八百", "九百" + ] + +- NumbersOrdinalHundreds: [ + "", "百分", "二百分", "三百分", "四百分", "五百分", "六百分", "七百分", "八百分", "九百分" + ] + +- NumbersOrdinalPluralHundreds: [ + "", "百分", "二百分", "三百分", "四百分", "五百分", "六百分", "七百分", "八百分", "九百分" + ] + + + # At this point, hopefully the language is regular. If not, code needs to be written +- NumbersLarge: [ + "", "千", "百万", "十亿", "兆", "千兆", "百京", "十垓", "秭", "千秭", "百穰" + ] + +- NumbersOrdinalLarge: [ + "", "千分", "百万分", "十亿分", "兆分", "千兆分", "百京分", "十垓分", "秭分", "千秭分", "百穰分" + ] + +- NumbersOrdinalPluralLarge: [ + "", "千分", "百万分", "十亿分", "兆分", "千兆分", "百京分", "十垓分", "秭分", "千秭分", "百穰分" + ] diff --git a/Rules/Languages/zh/navigate.yaml b/Rules/Languages/zh/navigate.yaml new file mode 100644 index 000000000..6cd7695a0 --- /dev/null +++ b/Rules/Languages/zh/navigate.yaml @@ -0,0 +1,1776 @@ +--- +# Documentation: +# +# The general form for many rules is: +# 1. Say the command if this is first rule to fire (MatchCounter) and depending upon "NavVerbosity"s value +# This will increment MatchCounter so that the command won't be spoken again +# 2. Say info about moving into/out of 2D structures +# 3. Set some variables and possibly recurse. +# If stopping, "NavNode" should be set. +# +# The meaning of NavVerbosity: +# * Verbose -- always echo the command and end points (e.g., "can't move right") +# * Medium -- only echo the command for obscure commands like the placemarker commands; also say end points +# * Terse -- no echo of commands or end points +# +# For the second item, a common set of rules is used. These rules require the variable "Move2D" +# to be set along with "Child2D", where "Move2D" is either 'in' or 'out'. +# +# In addition, the navigation rules make use of two functions: +# +# int DistanceFromLeaf(node, leftSide, treat2DElementsAsTokens) +# Returns the distance (number of children) until a leaf is reached by traversing the leftmost/rightmost child +# If 'treat2DElementsAsTokens' is true, then 2D notations such as fractions are treated like leaves +# A leaf has distance == 0 +# +# EdgeNode(node, "left"/"right", stopNodeName) +# Returns the stopNode if at left/right edge of named ancestor node. 'stopNodeName' can also be "2D" +# If the stopNode isn't found, the original node is returned +# Note: if stopNodeName=="math", then punctuation is taken into account since it isn't really part of the math +# +# A few other variables are of importance to Navigation +# NavMode -- Enhanced, Simple, Character +# ReadZoomLevel -- -1 for Enhanced, otherwise the distance from leaf the rules should maintain +# PlaceMarkerIndex + +# Note: the rules for saying a command and announcing what is said when moving in/out of a 2d exprs are hacks +# They depend upon special variables "SayCommand" and "Move2D" being set and if they are, the rules are activated. +# If/when functions can be defined in a rules file, it is likely these would be much better done via those functions +# as they would likely be much more efficient and also cleaner. + +# Rules for announcing the command +- name: say-command + tag: "!*" + match: "$SayCommand != ''" # value should be '', 'true', or 'false' + variables: [Prefix: "''"] + replace: + - test: + - if: "$MatchCounter = 0 and $SayCommand = 'true'" + then_test: + - if: "self::m:math and starts-with($NavCommand, 'ZoomOut')" + then: [T: "已返回最外层", pause: "medium"] + - else_if: "IsNode(., 'leaf') and starts-with($NavCommand, 'ZoomIn')" + then: + - test: + - if: "string-length(.) = 1" + then: [T: "已进入最内层"] # phrase('zoomed in all of the way') + - else_if: "$NavNodeOffset = 0" + then: [T: "已进入第一个字符"] # phrase('zoomed in to first character') + else: [T: "已进入当前字符"] # phrase('zoomed in to character') + - pause: "medium" + else: + - test: + # CommandOffset is 1 + length of the English NavCommand stem (Zoom/Move/Read/Describe), + # not the spoken Prefix length — so translations can use a different Prefix word. + - if: "starts-with($NavCommand, 'Zoom')" + then: [set_variables: [Prefix: "''", CommandOffset: "5"]] # Chinese uses complete phrases for level changes. + - else_if: "starts-with($NavCommand, 'Move')" + then: [set_variables: [Prefix: "'移到'", CommandOffset: "5"]] # phrase('move' to next entry in table) + - else_if: "starts-with($NavCommand, 'Read')" + then: [set_variables: [Prefix: "'朗读'", CommandOffset: "5"]] # phrase('read' to next entry in table) + - else_if: "starts-with($NavCommand, 'Describe')" + then: [set_variables: [Prefix: "'概述'", CommandOffset: "9"]] # phrase('describe' to next entry in table) + - test: + if: "$Prefix != '' or starts-with($NavCommand, 'Zoom')" + then: + - x: "$Prefix" + - test: + - if: "substring($NavCommand, $CommandOffset) = 'In'" + then: [T: "进入下一层"] # phrase(zoom 'in' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'InAll'" + # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom" + then: [T: "进入最内层"] # phrase(zoom 'out all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Out'" + then: [T: "返回上一层"] # phrase(zoom 'out' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'OutAll'" + # HACK: '\uF8FE' is used internally for the concatenation char by 'ct' -- this gets "ed" concatenated to "zoom" + then: [T: "返回最外层"] # phrase(zoom 'out all of the way' to see more details) + - else_if: "substring($NavCommand, $CommandOffset) = 'Next'" + then: [T: "后一项"] # phrase(move to the 'right') + - else_if: "substring($NavCommand, $CommandOffset) = 'Previous'" + then: [T: "前一项"] # phrase(move to the 'left') + - else_if: "substring($NavCommand, $CommandOffset) = 'Current'" + then: [T: "当前项"] # phrase(who is the 'current' president) + - else_if: "substring($NavCommand, $CommandOffset) = 'LineStart'" + then: [T: "本行开头"] # phrase(move 'to start of line') + - else_if: "substring($NavCommand, $CommandOffset) = 'LineEnd'" + then: [T: "本行结尾"] # phrase(move 'to end of line') + - pause: "medium" + - set_variables: [MatchCounter: "$MatchCounter + 1"] + +- name: into-or-out-of-silent + tag: "*" + # saying "out of row n" is not very useful, so skip it + match: "$Move2D != '' and (not(@data-from-mathml) or @data-from-mathml = name(.)) and + (name(.)='mrow' or name(.) = 'mtr' or name(.) = 'mlabeledtr' or @data-from-mathml = 'mtable')" + replace: [] + +# Prefix/silent intents with no NavigationParts entry (e.g. unary minus, x-hat) +# should not announce "in"/"out of". +- name: into-or-out-of-prefix-or-silent-without-parts + tag: "*" + match: "$Move2D != '' and + (contains(@data-intent-property, ':prefix:') or contains(@data-intent-property, ':silent:')) and + GetNavigationPartName(name(.), count($Child2D/preceding-sibling::*)) = ''" + replace: [] + +- name: into-or-out-of-mtr + tag: [mtr, mlabeledtr] + match: "$Move2D = 'in'" + replace: + - T: "列" + - x: "count($Child2D/preceding-sibling::*)+1" + - pause: "medium" + +- name: into-or-out-of-mmultiscripts + tag: "*" + match: "$Move2D != '' and (@data-from-mathml='mmultiscripts' or self::m:mmultiscripts)" + replace: + - test: + if: "name($Child2D)!='none'" + then: + - with: + variables: + - NumPrecedingSiblings: "count($Child2D/preceding-sibling::*)" + replace: + - x: "IfThenElse($Move2D='in', '进入', IfThenElse($Move2D='out of', '离开', IfThenElse($Move2D='start of', '起始位置在', '末端位置在')))" + - test: + - if: "$NumPrecedingSiblings=0" + then: [T: "底数"] # phrase(the 'base' of the power) + - else_if: "$Child2D/preceding-sibling::*[self::m:mprescripts]" # are we before mprescripts and hence are postscripts + then: + - test: # in postscripts -- base shifts by one + if: "$NumPrecedingSiblings mod 2 = 0" + then: [T: "前置下标"] # phrase(x with 'subscript' 2) + else: [T: "前置上标"] # phrase(x with 'superscript' 2) + else: + - test: + if: "$NumPrecedingSiblings mod 2 = 0" + then: [T: "上标"] # phrase(x with 'pre-superscript' 2) + else: [T: "下标"] # phrase(x with 'pre-subscript' 2) + - pause: "medium" + +# Rules for speaking what happens when moving into or out of a notation +- name: into-or-out-of-default + tag: "*" + # saying "out of row n" is not very useful, so skip it + # match: "$Move2D != '' and @data-from-mathml and @data-from-mathml != name(.) and count(*)>1 and @data-from-mathml != 'mtable'" + match: "$Move2D != '' and not(self::m:math or @data-from-mathml = 'mtable' or @data-from-mathml = 'mtd') " + replace: + - with: + variables: + - PartNumber: "count($Child2D/preceding-sibling::*)" + - PartName: "GetNavigationPartName(name(.), $PartNumber)" + replace: + - x: "IfThenElse($Move2D='in', '进入', IfThenElse($Move2D='out of', '离开', IfThenElse($Move2D='start of', '起始位置在', '末端位置在')))" + - test: + - if: "$PartName != ''" + then: [x: "$PartName"] + - else_if: "count(*) = 1" + then_test: + if: "$NavVerbosity = 'Verbose'" + then: # IntentMappings via SpeakIntentName; falls back to spaced intent name + - test: + - if: "contains(@data-intent-property, ':prefix:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'prefix')"] + - else_if: "contains(@data-intent-property, ':postfix:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'postfix')"] + - else_if: "contains(@data-intent-property, ':infix:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'infix')"] + - else_if: "contains(@data-intent-property, ':function:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'function')"] + - else_if: "contains(@data-intent-property, ':nofix:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'nofix')"] + - else_if: "contains(@data-intent-property, ':silent:')" + then: [x: "SpeakIntentName(name(.), $Verbosity, 'silent')"] + - else: [x: "SpeakIntentName(name(.), $Verbosity, 'other')"] + else: + - T: "第" + - x: "count($Child2D/preceding-sibling::*) + 1" + - T: "部分" # phrase(the 'part' of the expression) + - pause: "medium" + +- name: default-move + # nothing to do (not 2D) -- need to catch $Move2D though so rules based on NavCommand don't trigger + tag: "*" + match: "$Move2D != ''" + replace: [] + +# ********* Go back to last position *************** +# This is first since start/end position shouldn't matter +- name: move-last-location + + + tag: "*" + match: "$NavCommand = 'MoveLastLocation'" + replace: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - test: + - if: "$PreviousNavCommand = 'ZoomIn'" + then: [T: "撤销进入下一层"] # phrase('undo zoom in') + - else_if: "$PreviousNavCommand = 'ZoomOut'" + then: [T: "撤销返回上一层"] # phrase('undo zoom out') + - else_if: "$PreviousNavCommand = 'ZoomInAll'" + then: [T: "撤销进入最内层"] # phrase('undo zooming in all of the way') + - else_if: "$PreviousNavCommand = 'ZoomOutAll'" + then: [T: "撤销返回最外层"] # phrase('undo zooming out all of the way') + - else_if: "$PreviousNavCommand = 'MovePrevious' or $PreviousNavCommand = 'MovePreviousZoom'" + then: [T: "撤销移到前一项"] # phrase('undo move left') + - else_if: "$PreviousNavCommand = 'MoveNext' or $PreviousNavCommand = 'MoveNextZoom'" + then: [T: "撤销移到后一项"] # phrase('undo move right') + - else_if: "$PreviousNavCommand = 'None'" + then: [T: "没有可撤销的导航操作"] # phrase('no previous command') + - pause: "medium" + - set_variables: [NavNode: "@id"] + +# many times, for typographic reasons, people include punctuation at the end of a math expr +# these rules detect that and skip speaking it (should be similar regular rule) +- name: skip-punct-at-end-zoom-in + tag: mrow + match: + - "($NavCommand = 'ZoomIn' or $NavCommand = 'ZoomInAll' or $NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and" + - " parent::m:math and count(*)=2 and" + - " *[2][translate(.,'.,;:?', '')='']" + replace: + - x: "*[1]" + +# ********* ZoomIn *************** +- name: zoom-in-leaf + tag: "*" + match: "($NavCommand = 'ZoomIn' or $NavCommand = 'ZoomInAll') and IsNode(., 'leaf')" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity != 'Terse')"] + replace: [x: "."] + - test: + if: "$ReadZoomLevel !=-1" + then: + - set_variables: [ReadZoomLevel: "0"] + - set_variables: + - NavNode: "@id" + - NavNodeOffset: "IfThenElse(string-length(.) > 1 and $MatchCounter = 1 and $NavNodeOffset = 0, '1', '0')" + +# special case of zooming into a table -- move to the first row (if only one row, first column) +- name: zoom-in-table + tag: "*" + match: "$NavCommand = 'ZoomIn' and (name(.) = 'mtable' or (count(*)=1 and *[1][@data-from-mathml='mtable']))" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNode: "IfThenElse(count(*)=1, (*[1]/*[1]/@id), (*[1]/@id))"] + +- name: zoom-in-mrow-in-math + # zooming in only once is meaningless because 'math' has only a single child and it was spoken at the math level -- dig inside and do it again + tag: math + match: "$NavCommand = 'ZoomIn' or $NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom'" + replace: + - test: + if: "$NavCommand = 'MovePreviousZoom'" + then: [x: "*[last()]"] + else: [x: "*[1]"] + +- # For msqrt and menclose, if the single child isn't an mrow, don't zoom in + name: zoom-in-again + tag: "*" + match: + - "($NavCommand = 'ZoomIn' or " + - " ($NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and $NavMode='Enhanced') and " + - "count(*)=1 and + (*[1][self::m:mrow or @data-from-mathml='mrow'] and + not(@data-from-mathml='msqrt' or self::m:msqrt or @data-from-mathml='menclose' or self::m:menclose))" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "not(*[1][self::m:mrow or @data-from-mathml='mrow'])" + then: + - with: + variables: [Move2D: "'in'", Child2D: "IfThenElse($NavCommand = 'MovePreviousZoom', (*[last()]), (*[1]))"] # phrase('in' the denominator) + replace: [x: "IfThenElse($NavCommand = 'MovePreviousZoom', 1, $Child2D)"] + - test: + if: "$NavCommand = 'MovePreviousZoom'" + then: [x: "*[last()]"] + else: [x: "*[1]"] + +- name: zoom-in-enhanced + tag: "*" + match: "$NavCommand = 'ZoomIn' and $NavMode='Enhanced'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + - if: "self::m:mtr or self::m:mlabeledtr" + then: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]/*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - set_variables: [NavNode: "*[1]/*[1]/@id"] # skip mtd + - else_if: "*[1][self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]" # auto zoom + then: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - set_variables: [NavNode: "*[1]/*[2]/@id"] # skip parens/brackets + else: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + # "(...)" to get around some weird parse bug" + - set_variables: + - NavNode: "*[1]/@id" + - NavNodeOffset: "IfThenElse(*[1]/@data-id-offset, (*[1]/@data-id-offset), '0')" + + +- name: zoom-in-simple + tag: "*" + match: "$NavCommand = 'ZoomIn' and $NavMode='Simple'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "$MatchCounter > 1 and IsNode(., '2D')" + then: + - set_variables: # time to stop, not going "in" to next thing, so before "Move2D" + - NavNode: "@id" + - NavNodeOffset: "IfThenElse(@data-id-offset, @data-id-offset, '0')" + else: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - x: "*[1]" + + # At this point, we are zooming in on a non-2D element, non-leaf in Character mode +- name: zoom-in-2D-character + tag: "*" + match: "$NavCommand = 'ZoomIn' and (IsNode(., '2D') or not(IsNode(., 'mathml')))" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - test: + if: "$NavMode = 'Simple'" + then: + - set_variables: [NavNode: "*[1]/@id"] + else: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "*[1]"] + + # At this point, we are zooming in on a non-2D element, non-leaf in Character mode +- name: zoom-in-default + tag: "*" + match: "$NavCommand = 'ZoomIn'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - x: "*[1]" + + +- name: zoom-in-all-default + tag: "*" + match: "$NavCommand = 'ZoomInAll'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - x: "*[1]" + +- name: zoom-out + tag: math + match: "$NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity != 'Terse')"] + replace: [x: "."] + - set_variables: [NavNode: "*[1]/@id"] # no-op for $NavCommand = 'ZoomOut' + +- name: skip-punct-at-end-zoom-out + tag: mrow + match: + - "($NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll') and" + - " parent::m:math and count(*)=2 and" + - " *[2][translate(.,'.,;:?', '')='']" + replace: + - x: ".." + +- name: zoom-out-top + tag: "*" + match: + - "($NavCommand = 'ZoomOut' or $NavCommand = 'ZoomOutAll') and" + - "parent::m:math" + replace: + - x: ".." # let math rule deal with it + +- name: zoom-out-all-default + tag: "*" + match: "$NavCommand = 'ZoomOutAll'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - with: + variables: [Move2D: "'out of'", Child2D: "."] + replace: [x: ".."] + - x: ".." + +# deal with internal zooming: MoveNextZoom and MovePreviousZoom + +# start with Enhanced mode +- name: move-zoom-enhanced + tag: "*" + match: + - "($NavCommand = 'MoveNextZoom' or $NavCommand = 'MovePreviousZoom') and " + - "$NavMode = 'Enhanced'" + replace: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - test: + - if: "count(*)> 1 or IsNode(., 'leaf') or + @data-from-mathml='msqrt' or self::m:msqrt or @data-from-mathml='menclose' or self::m:menclose" + then: + - set_variables: + - NavNode: "@id" + - NavNodeOffset: "IfThenElse(@data-id-offset, @data-id-offset, '0')" + else: [x: "*[1]"] + +- name: move-next-zoom-not-enhanced + # $ReadZoomLevel must be >= 0 + tag: "*" + match: "$NavCommand = 'MoveNextZoom'" + replace: + #don't bother with MatchCounter since we only get here if > 1 + - test: + if: "IsNode(., 'leaf') or $ReadZoomLevel >= DistanceFromLeaf(., false, $NavMode!='Character')" + then: + # - with: + # variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) + # replace: [x: ".."] + - set_variables: + - NavNode: "@id" + - NavNodeOffset: "IfThenElse(@data-id-offset, @data-id-offset, '0')" + else: + - with: + variables: [Move2D: "'in'", Child2D: "*[1]"] # phrase('in' the denominator) + replace: [x: "."] + - x: "*[1]" + +- name: move-previous-zoom-not-enhanced + # $ReadZoomLevel must be >= 0 + tag: "*" + match: "$NavCommand = 'MovePreviousZoom'" + replace: + #don't bother with MatchCounter since we only get here if > 1 + - test: + if: "$ReadZoomLevel >= DistanceFromLeaf(., true, $NavMode!='Character')" + then: + # - with: + # variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator) + # replace: [x: ".."] + - set_variables: + - NavNode: "@id" + - NavNodeOffset: "IfThenElse(@data-id-offset, @data-id-offset, '0')" + else: + - with: + variables: [Move2D: "'in'", Child2D: "*[last()]"] # phrase('in' the denominator) + replace: [x: "."] + - x: "*[last()]" + +# ********* ZoomOut *************** +- name: zoom-out-default + tag: mtd + match: "$Move2D = '' and ($NavCommand = 'ZoomOut')" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + # we need to speak it here + # - T: "行" # phrase(the first 'row' of the matrix) + # # if we let the speech rules speak the row, it is given just the MathML for the row, so the row # will always be '1' + # - x: "count(../preceding-sibling::*)+1" + # - pause: medium + - set_variables: + - NavNode: "../@id" + - NavNodeOffset: "IfThenElse(../@data-id-offset, ../@data-id-offset, '0')" + +- name: zoom-out + # a row around a single element -- these might duplicate the position/offset, so we jump an extra level here + tag: "*" + match: "$NavCommand = 'ZoomOut'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + - if: "$NavNodeOffset > 0 and IsNode(., 'leaf')" # Inside leaf -- just reset offset, intent offset doesn't change + then: + - set_variables: [NavNodeOffset: "0"] + # NavNode remains the same + - else_if: "$NavMode='Enhanced' and parent::*[self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]" + then: [x: ".."] # auto-zoom: move out a level and retry + else: + - with: + variables: [Move2D: "'out of'", Child2D: "."] # phrase('out of' the denominator) + replace: [x: ".."] + - test: + if: "parent::m:mtd" + then: [x: ".."] + else: + - test: + if: "DEBUG($ReadZoomLevel)!=-1" + then: [set_variables: [ReadZoomLevel: "DistanceFromLeaf(.., true, $NavMode!='Character')"]] + - set_variables: + - NavNode: "../@id" + - NavNodeOffset: "IfThenElse(../@data-id-offset, ../@data-id-offset, '0')" + +# ********* MoveStart/End *************** +- name: math-move-to-start-or-end + tag: math + match: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart' or $NavCommand = 'MoveEnd' or $NavCommand = 'MoveLineEnd'" + replace: + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'MoveStart'" + then: [T: "移到表达式开头"] # phrase('move to start of math') + - else_if: "$NavCommand = 'MoveLineStart'" + then: [T: "移到本行开头"] # phrase('move to start of line') + - else_if: "$NavCommand = 'MoveEnd'" + then: [T: "移到表达式结尾"] # phrase('move to end of math') + else: [T: "移到本行结尾"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') + - pause: "medium" + - test: + if: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart'" + then: + # move inside of the mrow inside of 'math' or inside the fraction, etc (hence two levels down) + # Note: an apparent bug in the xpath code doesn't let me use IfThenElse for the 2 if: then: below + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: + - test: + if: "*[1]/*[1]" # could be a x, so no grandchild + then: [x: "*[1]/*[1]"] + else: [x: "*[1]"] + else: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: + - test: + if: "*[last()]/*[last()]" # could be a x, so no grandchild + then: [x: "*[last()]/*[last()]"] + else: [x: "*[last()]"] + +# We stop when the parent is 2d (e.g., frac), but not if in leaf base of msub/msup/msubsup/mmultiscripts because that's really on the same line +- name: move-to-start-or-end-2d + tag: "*" + match: + - "($NavCommand = 'MoveLineStart' or $NavCommand = 'MoveLineEnd') and IsNode(.., '2D') and" + - "not( IsNode(., 'leaf') and" + - " parent::*[1][self::m:msub or self::m:msup or self::m:msubsup or self::m:mmultiscripts or" + - " @data-from-mathml and" + - " (@data-from-mathml='msub' or @data-from-mathml='msup' or" + - " @data-from-mathml='msubsup' or @data-from-mathml='mmultiscripts')" + - " ] )" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - test: + if: "$NavCommand = 'MoveLineStart'" + then: [T: "移到本行开头"] # phrase('move to start of line') + else: [T: "移到本行结尾"] # "$NavCommand = 'MoveLineEnd'" # phrase('move to end of line') + - pause: "medium" + - test: + if: "self::m:mrow or @data-from-mathml = 'mrow'" + then_test: + if: "$NavCommand = 'MoveLineStart'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "*[1]"] + else: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "*[last()]"] + else: [set_variables: [NavNode: "@id"]] + +- name: move-to-start-or-end-default + tag: "*" + match: "$NavCommand = 'MoveStart' or $NavCommand = 'MoveLineStart' or $NavCommand = 'MoveEnd' or $NavCommand = 'MoveLineEnd'" + replace: + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: ".."] + +# Table-related movement +# Typically, we need to zoom out to the mtd level, then we move the appropriate direction +- name: not-in-table + + + tag: math + match: + - "$NavCommand='MoveCellPrevious' or $NavCommand='MoveCellNext' or" + - "$NavCommand='MoveCellUp' or $NavCommand='MoveCellDown' or" + - "$NavCommand='MoveColumnStart' or $NavCommand='MoveColumnEnd' or" + - "$NavCommand='ReadCellCurrent'" + replace: + - T: "不在表格中" # phrase('not in table') + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-previous + tag: mtd + match: "$NavCommand='MoveCellPrevious'" + replace: + - test: + if: "preceding-sibling::*" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到前一项" # phrase('move left') + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "列" # phrase(the first 'column' of the table) + - x: "count(preceding-sibling::*)" + - pause: medium + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: + - set_variables: [NavNode: "preceding-sibling::*[1]/*[1]/@id"] + else: + - T: "没有前一列" # phrase('no previous column' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-next + tag: mtd + match: "$NavCommand='MoveCellNext'" + replace: + - test: + if: "following-sibling::*" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到后一项" # phrase('move right') + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "列" # phrase(the first 'column' in the table) + - x: "count(preceding-sibling::*)+2" + - pause: medium + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + else: + - set_variables: [NavNode: "following-sibling::*[1]/*[1]/@id"] + else: + - T: "没有后一列" # phrase('no next column' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-up + tag: mtd + match: "$NavCommand='MoveCellUp'" + replace: + - test: + if: "../preceding-sibling::*" + then: + - with: + variables: [Column: "count(preceding-sibling::*)+1"] # store this because otherwise the value is used in the wrong context below + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到上一行" # phrase('move up' to previous row in the table) + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "行" # phrase(the previous 'row' in the table) + - x: "count(../preceding-sibling::*)" + - pause: short + - T: "列" # phrase(the previous 'column' in the table) + - x: "count(preceding-sibling::*)+1" + - pause: medium + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "../preceding-sibling::*[1]/*[$Column]"] + else: + - set_variables: [NavNode: "../preceding-sibling::*[1]/*[$Column]/*[1]/@id"] + else: + - T: "没有上一行" # phrase('no previous row' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-down + tag: mtd + match: "$NavCommand='MoveCellDown'" + replace: + - test: + if: "../following-sibling::*" + then: + - with: + variables: [Column: "count(preceding-sibling::*)+1"] # store this because otherwise the value is used in the wrong context below + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到下一行" # phrase('move down' to the next row in the table) + - pause: short + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "行" # phrase(the next 'row' in the table) + - x: "count(../preceding-sibling::*)+2" + - pause: short + - T: "列" # phrase(the next 'column' in the table) + - x: "count(preceding-sibling::*)+1" + - pause: medium + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "../following-sibling::*[1]/*[$Column]"] + else: + - set_variables: [NavNode: "../following-sibling::*[1]/*[$Column]/*[1]/@id"] + else: + - T: "没有下一行" # phrase('no next row' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-up + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellUp'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到上一行" # phrase('move to previous row' in the table) + - pause: medium + - test: + if: "preceding-sibling::*" + then: + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: + - set_variables: [NavNode: "preceding-sibling::*[1]/@id"] + else: + - T: "没有上一行" # phrase('no previous row' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-down + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellDown'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到下一行" # phrase('move to next row' in the table) + - pause: medium + - test: + if: "following-sibling::*" + then: + - test: + if: "$NavMode='Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + else: + - set_variables: [NavNode: "following-sibling::*[1]/@id"] + else: + - T: "没有下一行" # phrase('no next row' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-previous + # if a row is selected, there is no previous/next column, so this is trivial + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellPrevious'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到前一列" # phrase('move to previous column' in the table) + - pause: medium + - T: "没有前一列" # phrase('no previous column' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: move-cell-next + # if a row is selected, there is no previous/next column, so this is trivial + tag: [mtr, mlabeledtr] + match: "$NavCommand='MoveCellNext'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "移到后一列" # phrase('move to next column' in the table) + - pause: medium + - T: "没有后一列" # phrase('no next row' in the table) + - set_variables: [SpeakExpression: "'false'"] + +- name: default-read-cell + tag: "*" + match: "$NavCommand='ReadCellCurrent'" + replace: + - with: + variables: [MTD: "ancestor::m:mtd"] + replace: + - test: + if: "$MTD" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "朗读当前单元格" # phrase('read current entry' in the table) + - pause: medium + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "行" # phrase(the previous 'row' in the table) + - x: "count($MTD[1]/../preceding-sibling::*)+1" + - pause: short + - T: "列" # phrase(the previous 'column' in the table) + - x: "count($MTD[1]/preceding-sibling::*)+1" + - pause: short + - set_variables: [NavNode: "$MTD[1]/*[1]/@id"] + else: + - T: "不在表格中" # phrase('not in table' or matrix) + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +# mtd ? ( $NavCommand='MoveColumnStart' ) +# => MoveColStart { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^match[0][index(match)][0].dfs; +# ::EndPosition = ^^match[0][index(match)][0].offset; +# }; + +# mtd ? ( $NavCommand='MoveColumnEnd' ) +# => MoveColEnd { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^match[count(^^match)-1][index(match)][0].dfs; +# ::EndPosition = ^^match[count(^^match)-1][index(match)][0].offset; +# }; + + + +# # Rules for columnar math (mstack and mlongdiv) -- each row is an msrow or mscarries except for the start of mlongdiv +# # FIX: not dealing with different number of digits on different lines +# # FIX: not dealing with + (etc) on same line if they are on the right side (Dutch, others) +# # FIX: not dealing with intervening msline (say it and move on??) +# # FIX: not dealing with carries well +# # FIX: not dealing with navigation of first three children of mlongdiv +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') && has_previous(match) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "previous"; +# ::StartPosition = previous(match).dfs; +# ::EndPosition = previous(match).offset; +# }; + +# # no previous child -- in first column -- don't move +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious' ) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "previous"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') && has_next(match) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "next"; +# ::StartPosition = next(match).dfs; +# ::EndPosition = next(match).offset; +# }; + +# # no next child -- in first column -- don't move +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext' ) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "next"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveCellUp' && has_previous(^^match) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "up"; +# ::StartPosition = ^^^match[index(^^match)-1][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[index(^^match)-1][-1][index(match)-count(^match)].offset; +# }; + +# # no previous child -- in first column -- don't move +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveCellUp' ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "up"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveCellDown' && has_next(^^match) ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "down"; +# ::StartPosition = ^^^match[index(^^match)+1][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[index(^^match)+1][-1][index(match)-count(^match)].offset; +# }; + +# # no previous child -- in first column -- don't move +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveCellDown' ) +# => MoveCell { +# ruleRef = name(^^match); +# wordRef = "down"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveColumnStart' ) +# => MoveColStart { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^^match[0][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[0][-1][index(match)-count(^match)].offset; +# }; + +# char ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveColumnEnd' ) +# => MoveColEnd { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].offset; +# }; + +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') && has_previous(match) ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "previous"; +# ::StartPosition = previous(match).dfs; +# ::EndPosition = previous(match).offset; +# }; + +# # no previous child -- in first column -- don't move +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# ($NavCommand="MovePrevious" || $NavCommand='MoveCellPrevious') ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "previous"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') && has_next(match) ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "next"; +# ::StartPosition = next(match).dfs; +# ::EndPosition = next(match).offset; +# }; + +# # no next child -- in last column -- don't move +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# ($NavCommand="MoveNext" || $NavCommand='MoveCellNext') ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "next"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# $NavCommand='MoveCellUp' && has_previous(^match) ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "up"; +# ::StartPosition = ^^match[index(^match)-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^match[index(^match)-1][index(match)-count(^match)].offset; +# }; + +# # no previous child -- in first row -- don't move +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# $NavCommand='MoveCellUp' ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "up"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# $NavCommand='MoveCellDown' && has_next(^match) ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "down"; +# ::StartPosition = ^^match[index(^match)+1][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^match[index(^match)+1][-1][index(match)-count(^match)].offset; +# }; + +# # no next child -- in last row -- don't move +# any ? ( (name(match)=="mn" || name(match)=="none") && +# has_parent(match) && name(^match)=="mscarries" && +# $NavCommand='MoveCellDown' ) +# => MoveCell { +# ruleRef = name(^match); +# wordRef = "down"; +# childIndex = -1; # key to know what to say for each notation +# ::SpeakAfterMove = false; +# }; + +# mn ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveColumnStart' ) +# => MoveColStart { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^^match[0][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[0][-1][index(match)-count(^match)].offset; +# }; + +# mn ? ( has_parent(match, 2) && name(^match)=="mn" && name(^^match)=="msrow" && +# $NavCommand='MoveColumnEnd' ) +# => MoveColEnd { +# ruleRef = name(^^match); +# column = index(match); +# ::StartPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].dfs; +# ::EndPosition = ^^^match[count(^^^match)-1][-1][index(match)-count(^match)].offset; +# }; + + + +- name: default-cell-move + + tag: "*" + match: + - "$NavCommand='MoveCellPrevious' or $NavCommand='MoveCellNext' or" + - "$NavCommand='MoveCellUp' or $NavCommand='MoveCellDown' or" + - "$NavCommand='MoveColumnStart' or $NavCommand='MoveColumnEnd' or" + - "$NavCommand='ReadCellCurrent'" + replace: + - test: + if: "ancestor::m:mtd" + then: + - x: "ancestor::m:mtd[1]" # try again on an mtd node + else: + - T: "不在表格中" # phrase('not in table' or matrix) + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +# ======== Move/Read/Describe Next rules ================= + + +- name: move-next-character + tag: [mn, mi, mtext] + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext' or $NavCommand = 'MoveNextZoom') and" + - "$NavNodeOffset > 0 and" + - "($NavNodeOffset < string-length(.) or name(EdgeNode(., 'right', 'math'))!='math')" # not at edge of math + replace: + - test: + if: "$NavNodeOffset < string-length(.)" + then: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNodeOffset: "$NavNodeOffset + 1"] + else: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNodeOffset: "0"] + - x: "." + +# skip 'none' +- name: move-next-none + tag: [none, mprescripts] + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext' or $NavCommand = 'DescribePrevious' or $NavCommand = 'MoveNextZoom') and" + - "parent::*[1][name(.)='mmultiscripts'] and following-sibling::*" + replace: + - with: + variables: [Following: "following-sibling::*[1]"] + replace: + # two 'none's in a row -- move over and try again; one 'none', zoom in on next + - test: + if: "$Following[name(.)='none']" + then: [x: "$Following"] + else: + - with: + variables: [Move2D: "'in'", Child2D: "$Following"] # phrase('in' the denominator) + replace: [x: ".."] + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "$Following"] + + + +# skip invisible chars except for Enhanced mode when "times" should be read +- name: move-next-invisible + tag: "*" + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and" + - "following-sibling::*[1][name(.)='mo' and translate(., '\u2061\u2062\u2063\u2064', '')='']" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "following-sibling::*[1][.='\u2062' or .='\u2064'] and + ($NavMode='Enhanced' or ($NavMode='Simple' and following-sibling::*[2][not(IsNode(., 'mathml'))]))" # invisible times and plus + then: [set_variables: [NavNode: "following-sibling::*[1]/@id"]] + else: [x: "following-sibling::*[1]"] + +- name: move-next-no-auto-zoom-at-edge + # at edge of 2D and in a mode where moving right isn't an option + tag: "*" + variables: [EdgeNode: "EdgeNode(., 'right', '2D')"] + match: "$NavCommand = 'MoveNext' and $NavMode!='Character' and not($AutoZoomOut) and $EdgeNode/@id!=@id" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - T: "无法移到后一项" # phrase('cannot move right') + - pause: medium + - with: + variables: + - Move2D: "'end of'" + - Child2D: "$EdgeNode/*[last()]" + - MatchCounter: $MatchCounter + 1 + replace: [x: "$EdgeNode"] + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +- name: move-next-no-auto-zoom-at-edge-math + # at edge of math -- no where to go (must be after we rule out being at the edge of 2D) because we want to speak that + tag: "*" + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and" + - "(self::m:math or name(EdgeNode(., 'right', 'math'))='math')" # at edge of math + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - T: "无法" # phrase('cannot' move right in expression) + - test: + - if: "$NavCommand = 'MoveNext'" + then: [T: "移动"] # phrase('move' to next entry in table) + - else_if: "$NavCommand = 'ReadNext'" + then: [T: "朗读"] # phrase('read' next entry in table) + else: [T: "概述"] # phrase('describe' next entry in table) + - T: "后一项" # phrase(move 'right') + - pause: short + - T: "表达式结尾" # phrase(move 'end of math') + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +- name: move-next-auto-zoom-up-one-level + # Last child or in auto-zoom'd in-- move up a level and try again + # Note: we've already checked the for the case where we are at an edge and should not AutoZoomOut + tag: "*" + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and" + - "( not(following-sibling::*) or" + - " ( $NavMode='Enhanced' and " + - " count(following-sibling::*)=1 and (IsBracketed(.., '(', ')') or IsBracketed(.., '[', ']'))" + - " )" + - ")" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "following-sibling::*" + then: + - with: + variables: [Move2D: "'in'", Child2D: "."] # phrase('in' the denominator) + replace: [x: ".."] + else: + - with: + variables: [Move2D: "'out of'", Child2D: "."] + replace: [x: ".."] + - x: ".." + +# At this point, if XXXNext, then we know there is must be a right sibling +- name: move-next-default + tag: mtd + match: "$Move2D = '' and ($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext')" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "following-sibling::*" + then: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "列" # phrase(the previous 'column' in the table) + - x: "count(preceding-sibling::*)+2" + - pause: short + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + else: [set_variables: [NavNode: "following-sibling::*[1]/*[1]/@id"]] + else: + - x: ".." # try again at the row level + +- name: move-next-default + tag: [mtr, mlabeledtr] + match: "$Move2D = '' and + ($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and + following-sibling::*" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + else: + - set_variables: [NavNode: "following-sibling::*[1]/@id"] + +- name: move-next-auto-zoom-parens + # auto-zoom into next child if next child is parenthesized expr + tag: "*" + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and" + - "$NavMode='Enhanced' and" + - "parent::m:mrow and following-sibling::* and" + - "following-sibling::*[1][self::m:mrow and count(*)=3 and " #exclude empty parens + - " (IsBracketed(., '(', ')') or IsBracketed(., '[', ']'))" + - " ]" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNode: "following-sibling::*[1]/*[2]/@id"] + +# normal cases for MoveNext +- name: move-next-locked-zoom-level + # locked zoom level + tag: "*" + match: + - "($NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext') and" + - "$ReadZoomLevel>=0" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + # if in base (nothing before), we must be moving to a script, so "in" will be said + if: "preceding-sibling::* and following-sibling::*[1][name(.)='none']" + then: + - with: + variables: [Move2D: "'out of'", Child2D: "."] + replace: [x: ".."] + - x: "following-sibling::*[1]" + else: + - with: + variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) + replace: [x: ".."] + - with: + variables: [NavCommand: "'MoveNextZoom'"] + replace: [x: "following-sibling::*[1]"] + +- name: move-next-default + tag: "*" + match: "$NavCommand = 'MoveNext' or $NavCommand = 'ReadNext' or $NavCommand = 'DescribeNext'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "following-sibling::*[1][@data-from-mathml='none' or @data-from-mathml='mprescripts']" + then: [x: "following-sibling::*[1]"] + else: + - test: + if: "IsNode(.., '2D') or not(IsNode(.., 'mathml'))" + then: + - with: + variables: [Move2D: "'in'", Child2D: "following-sibling::*[1]"] # phrase('in' the denominator) + replace: [x: ".."] + - set_variables: [NavNode: "following-sibling::*[1]/@id"] + +# ======== Move/Read/Describe Previous rules ================= + +# skip 'none' +- name: move-previous-none + tag: [none, mprescripts] + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious' or $NavCommand = 'MovePreviousZoom') and" + - "parent::*[1][name(.)='mmultiscripts'] and preceding-sibling::*" + replace: + - with: + variables: [Preceding: "preceding-sibling::*[1]"] + replace: + # two 'none's in a row -- move over and try again; one 'none', zoom in on preceding + - test: + if: "$Preceding[name(.)='none']" + then: [x: "$Preceding"] + else: + - with: + variables: [Move2D: "'in'", Child2D: "$Preceding"] # phrase('in' the denominator) + replace: [x: ".."] + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "$Preceding"] + + +# skip invisible chars except for Enhanced mode when "times" should be read +- name: move-previous-invisible + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "preceding-sibling::*[1][name(.)='mo' and translate(., '\u2061\u2062\u2063\u2064', '')='']" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "preceding-sibling::*[1][.='\u2062' or .='\u2064'] and $NavMode='Enhanced'" # invisible times and plus + then: [set_variables: [NavNode: "preceding-sibling::*[1]/@id"]] + else: [x: "preceding-sibling::*[1]"] + +# two rules for when can't move left +- name: move-previous-no-auto-zoom-at-edge + # at edge of 2D and in a mode where moving left isn't an option + tag: "*" + variables: [EdgeNode: "EdgeNode(., 'left', '2D')"] + match: "$NavCommand = 'MovePrevious' and $NavMode!='Character' and not($AutoZoomOut) and $EdgeNode/@id!=@id" + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose' and $NavCommand = 'MovePrevious'" + then: + - T: "无法移到前一项" # phrase('cannot move left' in expression) + - pause: medium + - with: + variables: + - Move2D: "'end of'" + - Child2D: "$EdgeNode/*[1]" + - MatchCounter: $MatchCounter + 1 + replace: [x: "$EdgeNode"] + - pause: long + +- name: move-previous-no-auto-zoom-at-edge-of-math + # at edge of math -- no where to go (must be after we rule out being at the edge of 2D) because we want to speak that + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "(self::m:math or name(EdgeNode(., 'left', 'math'))='math')" + replace: + - T: "表达式开头" # phrase('start of math') + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +- name: move-previous-at-end + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "name(EdgeNode(., 'left', 'math'))='math'" # at edge of math + replace: + - test: + if: "$MatchCounter = 0 and $NavVerbosity = 'Verbose'" + then: + - T: "无法移到前一项" # phrase('cannot move left') + - pause: short + - with: + variables: [Move2D: "'start of'", Child2D: "."] + replace: [x: "."] + - pause: long + - set_variables: [SpeakExpression: "'false'"] + +- name: move-prev-character + tag: [mn, mi, mtext] + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious' or $NavCommand = 'MovePreviousZoom') and" + - "$NavNodeOffset > 0 and" + - "($NavNodeOffset > 1 or name(EdgeNode(., 'left', 'math'))!='math')" # not at edge of math + replace: + - test: + if: "$NavNodeOffset > 1" + then: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNodeOffset: "$NavNodeOffset - 1"] + else: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - set_variables: [NavNodeOffset: "0"] + - x: "." + +- name: move-previous-auto-zoom-up-one-level + # Last child or in auto-zoom'd in-- move up a level and try again + # Note: we've already checked the for the case where we are at an edge and should not AutoZoomOut + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "( not(preceding-sibling::*) or" + - " ( $NavMode='Enhanced' and " + - " count(preceding-sibling::*)=1 and (IsBracketed(.., '(', ')') or IsBracketed(.., '[', ']'))" + - " )" + - ")" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "preceding-sibling::*" + then: + - with: + variables: [Move2D: "'in'", Child2D: "."] # phrase('in' the denominator) + replace: [x: ".."] + else: + - with: + variables: [Move2D: "'out of'", Child2D: "."] + replace: [x: ".."] + - x: ".." + +- name: move-previous-auto-zoom-parens + # auto-zoom into previous child if previous child is parenthesized expr + # Note: there is an asymmetry here from MoveNext because the base of a scripted might have parens for grouping, but not true for the script + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "$NavMode='Enhanced' and" + - "preceding-sibling::* and" + - "(parent::m:mrow or parent::m:msub or parent::m:msup or" + - " (count(preceding-sibling::*)=1 and (parent::m:msubsup or parent::m:mmultiscripts))" # make sure moving into base + - ") and" + - "preceding-sibling::*[1][self::m:mrow and count(*)=3 and " #exclude empty parens + - " (IsBracketed(., '(', ')') or IsBracketed(., '[', ']'))" + - " ]" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "not(parent::m:mrow)" + then: + - with: + variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator) + replace: [x: ".."] + - set_variables: [NavNode: "preceding-sibling::*[1]/*[2]/@id"] + +# normal cases for MovePrevious + +- name: move-previous-default + tag: mtd + match: "$Move2D = '' and + ($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and + preceding-sibling::*" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - T: "列" # phrase(the first 'column' in the table) + - x: "count(preceding-sibling::*)" + - pause: short + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: [set_variables: [NavNode: "preceding-sibling::*[1]/*[last()]/@id"]] + +- name: move-previous-default + tag: [mtr, mlabeledtr] + match: "$Move2D = '' and ($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious')" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "preceding-sibling::*" + then: + - test: + if: "$NavMode = 'Character'" + then: + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + else: + - set_variables: [NavNode: "preceding-sibling::*[1]/@id"] + else: [x: ".."] # try again for after + +- name: move-previous-locked-zoom-level + # locked zoom level + tag: "*" + match: + - "($NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious') and" + - "$ReadZoomLevel>=0" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + # if moving into base (nothing before), we must be moving to the base, so "in" will be said + if: "count(preceding-sibling::*) > 2 and preceding-sibling::*[1][name(.)='none']" + then: + - with: + variables: [Move2D: "'out of'", Child2D: "."] + replace: [x: ".."] + - x: "preceding-sibling::*[1]" # skip over 'none' + else: + - with: + variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator) + replace: [x: ".."] + - with: + variables: [NavCommand: "'MovePreviousZoom'"] + replace: [x: "preceding-sibling::*[1]"] + +- name: move-previous-default + tag: "*" + match: "$NavCommand = 'MovePrevious' or $NavCommand = 'ReadPrevious' or $NavCommand = 'DescribePrevious'" + replace: + - with: + variables: [SayCommand: "string($NavVerbosity = 'Verbose')"] + replace: [x: "."] + - test: + if: "preceding-sibling::*[1][@data-from-mathml='none' or @data-from-mathml='mprescripts']" + then: [x: "preceding-sibling::*[1]"] + else: + - test: + if: "IsNode(.., '2D') or not(IsNode(.., 'mathml'))" + then: + - with: + variables: [Move2D: "'in'", Child2D: "preceding-sibling::*[1]"] # phrase('in' the denominator) + replace: [x: ".."] + - set_variables: [NavNode: "preceding-sibling::*[1]/@id"] + +# ********* ReadZoomLevel toggle *************** +# These set ::NavMode + +- name: toggle-mode-up + tag: "*" + match: "$NavCommand = 'ToggleZoomLockUp'" + replace: + - test: + - if: "$NavMode = 'Enhanced'" + then: + - T: "字符" # phrase(a mathematical 'character') + - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] + - else_if: "$NavMode = 'Character'" + then: + - T: "简单" # phrase(a 'simple' way to do something) + - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] + - else: + - T: "增强" # phrase(an 'enhanced' way to do something) + - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] + - T: "模式" # phrase(a simple 'mode' of use) + - pause: long + - test: + - if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling + then: + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"] + replace: [x: "."] + +- name: toggle-mode-down + tag: "*" + match: "$NavCommand = 'ToggleZoomLockDown'" + replace: + - test: + - if: "$NavMode = 'Enhanced'" + then: + - T: "简单" # phrase(a 'simple' way to do something) + - set_variables: [NavMode: "'Simple'", ReadZoomLevel: "1"] + - else_if: "$NavMode = 'Character'" + then: + - T: "增强" # phrase(an 'enhanced' way to do something) + - set_variables: [NavMode: "'Enhanced'", ReadZoomLevel: "-1"] + - else: + - T: "字符" # phrase(a mathematical 'character') + - set_variables: [NavMode: "'Character'", ReadZoomLevel: "1"] + - T: "模式" # phrase(a simple 'mode' of use) + - pause: long + - test: + - if: "$NavMode != 'Enhanced'" # potentially need to zoom to the sibling + then: + - with: + variables: [MatchCounter: "$MatchCounter + 1", NavCommand: "'MoveNextZoom'"] + replace: [x: "."] + +- name: toggle-speech-describe + tag: "*" + match: "$NavCommand = 'ToggleSpeakMode'" + replace: + - test: + if: "$Overview = 'true'" + then: + - T: "移动后朗读表达式" # phrase('speak expression after move') + - pause: long + - set_variables: [Overview: "'false'"] + else: + - T: "移动后概述表达式" # phrase('overview of expression after move') + - pause: long + - set_variables: [Overview: "'true'"] + +- name: current + tag: "*" + match: "$NavCommand = 'ReadCurrent' or $NavCommand = 'DescribeCurrent'" + replace: + - test: + if: "$NavVerbosity = 'Verbose'" + then: + - test: + - if: "$NavCommand = 'ReadCurrent'" + then: [T: "朗读"] # phrase('read' next entry in table) + else: [T: "概述"] # phrase('describe' next entry in table) + - T: "当前项" # phrase('current' entry in table) + - pause: long + - set_variables: [NavNode: "@id"] + +# this needs to be near the end because we only test for 'Describe', "Read", etc., and we don't want to get 'DescribeNext', etc. +- name: placemarker + + tag: "*" + match: + - "starts-with($NavCommand, 'Read') or " + - "starts-with($NavCommand, 'Describe') or " + - "starts-with($NavCommand, 'MoveTo')" + replace: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - test: + - if: "starts-with($NavCommand, 'Read')" + then: [T: "朗读"] # phrase('read' next entry in table) + - else_if: "starts-with($NavCommand, 'Describe')" + then: [T: "概述"] # phrase('describe' next entry in table) + - else_if: "starts-with($NavCommand, 'MoveTo')" + then: [T: "移到"] # phrase('move to' the next entry in table) + else: [T: "设置"] # phrase('set' the value of the next entry in table) + - T: "位置标记" # phrase('placeholder' for the value) + - x: "$PlaceMarkerIndex" + - pause: long + - set_variables: [NavNode: "$PlaceMarker"] + +- name: set-placemarker + tag: "*" + match: "starts-with($NavCommand, 'SetPlacemarker')" + replace: + - test: + if: "$NavVerbosity != 'Terse'" + then: + - T: "设置位置标记" # phrase('set placeholder' to the value) + - x: "$PlaceMarkerIndex" + - pause: long + - set_variables: [NavNode: "@id"] + +# ********* WhereAmI *************** + +# FIX: WhereAmI needs support from the Rust code to loop around and do speech at each iteration. +# Alternatively, it could insert a special token that Rust code does a "replace" on with the speech (e.g. SPEECH_AT{id}) +# or a new command "speak" which takes a node id +- name: where-am-i-start + tag: "*" + match: "($NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll') and $MatchCounter = 0" + replace: + - translate: "@id" + - pause: long + - with: + variables: [MatchCounter: "$MatchCounter + 1"] + replace: [x: ".."] + +- name: where-am-i-stop + tag: "*" + match: + - "($NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll') and $MatchCounter > 0 and" + # stopping conditions + - "(self::m:math or " + - " parent::*[self::m:math and (count(*)=1 or (count(*)=2 and *[2][.=',' or .='.' or .=';' or .='?']) ) ] " + - ")" + replace: + - test: + if: "$NavCommand = 'WhereAmI'" + then: + - T: "已在最外层" # phrase('inside of nothing more') + - pause: long + - set_variables: [SpeakExpression: "'false'"] + else: + - T: "位于" # phrase('inside' a big expression) + - pause: medium + - set_variables: [NavNode: "@id"] + +- name: where-am-i-middle + tag: "*" + match: "$NavCommand = 'WhereAmI' or $NavCommand = 'WhereAmIAll'" + replace: + - T: "位于" # phrase('inside' a big expression) + - pause: medium + - test: + - if: "$NavMode='Enhanced' and parent::*[self::m:mrow and (IsBracketed(., '(', ')', false) or IsBracketed(., '[', ']', false))]" + then: [x: ".."] # auto-zoom up + - else_if: "$NavCommand = 'WhereAmI'" + then: [set_variables: [NavNode: "@id"]] + else: + - translate: "@id" + - pause: long + - x: '..' diff --git a/Rules/Languages/zh/overview.yaml b/Rules/Languages/zh/overview.yaml new file mode 100644 index 000000000..5f66fe3a6 --- /dev/null +++ b/Rules/Languages/zh/overview.yaml @@ -0,0 +1,122 @@ +--- +# Provide an overview/outline/description of the math +# MathPlayer just tried to shorten things like "mfrac" by just saying "fraction" +# For mrow, it say up to 5 operands and just say "and n more things" for the rest +# This results in strings of varying length. Given human memory is about 7 words long, +# it would be better to aim for 7 words (maybe aim for a range of 6-10 words). +# Idea: +# Start by generating a terse form of the speech +# At every step when the strings are being joined +# a) if the #words <= 7, return the joined string +# b) otherwise, set verbosity to 'overview' and regenerate the expression and use that +# There is a balance that you want to maximize the info given, so 10 words is likely better then 3. +# That might mean that at the top level, we may want to allow the first few children to expand + + +- name: overview-default + tag: [mfrac, fraction] + match: "." + replace: + - test: + if: "IsNode(*[1], 'simple') and IsNode(*[2], 'simple')" + then: + - x: "*[2]" + - T: "分之" + - x: "*[1]" + else: + - T: "分数" + +- name: overview-default + tag: [msqrt, "square-root"] + match: "." + replace: + - T: "根号" + - test: + if: "IsNode(*[1], 'simple')" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - x: "*[1]" + +- name: overview-default + tag: [mroot, root] + match: "." + replace: + - T: "" + - x: "*[1]" + - test: + if: "IsNode(*[1], 'simple')" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: "的"] + - test: + if: "*[2][self::m:mn]" + then_test: + - if: "*[2][.='2']" + then: [T: "平方根"] + - else_if: "*[2][.='3']" + then: [T: "立方根"] + - else_if: "*[2][not(contains(., '.'))]" + then: [x: "*[2]", T: "次方根"] + - else: [x: "*[2]", T: "次方根"] + else: + - test: + if: "*[2][self::m:mi][string-length(.)=1]" + then: + - x: "*[2]" + #- pronounce: [text: "-th", ipa: "θ", sapi5: "th", eloquence: "T"] + else: [x: "*[2]"] + - T: "次方根" + +- name: matrix-override + tag: mrow + match: + - "*[2][self::m:mtable] and" + - "(IsBracketed(., '(', ')') or IsBracketed(., '[', ']') or IsBracketed(., '|', '|'))" + replace: + - T: "" + - x: count(*[2]/*) + - T: "乘" + - x: count(*[2]/*[self::m:mtr][1]/*) + - test: + if: "*[1][.='|']" # just need to check the first bracket since we know it must be (, [, or | + then: [T: "行列式"] + else: [T: "矩阵"] + +- name: overview-default + tag: mtable + match: "." + replace: + - T: "" + - x: count(*[2]/*) + - T: "乘" + - x: count(*[2]/*[self::m:mtr][1]/*) + - T: "表格" + +- 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: "等等" + +- include: "SimpleSpeak_Rules.yaml" diff --git a/Rules/Languages/zh/unicode-full.yaml b/Rules/Languages/zh/unicode-full.yaml new file mode 100644 index 000000000..964d79d27 --- /dev/null +++ b/Rules/Languages/zh/unicode-full.yaml @@ -0,0 +1,3802 @@ +--- + + + - "¢": [T: "分币符号"] # 0xa2 + - "£": [T: "英镑符号"] # 0xa3 + - "¤": [T: "货币符号"] # 0xa4 + - "¥": [T: "日元符号"] # 0xa5 + - "¦": [T: "断竖线"] # 0xa6 + - "§": [T: "章节号"] # 0xa7 + - "¨": [T: "分音符"] # 0xa8 + - "©": [T: "版权符号"] # 0xa9 + - "ª": [T: "阴性序数指示符"] # 0xaa + - "¬": [T: "非"] # 0xac + - "«": [T: "左尖引号"] # 0xab + - "¯": # 0xaf + - test: + if: "ancestor::m:modified-variable and preceding-sibling::*[1][self::m:mi]" + then: [T: "长音符"] + else: [T: "长音符"] + - "²": [T: "二"] # 0xb2 + - "³": [T: "三"] # 0xb3 + - "´": [T: "锐音符"] # 0xb4 + - "µ": [T: "微"] # 0xb5 + - "¹": [T: "一"] # 0xb9 + - "º": [T: "阳性序数指示符"] # 0xb9 + - "·": + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolDot = 'Auto'" + then: [T: "乘"] + else: [T: "点"] + - "×": # 0xd7 + - test: + if: "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_MultSymbolX = 'Auto'" + then: [T: "乘"] + else_test: + if: $ClearSpeak_MultSymbolX = 'By' + then: [T: "乘"] + else: [T: "叉乘"] + - "÷": [T: "除以"] # 0xf7 + - "¡": [T: "倒置感叹号"] # 0xa1 + - "¶": [T: "段落号"] # 0xb6 + - "¿": [T: "倒置问号"] # 0xbf + + - "ʰ": [T: "修饰用小写h"] # 0x2b0 + - "ʱ": [T: "带钩的修饰用小写h"] # 0x2b1 + - "ʲ": [T: "修饰用小写j"] # 0x2b2 + - "ʳ": [T: "修饰用小写r"] # 0x2b3 + - "ʴ": [T: "修饰用小写倒置r"] # 0x2b4 + - "ʵ": [T: "带钩的修饰用小写倒置r"] # 0x2b5 + - "ʶ": # 0x2b6 + - T: "修饰用倒置小写字母" + - SPELL: "translate('R', 'R', 'R')" + + - "ʷ": [T: "修饰用小写w"] # 0x2b7 + - "ʸ": [T: "修饰用小写y"] # 0x2b8 + - "ʹ": [T: "修饰用撇号"] # 0x2b9 + - "ʺ": [T: "修饰用双撇号"] # 0x2ba + - "ʻ": [T: "修饰用倒置逗号"] # 0x2bb + - "ʼ": [T: "修饰用撇号"] # 0x2bc + - "ʽ": [T: "修饰用反向逗号"] # 0x2bd + - "ʾ": [T: "修饰用右半圆环"] # 0x2be + - "ʿ": [T: "修饰用左半圆环"] # 0x2bf + - "ˀ": [T: "修饰用声门塞音"] # 0x2c0 + - "ˁ": [T: "修饰用反向声门塞音"] # 0x2c1 + - "˂": [T: "修饰字母左箭头"] # 0x2c2 + - "˃": [T: "修饰字母右箭头"] # 0x2c3 + - "˄": [T: "修饰字母上箭头"] # 0x2c4 + - "˅": [T: "修饰字母下箭头"] # 0x2c5 + - "ˆ": [T: "修饰用抑扬重音符"] # 0x2c6 + - "ˇ": [T: "倒抑扬符"] # 0x2c7 + - "ˈ": [T: "修饰用竖直线"] # 0x2c8 + - "ˉ": [T: "修饰用长音"] # 0x2c9 + - "ˊ": [T: "修饰用锐音重音符"] # 0x2ca + - "ˋ": [T: "修饰用钝音重音符"] # 0x2cb + - "ˌ": [T: "修饰用低竖直线"] # 0x2cc + - "ˍ": [T: "修饰用低长音"] # 0x2cd + - "ˎ": [T: "修饰用低钝音重音符"] # 0x2ce + - "ˏ": [T: "修饰用低锐音重音符"] # 0x2cf + - "ː": [T: "修饰用三角形冒号"] # 0x2d0 + - "ˑ": [T: "修饰用半三角形冒号"] # 0x2d1 + - "˒": [T: "修饰字母居中右半圆圈"] # 0x2d2 + - "˓": [T: "修饰字母居中左半圆圈"] # 0x2d3 + - "˔": [T: "修饰字母上钉"] # 0x2d4 + - "˕": [T: "修饰字母下钉"] # 0x2d5 + - "˖": [T: "修饰字母加号"] # 0x2d6 + - "˗": [T: "修饰字母减号"] # 0x2d7 + - "˘": [T: "短音符"] # 0x2d8 + - "˙": [T: "点"] # 0x2d9 + - "˚": [T: "上圆圈"] # 0x2da + - "˛": [T: "下尾符"] # 0x2db + - "˜": [T: "小波浪号"] # 0x2dc + - "˝": [T: "双重锐音符"] # 0x2dd + - "˞": [T: "修饰字母卷舌钩"] # 0x2de + - "˟": [T: "修饰字母十字重音"] # 0x2df + - "ˠ": [T: "修饰用小写伽马"] # 0x2e0 + - "ˡ": [T: "修饰用小写l"] # 0x2e1 + - "ˢ": [T: "修饰用小写s"] # 0x2e2 + - "ˣ": [T: "修饰用小写x"] # 0x2e3 + - "ˤ": [T: "修饰用小型反向声门塞音"] # 0x2e4 + - "˥": [T: "修饰字母特高声调横线"] # 0x2e5 + - "˦": [T: "修饰字母高声调横线"] # 0x2e6 + - "˧": [T: "修饰字母声调横线"] # 0x2e7 + - "˨": [T: "修饰字母低声调横线"] # 0x2e8 + - "˩": [T: "修饰字母特低声调横线"] # 0x2e9 + - "˪": [T: "修饰字母阴离调声调"] # 0x2ea + - "˫": [T: "修饰字母阳离调声调"] # 0x2eb + - "ˬ": [T: "修饰用浊音"] # 0x2ec + - "˭": [T: "修饰字母不送气"] # 0x2ed + - "ˮ": [T: "修饰用双撇号"] # 0x2ee + - "˯": [T: "修饰字母低下箭头"] # 0x2ef + - "˰": [T: "修饰字母低上箭头"] # 0x2f0 + - "˱": [T: "修饰字母低左箭头"] # 0x2f1 + - "˲": [T: "修饰字母低右箭头"] # 0x2f2 + - "˳": [T: "修饰字母低圆圈"] # 0x2f3 + - "˴": [T: "修饰字母中指钝重音"] # 0x2f4 + - "˵": [T: "修饰字母中指双钝重音"] # 0x2f5 + - "˶": [T: "修饰字母中指双锐重音"] # 0x2f6 + - "˷": [T: "修饰字母低波浪"] # 0x2f7 + - "˸": [T: "修饰字母上标冒号"] # 0x2f8 + - "˹": [T: "修饰字母始高声调"] # 0x2f9 + - "˺": [T: "修饰字母终高声调"] # 0x2fa + - "˻": [T: "修饰字母始低声调"] # 0x2fb + - "˼": [T: "修饰字母终低声调"] # 0x2fc + - "˽": [T: "修饰字母架"] # 0x2fd + - "˾": [T: "修饰字母开放架"] # 0x2fe + - "˿": [T: "修饰字母低左箭头"] # 0x2ff + - "̀": [T: "上加钝音符"] # 0x300 + - "́": [T: "上加锐音符"] # 0x301 + - "̂": [T: "上加抑扬符"] # 0x302 + - "̃": [T: "上加波浪号"] # 0x303 + - "̄": [T: "上加长音符"] # 0x304 + - "̅": [T: "上加横线"] # 0x305 + - "̆": [T: "短音符修饰符"] # 0x306 + - "̇": [T: "上加点"] # 0x307 + - "̈": [T: "分音修饰符"] # 0x308 + - "̉": [T: "上加钩"] # 0x309 + - "̊": [T: "上加圆环"] # 0x30a + - "̋": [T: "双锐音重音符修饰符"] # 0x30b + - "̌": [T: "倒抑扬符"] # 0x30c + - "̍": [T: "上加竖线"] # 0x30d + - "̎": [T: "上加双竖线"] # 0x30e + - "̏": [T: "双钝音重音符修饰符"] # 0x30f + - "̐": [T: "上加月点"] # 0x310 + - "̑": [T: "倒置短音修饰符"] # 0x311 + - "̒": [T: "上加倒置逗号"] # 0x312 + - "̓": [T: "上加逗号"] # 0x313 + - "̔": [T: "上加反向逗号"] # 0x314 + - "̕": [T: "右上加逗号"] # 0x315 + - "̖": [T: "下加钝音重音符"] # 0x316 + - "̗": [T: "下加锐音重音符"] # 0x317 + - "̘": [T: "下加左丁字"] # 0x318 + - "̙": [T: "下加右丁字"] # 0x319 + - "̚": [T: "上加左角"] # 0x31a + - "̛": [T: "上加角符"] # 0x31b + - "̜": [T: "下加左半圆环"] # 0x31c + - "̝": [T: "下加上丁字"] # 0x31d + - "̞": [T: "下加下丁字"] # 0x31e + - "̟": [T: "下加加号"] # 0x31f + - "̠": [T: "下加减号"] # 0x320 + - "̡": [T: "下加腭化钩"] # 0x321 + - "̢": [T: "下加卷舌钩"] # 0x322 + - "̣": [T: "下加点"] # 0x323 + - "̤": [T: "下加分音"] # 0x324 + - "̥": [T: "下加圆环"] # 0x325 + - "̦": [T: "下加逗号"] # 0x326 + - "̧": [T: "下加符修饰符"] # 0x327 + - "̨": [T: "下尾钩修饰符"] # 0x328 + - "̩": [T: "下加竖线"] # 0x329 + - "̪": [T: "下加桥形符"] # 0x32a + - "̫": [T: "下加倒置双弧"] # 0x32b + - "̬": [T: "下加倒抑扬"] # 0x32c + - "̭": [T: "下加抑扬重音符"] # 0x32d + - "̮": [T: "下加短音符"] # 0x32e + - "̯": [T: "下加倒置短音"] # 0x32f + - "̰": [T: "下加波浪号"] # 0x330 + - "̱": [T: "下加长音"] # 0x331 + - "̲": [T: "低线修饰符"] # 0x332 + - "̳": [T: "双低线修饰符"] # 0x333 + - "̴": [T: "叠加波浪号"] # 0x334 + - "̵": [T: "叠加短笔画"] # 0x335 + - "̶": [T: "叠加长笔画"] # 0x336 + - "̷": [T: "叠加短斜杠"] # 0x337 + - "̸": [T: "叠加长斜杠"] # 0x338 + - "̹": [T: "下加右半圆环"] # 0x339 + - "̺": [T: "下加倒置桥形符"] # 0x33a + - "̻": [T: "下加方形"] # 0x33b + - "̼": [T: "下加海鸥形"] # 0x33c + - "̽": [T: "上加x"] # 0x33d + - "̾": [T: "竖直波浪号修饰符"] # 0x33e + - "̿": [T: "双上划线修饰符"] # 0x33f + - "̀": [T: "钝音声调标记修饰符"] # 0x340 + - "́": [T: "锐音声调标记修饰符"] # 0x341 + - "͆": [T: "上加桥形符"] # 0x346 + + - "ΪΫϏ": # 0x3aa, 0x3ab, 0x3cf + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [SPELL: "translate('.', 'ΪΫϏ', 'ιυϗ')"] + - T: "带分音符" + - "ϊ": [T: "带分音符的约塔"] # 0x3ca + - "ϋ": [T: "带分音符的宇普西隆"] # 0x3cb + - "ό": [T: "带重音符的奥密克戎"] # 0x3cc + - "ύ": [T: "带重音符的宇普西隆"] # 0x3cd + - "ώ": [T: "带重音符的欧米伽"] # 0x3ce + - "ϐ": [T: "贝塔"] # 0x3d0 + - "ϑ": [T: "西塔"] # 0x3d1 + - "ϒ": [T: "带钩的宇普西隆"] # 0x3d2 + - "ϓ": [T: "带锐音与钩的宇普西隆"] # 0x3d3 + - "ϔ": [T: "带分音与钩的宇普西隆"] # 0x3d4 + - "ϕ": [T: "斐"] # 0x3d5 + - "ϖ": [T: "派"] # 0x3d6 + - "ϗ": [T: "凯"] # 0x3d7 + - "Ϙ": [T: "大写古科帕"] # 0x3d8 + - "ϙ": [T: "古科帕"] # 0x3d9 + - "ϵ": [T: "艾普西隆"] # 0x3f5 + - "϶": [T: "反向艾普西隆"] # 0x3f6 + - "А-Я": # 0x410 - 0x42f + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [SPELL: "translate('.', 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ', 'абвгдежзийклмнопрстуфхцчшщъыьэюя')"] + - "а": [T: ""] # 0x430 + - "б": [T: "西里尔字母贝"] # 0x431 + - "в": [T: "西里尔字母韦"] # 0x432 + - "г": [T: "西里尔字母格"] # 0x433 + - "д": [T: "西里尔字母德"] # 0x434 + - "е": [T: "西里尔字母耶"] # 0x435 + - "ж": [T: "西里尔字母热"] # 0x436 + - "з": [T: "西里尔字母泽"] # 0x437 + - "и": [T: "西里尔字母伊"] # 0x438 + - "й": [T: "西里尔字母短伊"] # 0x439 + - "к": [T: "西里尔字母卡"] # 0x43a + - "л": [T: "西里尔字母埃尔"] # 0x43b + - "м": [T: "西里尔字母埃姆"] # 0x43c + - "н": [T: "西里尔字母恩"] # 0x43d + - "о": [T: "西里尔字母奥"] # 0x43e + - "п": [T: "西里尔字母佩"] # 0x43f + - "р": [T: "西里尔字母埃尔"] # 0x440 + - "с": [T: "西里尔字母埃斯"] # 0x441 + - "т": [T: "西里尔字母特"] # 0x442 + - "у": [T: "西里尔字母乌"] # 0x443 + - "ф": [T: "西里尔字母埃弗"] # 0x444 + - "х": [T: "西里尔字母哈"] # 0x445 + - "ц": [T: "西里尔字母采"] # 0x446 + - "ч": [T: "西里尔字母切"] # 0x447 + - "ш": [T: "西里尔字母沙"] # 0x448 + - "щ": [T: "西里尔字母夏"] # 0x449 + - "ъ": [T: "西里尔字母硬音符"] # 0x44a + - "ы": [T: "西里尔字母厄"] # 0x44b + - "ь": [T: "西里尔字母软音符"] # 0x44c + - "э": [T: "西里尔字母埃"] # 0x44d + - "ю": [T: "西里尔字母尤"] # 0x44e + - "я": [T: "西里尔字母雅"] # 0x44f + - "؆": [T: "阿拉伯文印度数字立方根"] # 0x606 + - "؇": [T: "阿拉伯文印度数字根"] # 0x607 + - "؈": [T: "阿拉伯文射线"] # 0x608 + - "‐": [T: "连字符"] # 0x2010 + - "‑": [T: "不换行连字符"] # 0x2011 + - "‒": [T: "数字破折号"] # 0x2012 + - "–": [T: "短破折号"] # 0x2013 + - "—": [T: "长破折号"] # 0x2014 + - "―": [T: "水平条"] # 0x2015 + - "‖": [T: "双竖线"] # 0x2016 + - "†": [T: "剑号"] # 0x2020 + - "‡": [T: "双剑号"] # 0x2021 + + - " - ": [T: " "] # 0x2000 - 0x2007 + + - "•": # 0x2022 + - test: + if: "@data-chem-formula-op" + then: [T: "点"] + else: [T: "项目符号"] + + - "…": # 0x2026 + test: + if: + - "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_Ellipses = 'Auto' or" + # must be ClearSpeak and $ClearSpeak_Ellipses = 'AndSoOn' + # speak '…' as 'and so on...' unless expr starts with '…' + - "../*[1][.='…']" + then: [T: "省略号"] + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [T: "等等"] + else: [T: "依此类推,直到"] + + - "‰": [T: "千分号"] # 0x2030 + - "‱": [T: "万分号"] # 0x2031 + - "′": [T: "撇号"] # 0x2032 + - "″": [T: "双撇号"] # 0x2033 + - "‴": [T: "三撇号"] # 0x2034 + - "‵": [T: "反撇号"] # 0x2035 + - "‶": [T: "反向双撇号"] # 0x2036 + - "‷": [T: "反向三撇号"] # 0x2037 + - "‸": [T: "脱字符"] # 0x2038 + - "‹": [T: "左单尖引号"] # 0x2039 + - "›": [T: "右单尖引号"] # 0x203a + - "‼": # 0x203c + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "双感叹号"] # 0x203c + else: [T: "双感叹号"] # 0x203c + - "⁄": # 0x2044 + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "分数斜杠"] # 0x203c + else: [T: "除以"] # 0x203c + - "⁅": [T: "左花方括号"] # 0x2045 + - "⁆": [T: "右花方括号"] # 0x2046 + - "※": [T: "参考标记"] # 0x203b + - "‿": [T: "下连线"] # 0x203F + - "⁀": [T: "字符连结符"] # 0x2040 + - "⁎": [T: "低星号"] # 0x204e + - "⁏": [T: "反向分号"] # 0x204f + - "⁐": [T: "闭合上"] # 0x2050 + - "⁑": [T: "二星号"] # 0x2051 + - "⁒": [T: "商业负号"] # 0x2052 + - "⁗": [T: "四撇号"] # 0x2057 + - "⁠": [T: ""] # 0x2060 + - "⁰": [T: "零次方"] # 0x2070 + - "ⁱ": [T: "i次方"] # 0x2071 + - "⁴": [T: "四次方"] # 0x2074 + - "⁵": [T: "五次方"] # 0x2075 + - "⁶": [T: "六次方"] # 0x2076 + - "⁷": [T: "七次方"] # 0x2077 + - "⁸": [T: "八次方"] # 0x2078 + - "⁹": [T: "九次方"] # 0x2079 + - "⁺": [T: "上标加号"] # 0x207a + - "⁻": [T: "上标减号"] # 0x207b + - "⁼": [T: "上标等号"] # 0x207c + - "⁽": [T: "上标左圆括号"] # 0x207d + - "⁾": [T: "上标右圆括号"] # 0x207e + - "ⁿ": [T: "n次方"] # 0x207f + - "₀": [T: "下标零"] # 0x2080 + - "₁": [T: "下标一"] # 0x2081 + - "₂": [T: "下标二"] # 0x2082 + - "₃": [T: "下标三"] # 0x2083 + - "₄": [T: "下标四"] # 0x2084 + - "₅": [T: "下标五"] # 0x2085 + - "₆": [T: "下标六"] # 0x2086 + - "₇": [T: "下标七"] # 0x2087 + - "₈": [T: "下标八"] # 0x2088 + - "₉": [T: "下标九"] # 0x2089 + - "₊": [T: "下标加号"] # 0x208a + - "₋": [T: "下标减号"] # 0x208b + - "₌": [T: "下标等号"] # 0x208c + - "₍": [T: "下标左圆括号"] # 0x208d + - "₎": [T: "下标右圆括号"] # 0x208e + - "ₐ": [T: "下标a"] # 0x2090 + - "ₑ": [T: "下标e"] # 0x2091 + - "ₒ": [T: "下标o"] # 0x2092 + - "ₓ": [T: "下标x"] # 0x2093 + - "ₕ": [T: "下标h"] # 0x2095 + - "ₖ": [T: "下标k"] # 0x2096 + - "ₗ": [T: "下标l"] # 0x2097 + - "ₘ": [T: "下标m"] # 0x2098 + - "ₙ": [T: "下标n"] # 0x2099 + - "ₚ": [T: "下标p"] # 0x209a + - "ₛ": [T: "下标s"] # 0x209b + - "ₜ": [T: "下标t"] # 0x209c + - "₠": [T: "欧元货币符号"] # 0x20a0 + - "₡": [T: "科朗符号"] # 0x20a1 + - "₢": [T: "克鲁赛罗符号"] # 0x20a2 + - "₣": [T: "法国法郎符号"] # 0x20a3 + - "₤": [T: "里拉符号"] # 0x20a4 + - "₥": [T: "密尔"] # 0x20a5 + - "₦": [T: "奈拉符号"] # 0x20a6 + - "₧": [T: "比塞塔符号"] # 0x20a7 + - "₨": [T: "卢比符号"] # 0x20a8 + - "₩": [T: "韩元符号"] # 0x20a9 + - "₪": [T: "新谢克尔符号"] # 0x20aa + - "₫": [T: "越南盾符号"] # 0x20ab + - "€": [T: "欧元符号"] # 0x20ac + - "₭": [T: "基普符号"] # 0x20ad + - "₮": [T: "图格里克符号"] # 0x20ae + - "₯": [T: "德拉克马符号"] # 0x20af + - "₰": [T: "德国便士符号"] # 0x20b0 + - "₱": [T: "比索符号"] # 0x20b1 + - "₲": [T: "瓜拉尼符号"] # 0x20b2 + - "₳": [T: "澳大利亚"] # 0x20b3 + - "₴": [T: "格里夫纳符号"] # 0x20b4 + - "₵": [T: "塞地符号"] # 0x20b5 + - "₶": [T: "里弗尔图尔努瓦"] # 0x20b6 + - "₷": [T: "斯佩斯米洛"] # 0x20b7 + - "₸": [T: "坦格符号"] # 0x20b8 + - "₹": [T: "印度卢比符号"] # 0x20b9 + - "₺": [T: "土耳其里拉符号"] # 0x20ba + - "⃐": [T: "上加左鱼叉箭头"] # 0x20d0 + - "⃑": [T: "上加右鱼叉箭头"] # 0x20d1 + - "⃒": [T: "叠加长竖直线"] # 0x20d2 + - "⃓": [T: "叠加短竖直线"] # 0x20d3 + - "⃔": [T: "上加逆时针箭头"] # 0x20d4 + - "⃕": [T: "上加顺时针箭头"] # 0x20d5 + - "⃖": [T: "上加向左箭头"] # 0x20d6 + - "⃗": [T: "上加向右箭头"] # 0x20d7 + - "⃘": [T: "叠加圆环"] # 0x20d8 + - "⃙": [T: "叠加顺时针圆环"] # 0x20d9 + - "⃚": [T: "叠加逆时针圆环"] # 0x20da + - "⃛": [T: "上加三点"] # 0x20db + - "⃜": [T: "上加四点"] # 0x20dc + - "⃝": [T: "外加圆圈"] # 0x20dd + - "⃞": [T: "外加方形"] # 0x20de + - "⃟": [T: "外加菱形"] # 0x20df + - "⃠": [T: "外加圆圈反斜杠"] # 0x20e0 + - "⃡": [T: "上加左右箭头"] # 0x20e1 + - "⃢": [T: "外加方框"] # 0x20e2 + - "⃣": [T: "外加键帽"] # 0x20e3 + - "⃤": [T: "外加向上指向三角形"] # 0x20e4 + - "⃥": [T: "叠加反向斜杠"] # 0x20e5 + - "⃦": [T: "叠加双竖线"] # 0x20e6 + - "⃧": [T: "精算符号"] # 0x20e7 + - "⃨": [T: "下加三点"] # 0x20e8 + - "⃩": [T: "上加宽桥形符"] # 0x20e9 + - "⃪": [T: "叠加向左箭头"] # 0x20ea + - "⃫": [T: "叠加长双斜杠"] # 0x20eb + - "⃬": [T: "倒钩向下的向右鱼叉箭头修饰符"] # 0x20ec + - "⃭": [T: "倒钩向下的向左鱼叉箭头修饰符"] # 0x20ed + - "⃮": [T: "下加向左箭头"] # 0x20ee + - "⃯": [T: "下加向右箭头"] # 0x20ef + - "⃰": [T: "上加星号"] # 0x20f0 + - "℄": [T: "中心线"] # 0x2104 + - "℅": [T: "代收"] # 0x2105 + - "℆": [T: "每(库拉)"] # 0x2106 + - "ℇ": [T: "欧拉常数"] # 0x2107 + - "℈": [T: "斯克鲁普尔"] # 0x2108 + - "℉": [T: "华氏度"] # 0x2109 + - "ℊ": [T: "花体g"] # 0x210a + - "ℌℑℨℭ": # 0x210c, 0x2111, 0x2128, 0x212d + - T: "哥特体" + - SPELL: "translate('.', 'ℌℑℨℭ', 'HIZC')" + + - "ℍℙℾℿ": # 0x210d, 0x2119, 0x213e, 0x213f + - T: "双线体" + - SPELL: "translate('.', 'ℍℙℾℿ', 'HPΓΠ')" + + - "ℎ": [T: "普朗克常数"] # 0x210e + - "ℏ": [T: "约化普朗克常数"] # 0x210f + + - "ℐℒ℘ℬℰℱℳ": # 0x2110, 0x2112, 0x2118, 0x2130, 0x2131, 0x2133 + - T: "花体" + - SPELL: "translate('.', 'ℐℒ℘ℬℰℱℳ', 'ILPBEFM')" + + - "ℓ": [T: "花体l"] # 0x2113 + - "℔": [T: "横线"] # 0x2114 + - "№": [T: "井号"] # 0x2116 + - "℥": [T: "盎司"] # 0x2125 + - "Ω": [T: "欧姆"] # 0x2126 + - "℧": [T: "倒"] # 0x2127 + - "℩": [T: "倒置希腊小写字母"] # 0x2129 + - "K": [T: "开尔文"] # 0x212a + - "Å": [T: "埃"] # 0x212b + - "ℯ": [T: "花体e"] # 0x212f + + # coalesced some chars that use cap letters + - "Ⅎ℺⅁⅂⅃⅄": # 0x2132, 0x213a, 0x2141, 0x2142, 0x2143, 0x2144 + - test: + - if: "'.' = '℺'" + then: [T: "旋转"] + - else_if: "'.' = 'Ⅎ'" + then: [T: "倒置"] + - else_if: "'.' = '⅃'" + then: [T: "反向无衬线体"] + else: [T: "倒置无衬线体"] + - SPELL: "translate('.', 'Ⅎ℺⅁⅂⅃⅄', 'FQGLLY')" + + - "ℴ": [T: "花体o"] # 0x2134 + - "ℵ": [T: "第一超限基数"] # 0x2135 + - "ℶ": [T: "第二超限基数"] # 0x2136 + - "ℷ": [T: "第三超限基数"] # 0x2137 + - "ℸ": [T: "第四超限基数"] # 0x2138 + - "ℼ": [T: "双线体派"] # 0x213c + - "ℽ": [T: "双线体伽马"] # 0x213d + - "⅀": [T: "双线和号(求和)"] # 0x2140 + - "⅋": [T: "倒置与号"] # 0x214b + - "⅌": [T: "每"] # 0x214c + - "ⅎ": [T: "倒置小写 f"] # 0x214e + - "¼": [T: "四分之一"] # 0x00bc + - "½": [T: "二分之一"] # 0x00bd + - "¾": [T: "四分之三"] # 0x00be + - "⅐": [T: "七分之一"] # 0x2150 + - "⅑": [T: "九分之一"] # 0x2151 + - "⅒": [T: "十分之一"] # 0x2152 + - "⅓": [T: "三分之一"] # 0x2153 + - "⅔": [T: "三分之二"] # 0x2154 + - "⅕": [T: "五分之一"] # 0x2155 + - "⅖": [T: "五分之二"] # 0x2156 + - "⅗": [T: "五分之三"] # 0x2157 + - "⅘": [T: "五分之四"] # 0x2158 + - "⅙": [T: "六分之一"] # 0x2159 + - "⅚": [T: "六分之五"] # 0x215a + - "⅛": [T: "八分之一"] # 0x215b + - "⅜": [T: "八分之三"] # 0x215c + - "⅝": [T: "八分之五"] # 0x215d + - "⅞": [T: "八分之七"] # 0x215e + - "⅟": [T: "分子一"] # 0x215f + - "Ⅰ": [T: "罗马数字一"] # 0x2160 + - "Ⅱ": [T: "罗马数字二"] # 0x2161 + - "Ⅲ": [T: "罗马数字三"] # 0x2162 + - "Ⅳ": [T: "罗马数字四"] # 0x2163 + - "Ⅴ": [T: "罗马数字五"] # 0x2164 + - "Ⅵ": [T: "罗马数字六"] # 0x2165 + - "Ⅶ": [T: "罗马数字七"] # 0x2166 + - "Ⅷ": [T: "罗马数字八"] # 0x2167 + - "Ⅸ": [T: "罗马数字九"] # 0x2168 + - "Ⅹ": [T: "罗马数字十"] # 0x2169 + - "Ⅺ": [T: "罗马数字十一"] # 0x216a + - "Ⅻ": [T: "罗马数字十二"] # 0x216b + - "Ⅼ": [T: "罗马数字五十"] # 0x216c + - "Ⅽ": [T: "罗马数字一百"] # 0x216d + - "Ⅾ": [T: "罗马数字五百"] # 0x216e + - "Ⅿ": [T: "罗马数字一千"] # 0x216f + - "ⅰ": [T: "罗马数字一"] # 0x2170 + - "ⅱ": [T: "罗马数字二"] # 0x2171 + - "ⅲ": [T: "罗马数字三"] # 0x2172 + - "ⅳ": [T: "罗马数字四"] # 0x2173 + - "ⅴ": [T: "罗马数字五"] # 0x2174 + - "ⅵ": [T: "罗马数字六"] # 0x2175 + - "ⅶ": [T: "罗马数字七"] # 0x2176 + - "ⅷ": [T: "罗马数字八"] # 0x2177 + - "ⅸ": [T: "罗马数字九"] # 0x2178 + - "ⅹ": [T: "罗马数字十"] # 0x2179 + - "ⅺ": [T: "罗马数字十一"] # 0x217a + - "ⅻ": [T: "罗马数字十二"] # 0x217b + - "ⅼ": [T: "罗马数字五十"] # 0x217c + - "ⅽ": [T: "罗马数字一百"] # 0x217d + - "ⅾ": [T: "罗马数字五百"] # 0x217e + - "ⅿ": [T: "罗马数字一千"] # 0x217f + - "↉": [T: "三分之零"] # 0x2189 + - "↔": [T: "左右箭头"] # 0x2194 + - "↕": [T: "上下箭头"] # 0x2195 + - "↖": [T: "左上箭头"] # 0x2196 + - "↗": # 0x2197 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "右上箭头"] + else: [T: "右上箭头"] + + - "↘": # 0x2198 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "右下箭头"] + else: [T: "右下箭头"] + + - "↙": [T: "左下箭头"] # 0x2199 + - "↚": [T: "向左带斜线箭头"] # 0x219a + - "↛": [T: "向右带斜线箭头"] # 0x219b + - "↜": [T: "向左波浪箭头"] # 0x219c + - "↝": [T: "向右波浪箭头"] # 0x219d + - "↞": [T: "向左双向箭头"] # 0x219e + - "↟": [T: "向上双向箭头"] # 0x219f + - "↠": [T: "向右双向箭头"] # 0x21a0 + - "↡": [T: "向下双向箭头"] # 0x21a1 + - "↢": [T: "向左带竖线箭头"] # 0x21a2 + - "↣": [T: "向右带竖线箭头"] # 0x21a3 + - "↤": [T: "向左带尾箭头"] # 0x21a4 + - "↥": [T: "向上带尾箭头"] # 0x21a5 + - "↦": [T: "向右带尾箭头"] # 0x21a6 + - "↧": [T: "向下带尾箭头"] # 0x21a7 + - "↨": [T: "上下箭头(带基线)"] # 0x21a8 + - "↩": [T: "向左带钩箭头"] # 0x21a9 + - "↪": [T: "向右带钩箭头"] # 0x21aa + - "↫": [T: "向左带钩圈箭头"] # 0x21ab + - "↬": [T: "向右带钩圈箭头"] # 0x21ac + - "↭": [T: "左右双向箭头"] # 0x21ad + - "↮": [T: "左右带斜线双向箭头"] # 0x21ae + - "↯": [T: "向下之字形箭头"] # 0x21af + - "↰": [T: "向上带尖角箭头"] # 0x21b0 + - "↱": [T: "向上带尖角箭头(右)"] # 0x21b1 + - "↲": [T: "向下带尖角箭头"] # 0x21b2 + - "↳": [T: "向下带尖角箭头(右)"] # 0x21b3 + - "↴": [T: "向右箭头角向下"] # 0x21b4 + - "↵": [T: "回车符号"] # 0x21b5 + - "↶": [T: "逆时针上半圆箭头"] # 0x21b6 + - "↷": [T: "顺时针上半圆箭头"] # 0x21b7 + - "↸": [T: "指向长横线的左上箭头"] # 0x21b8 + - "↹": [T: "向左箭头横线向右箭头横线"] # 0x21b9 + - "↺": [T: "逆时针开放圆箭头"] # 0x21ba + - "↻": [T: "顺时针开放圆箭头"] # 0x21bb + - "↼": [T: "向左上半镖箭头"] # 0x21bc + - "↽": [T: "倒钩向下的向左鱼叉箭头"] # 0x21bd + - "↾": [T: "向上右侧镖箭头"] # 0x21be + - "↿": [T: "向上左侧镖箭头"] # 0x21bf + - "⇀": [T: "向右上半镖箭头"] # 0x21c0 + - "⇁": [T: "倒钩向下的向右鱼叉箭头"] # 0x21c1 + - "⇂": [T: "向下右侧镖箭头"] # 0x21c2 + - "⇃": [T: "向下左侧镖箭头"] # 0x21c3 + - "⇄": [T: "向右向上箭头(互换)"] # 0x21c4 + - "⇅": [T: "向上向右箭头"] # 0x21c5 + - "⇆": [T: "向左向下箭头(互换)"] # 0x21c6 + - "⇇": [T: "向左双平行箭头"] # 0x21c7 + - "⇈": [T: "向上双平行箭头"] # 0x21c8 + - "⇉": [T: "向右双平行箭头"] # 0x21c9 + - "⇊": [T: "向下双平行箭头"] # 0x21ca + - "⇋": [T: "向左向右互推箭头"] # 0x21cb + - "⇌": [T: "向右向左互推箭头"] # 0x21cc + - "⇍": [T: "向左双线带斜线箭头"] # 0x21cd + - "⇎": [T: "左右双线带斜线箭头"] # 0x21ce + - "⇏": [T: "向右双线带斜线箭头"] # 0x21cf + - "⇐": [T: "向左双线箭头"] # 0x21d0 + - "⇑": [T: "向上双线箭头"] # 0x21d1 + - "⇒": [T: "蕴含"] # 0x21d2 + - "⇓": [T: "向下双线箭头"] # 0x21d3 + - "⇔": [T: "当且仅当"] # 0x21d4 + - "⇕": [T: "上下双线箭头"] # 0x21d5 + - "⇖": [T: "左上双线箭头"] # 0x21d6 + - "⇗": [T: "右上双线箭头"] # 0x21d7 + - "⇘": [T: "右下双线箭头"] # 0x21d8 + - "⇙": [T: "左下双线箭头"] # 0x21d9 + - "⇚": [T: "向左三重箭头"] # 0x21da + - "⇛": [T: "向右三重箭头"] # 0x21db + - "⇜": [T: "向左箭头"] # 0x21dc + - "⇝": [T: "向右箭头"] # 0x21dd + - "⇞": [T: "向上箭头双笔画"] # 0x21de + - "⇟": [T: "向下箭头双笔画"] # 0x21df + - "⇠": [T: "向左虚线箭头"] # 0x21e0 + - "⇡": [T: "向上虚线箭头"] # 0x21e1 + - "⇢": [T: "向右虚线箭头"] # 0x21e2 + - "⇣": [T: "向下虚线箭头"] # 0x21e3 + - "⇤": [T: "向左到栏箭头"] # 0x21e4 + - "⇥": [T: "向右到栏箭头"] # 0x21e5 + - "⇦": [T: "向左白色箭头"] # 0x21e6 + - "⇧": [T: "向上白色箭头"] # 0x21e7 + - "⇨": [T: "向右白色箭头"] # 0x21e8 + - "⇩": [T: "向下白色箭头"] # 0x21e9 + - "⇪": [T: "向上白色双线箭头"] # 0x21ea + - "⇫": [T: "向上白色箭头基座"] # 0x21eb + - "⇬": [T: "向上白色箭头基座水平横线"] # 0x21ec + - "⇭": [T: "向上白色箭头基座垂直横线"] # 0x21ed + - "⇮": [T: "向上白双线箭头"] # 0x21ee + - "⇯": [T: "向上白双线箭头基座"] # 0x21ef + - "⇰": [T: "向右白色箭头墙"] # 0x21f0 + - "⇱": [T: "指向角部的左上箭头"] # 0x21f1 + - "⇲": [T: "指向角部的右下箭头"] # 0x21f2 + - "⇳": [T: "上下白色箭头"] # 0x21f3 + - "⇴": [T: "右箭头小写圆"] # 0x21f4 + - "⇵": [T: "上箭头左侧的下箭头"] # 0x21f5 + - "⇶": [T: "三向右箭头"] # 0x21f6 + - "⇷": [T: "向左箭头垂直笔画"] # 0x21f7 + - "⇸": [T: "向右箭头垂直笔画"] # 0x21f8 + - "⇹": [T: "左右箭头垂直笔画"] # 0x21f9 + - "⇺": [T: "向左箭头双垂直笔画"] # 0x21fa + - "⇻": [T: "向右箭头双垂直笔画"] # 0x21fb + - "⇼": [T: "左右箭头双垂直笔画"] # 0x21fc + - "⇽": [T: "向左开放朝向箭头"] # 0x21fd + - "⇾": [T: "向右开放朝向箭头"] # 0x21fe + - "⇿": [T: "左右开放朝向箭头"] # 0x21ff + - "∀": [T: "全部"] # 0x2200 + - "∁": # 0x2201 + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "补集" + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [T: "偏微分"] + else: [T: "偏导数"] + - "∃": [T: "存在"] # 0x2203 + - "∄": [T: "不存在"] # 0x2204 + - "∅": [T: "空集合"] # 0x2205 + - "∆": # 0x2206 + - test: + if: "following-sibling::*" + then_test: + if: "$Verbosity!='Terse'" + then: [T: "拉普拉斯算子"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested + else: [T: "拉普拉斯算子"] + else: [T: "拉普拉斯算子"] + + - "∇": [T: "纳布拉"] # 0x2207 + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: [T: "不属于"] + # Several options for speaking elements in ClearSpeak -- they split between being inside a set or not and then the option + else_test: + if: "../../self::m:set or ../../../self::m:set" # inside a set + then_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "不属于"] + - else: [T: "不属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "不属于"] + - else: [T: "不属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "∊": # 0x220a + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: + - test: + if: "$Verbosity!='Terse' and not(parent::m:set)" # "the set x is an element of ..." sounds bad + then: [T: ""] + - T: "属于" + # Several options for speaking elements in ClearSpeak -- they split between being inside a set or not and then the option + else_test: + if: "../../self::m:set or ../../../self::m:set" # inside a set + then_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "∋": [T: "包含元素"] # 0x220b + - "∌": [T: "不包含元素"] # 0x220c + - "∍": [T: "包含元素"] # 0x220d + - "∎": [T: "证毕"] # 0x220e + - "∏": [T: "连乘"] # 0x220f + - "∐": [T: "余积"] # 0x2210 + - "∑": [T: "求和"] # 0x2211 + - "−": [T: "减"] # 0x2212 + - "∓": [T: "减加号"] # 0x2213 + - "∔": [T: "点加号"] # 0x2214 + - "∕": [T: "除以"] # 0x2215 + - "∖": [T: "集合减"] # 0x2216 + - "∗": [T: "乘"] # 0x2217 + - "∘": [T: "复合"] # 0x2218 + - "∙": # 0x2219 + - test: + if: "@data-chem-formula-op" + then: [T: "点"] + else: [T: "乘"] + + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "平方根" + - "∛": # 0x221b + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "立方根" + - "∜": # 0x221c + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "四次方根" + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "正比于" + - "∞": [T: "无穷大"] # 0x221e + - "∟": [T: "直角"] # 0x221f + - "∠": [T: "角"] # 0x2220 + - "∡": [T: "测量角"] # 0x2221 + - "∢": [T: "球面角"] # 0x2222 + - "∣": [T: "整除"] # 0x2223 + - "∤": [T: "不整除"] # 0x2224 + - "∧": [T: "逻辑与"] # 0x2227 + - "∨": [T: "逻辑或"] # 0x2228 + - "∩": [T: "交"] # 0x2229 + - "∪": [T: "并"] # 0x222a + - "∫": [T: "积分"] # 0x222b + - "∬": [T: "二重积分"] # 0x222c + - "∭": [T: "三重积分"] # 0x222d + - "∮": [T: "围道积分"] # 0x222e + - "∯": [T: "面积分"] # 0x222f + - "∰": [T: "体积分"] # 0x2230 + - "∱": [T: "顺时针环路积分"] # 0x2231 + - "∲": [T: "顺时针围道积分"] # 0x2232 + - "∳": [T: "逆时针围道积分"] # 0x2233 + - "∴": [T: "所以"] # 0x2234 + - "∵": [T: "因为"] # 0x2235 + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "比" + - "∷": [T: "比例"] # 0x2237 + - "∸": [T: "点减号"] # 0x2238 + - "∹": [T: "超出"] # 0x2239 + - "∺": # 0x223a + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "几何正比于" + - "∻": # 0x223b + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "位似于" + - "∼": [T: "随之变化"] # 0x223c + - "∽": [T: "反向波浪号"] # 0x223d + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "最正值" + - "∿": [T: "正弦波"] # 0x223f + - "≀": [T: "圈积"] # 0x2240 + - "≁": [T: "非波浪号"] # 0x2241 + - "≂": [T: "减波浪号"] # 0x2242 + - "≃": # 0x2243 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "渐近相等" + - "≄": # 0x2244 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "非渐近等" + - "≅": # 0x2245 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "全等于" + - "≆": # 0x2246 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "近似但不等" + - "≇": # 0x2247 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不全等于" + - "≈": # 0x2248 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "约等于" + - "≉": # 0x2249 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不约等于" + - "≊": # 0x224a + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "约等于或等于" + - "≋": [T: "三重波浪"] # 0x224b + - "≌": [T: "均等于"] # 0x224c + - "≍": # 0x224d + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等价于" + - "≎": # 0x224e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "几何等价于" + - "≏": # 0x224f + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "差" + - "≐": [T: "趋于极限"] # 0x2250 + - "≑": # 0x2251 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "几何上等于" + - "≒": # 0x2252 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "约等于或为像" + - "≓": # 0x2253 + - test: + if: "$Verbosity!='Terse'" + then: [T: "图像约等于"] + - T: "为像或约等于" + - "≔": [T: "定义为"] # 0x2254 + - "≕": [T: "等号冒号"] # 0x2255 + - "≖": [T: "带圆圈的等号"] # 0x2256 + - "≗": # 0x2257 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "环等于(同余)" + - "≘": [T: "对应"] # 0x2258 + - "≙": [T: "估计"] # 0x2259 + - "≚": # 0x225a + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等角" + - "≛": [T: "星等号"] # 0x225b + - "≜": [T: "三角等号"] # 0x225c + - "≝": [T: "定义为"] # 0x225d + - "≞": # 0x225e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "度量为" + - "≟": [T: "关系未知"] # 0x225f + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不等于" + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "恒等于" + - "≢": # 0x2262 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不恒等于" + - "≣": # 0x2263 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "严格等价于" + - "≦": [T: "小于号上方的等号"] # 0x2266 + - "≧": [T: "大于号上方的等号"] # 0x2267 + - "≨": # 0x2268 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于但不等于" + - "≩": # 0x2269 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于但不等于" + - "≪": # 0x226a + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "远小于" + - "≫": # 0x226b + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "远大于" + - "≬": # 0x226c + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "介于" + - "≭": # 0x226d + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "非等价" + - "≮": # 0x226e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不小于" + - "≯": # 0x226f + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不大于" + - "≰": # 0x2270 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不小于也不等于" + - "≱": # 0x2271 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不大于也不等于" + - "≲": # 0x2272 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于或等价于" + - "≳": # 0x2273 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于或等价于" + - "≴": # 0x2274 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不小于也不等价于" + - "≵": # 0x2275 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不大于也不等价于" + - "≶": # 0x2276 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于或大于" + - "≷": # 0x2277 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于或小于" + - "≸": # 0x2278 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不小于也不大于" + - "≹": # 0x2279 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不大于也不小于" + - "≺": [T: "先于"] # 0x227a + - "≻": [T: "后于"] # 0x227b + - "≼": [T: "先于或等于"] # 0x227c + - "≽": [T: "后于或等于"] # 0x227d + - "≾": [T: "先于或等价于"] # 0x227e + - "≿": [T: "后于或等价于"] # 0x227f + - "⊀": [T: "不先于"] # 0x2280 + - "⊁": [T: "不后于"] # 0x2281 + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "真包含于" + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "真包含" + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不真包含于" + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不真包含" + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "包含于" + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "包含" + - "⊈": # 0x2288 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不包含于" + - "⊉": # 0x2289 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不包含" + - "⊊": [T: "真包含于"] # 0x228a + - "⊋": [T: "真包含"] # 0x228b + - "⊌": [T: "多重集"] # 0x228c + - "⊍": [T: "多重集乘号"] # 0x228d + - "⊎": [T: "多重集并"] # 0x228e + - "⊏": [T: "方形像"] # 0x228f + - "⊐": [T: "方形原像"] # 0x2290 + - "⊑": [T: "方形像或等于"] # 0x2291 + - "⊒": [T: "方形原像或等于"] # 0x2292 + - "⊓": [T: "方形交集"] # 0x2293 + - "⊔": [T: "方形并集"] # 0x2294 + - "⊕": [T: "带圈加号"] # 0x2295 + - "⊖": [T: "带圈减号"] # 0x2296 + - "⊗": [T: "带圈乘号"] # 0x2297 + - "⊘": [T: "带圈除号"] # 0x2298 + - "⊙": [T: "带圈点运算符"] # 0x2299 + - "⊚": [T: "带圈环运算符"] # 0x229a + - "⊛": [T: "带圈星号运算符"] # 0x229b + - "⊜": [T: "带圈等号"] # 0x229c + - "⊝": [T: "带圈短横"] # 0x229d + - "⊞": [T: "方框加号"] # 0x229e + - "⊟": [T: "带方框减号"] # 0x229f + - "⊠": [T: "带方框乘号"] # 0x22a0 + - "⊡": [T: "带方框点运算符"] # 0x22a1 + - "⊢": [T: "可推出"] # 0x22a2 + - "⊣": [T: "不能推出"] # 0x22a3 + - "⊤": [T: "真"] # 0x22a4 + - "⊦": [T: "可归约为"] # 0x22a6 + - "⊧": [T: "满足"] # 0x22a7 + - "⊨": # 0x22a8 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "真(断定)" + - "⊩": [T: "迫使"] # 0x22a9 + - "⊪": [T: "三重竖直线右断言符"] # 0x22aa + - "⊫": [T: "双竖直线双右断言符"] # 0x22ab + - "⊬": [T: "不能证明"] # 0x22ac + - "⊭": # 0x22ad + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不成立" + - "⊮": [T: "不迫使"] # 0x22ae + - "⊯": [T: "否定双竖直线双右断言符"] # 0x22af + - "⊰": [T: "先于关系"] # 0x22b0 + - "⊱": [T: "后于关系"] # 0x22b1 + - "⊲": # 0x22b2 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "正规包含于" + - "⊳": [T: "包含正规子群"] # 0x22b3 + - "⊴": # 0x22b4 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "正规包含于或等于" + - "⊵": [T: "包含正规子群或等于"] # 0x22b5 + - "⊶": # 0x22b6 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "原像" + - "⊷": # 0x22b7 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "像" + - "⊸": [T: "多重映射"] # 0x22b8 + - "⊹": [T: "厄米共轭矩阵"] # 0x22b9 + - "⊺": [T: "交错"] # 0x22ba + - "⊻": [T: "异或"] # 0x22bb + - "⊼": [T: "与非"] # 0x22bc + - "⊽": [T: "或非"] # 0x22bd + - "⊾": [T: "右角弧"] # 0x22be + - "⊿": [T: "右三角"] # 0x22bf + - "⋀": [T: "n 元逻辑与"] # 0x22c0 + - "⋁": [T: "n 元逻辑或"] # 0x22c1 + - "⋂": [T: "n 元交集"] # 0x22c2 + - "⋃": [T: "n 元并集"] # 0x22c3 + - "⋄": [T: "菱形运算符"] # 0x22c4 + - "⋅": # 0x22c5 + - test: + if: "@data-chem-formula-op" + then: [T: "点"] + else: [T: "乘"] + + - "⋆": [T: "乘"] # 0x22c6 + - "⋇": [T: "除号乘号"] # 0x22c7 + - "⋈": [T: "蝴蝶结"] # 0x22c8 + - "⋉": # 0x22c9 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "左正规因子半直积" + - "⋊": # 0x22ca + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "右正规因子半直积" + - "⋋": # 0x22cb + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "左半直积" + - "⋌": # 0x22cc + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "右半直积" + - "⋍": [T: "反向波浪等号"] # 0x22cd + - "⋎": [T: "花体逻辑或"] # 0x22ce + - "⋏": [T: "花体逻辑与"] # 0x22cf + - "⋐": # 0x22d0 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "双子集" + - "⋑": # 0x22d1 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "双超集" + - "⋒": # 0x22d2 + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "双交集" + - "⋓": # 0x22d3 + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "双并集" + - "⋔": # 0x22d4 + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "横截于" + - "⋕": # 0x22d5 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等于且平行于" + - "⋖": [T: "带点的小于"] # 0x22d6 + - "⋗": [T: "带点的大于"] # 0x22d7 + - "⋘": # 0x22d8 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "极远小于" + - "⋙": # 0x22d9 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "极远大于" + - "⋚": # 0x22da + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于、等于或大于" + - "⋛": # 0x22db + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于、等于或小于" + - "⋜": # 0x22dc + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等号或小于" + - "⋝": # 0x22dd + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等于或大于" + - "⋞": # 0x22de + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等于或先于" + - "⋟": # 0x22df + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "等于或后于" + - "⋠": [T: "既不先于也不等于"] # 0x22e0 + - "⋡": [T: "既不后于也不等于"] # 0x22e1 + - "⋢": [T: "非方图像等"] # 0x22e2 + - "⋣": [T: "非方原像等"] # 0x22e3 + - "⋤": [T: "方图像非等"] # 0x22e4 + - "⋥": [T: "方原像非等"] # 0x22e5 + - "⋦": # 0x22e6 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于但不等价于" + - "⋧": # 0x22e7 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于但不等价于" + - "⋨": [T: "先于但不等价于"] # 0x22e8 + - "⋩": [T: "后于但不等价于"] # 0x22e9 + - "⋪": # 0x22ea + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不是正规子群" + - "⋫": [T: "不包含正规子群"] # 0x22eb + - "⋬": # 0x22ec + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "既不是正规子群也不等于" + - "⋭": [T: "既不包含正规子群也不等于"] # 0x22ed + - "⋮": [T: "竖省略号"] # 0x22ee + - "⋯": [T: "省略号"] # 0x22ef + - "⋰": [T: "右上斜省略号"] # 0x22f0 + - "⋱": [T: "右下斜省略号"] # 0x22f1 + - "⋲": [T: "元素长水平笔画"] # 0x22f2 + - "⋳": [T: "元素垂直横线终水平笔画"] # 0x22f3 + - "⋴": [T: "小写元素垂直横线终水平笔画"] # 0x22f4 + - "⋵": [T: "上方带点的属于号"] # 0x22f5 + - "⋶": [T: "元素上横线"] # 0x22f6 + - "⋷": [T: "小写元素上横线"] # 0x22f7 + - "⋸": [T: "元素下横线"] # 0x22f8 + - "⋹": [T: "元素二水平笔画"] # 0x22f9 + - "⋺": [T: "包含长水平笔画"] # 0x22fa + - "⋻": [T: "包含垂直横线终水平笔画"] # 0x22fb + - "⋼": [T: "小写包含垂直横线终水平笔画"] # 0x22fc + - "⋽": [T: "包含上横线"] # 0x22fd + - "⋾": [T: "小写包含上横线"] # 0x22fe + - "⋿": [T: "记号集合袋隶属"] # 0x22ff + - "⌀": [T: "直径"] # 0x2300 + - "⌁": [T: "电箭头"] # 0x2301 + - "⌂": [T: "房屋"] # 0x2302 + - "⌃": [T: "控制键"] # 0x2303 + - "⌄": [T: "下箭头"] # 0x2304 + - "⌅": [T: "射影"] # 0x2305 + - "⌆": [T: "透视"] # 0x2306 + - "⌇": [T: "波浪线"] # 0x2307 + - "⌈": [T: "左上取整符号"] # 0x2308 + - "⌉": [T: "右上取整符号"] # 0x2309 + - "⌊": [T: "左下取整符号"] # 0x230a + - "⌋": [T: "右下取整符号"] # 0x230b + - "⌌": [T: "底右"] # 0x230c + - "⌍": [T: "底左"] # 0x230d + - "⌎": [T: "顶右"] # 0x230e + - "⌏": [T: "顶左"] # 0x230f + - "⌐": [T: "反向非"] # 0x2310 + - "⌑": [T: "方菱形"] # 0x2311 + - "⌒": [T: "弧"] # 0x2312 + - "⌓": [T: "线段"] # 0x2313 + - "⌔": [T: "扇区"] # 0x2314 + - "⌕": [T: "电话"] # 0x2315 + - "⌖": [T: "位置指示器"] # 0x2316 + - "⌗": [T: "视图数据方框"] # 0x2317 + - "⌘": [T: "命令键"] # 0x2318 + - "⌙": [T: "倒置非"] # 0x2319 + - "⌚": [T: "手表"] # 0x231a + - "⌛": [T: "沙漏"] # 0x231b + - "⌜": [T: "顶左角"] # 0x231c + - "⌝": [T: "顶右角"] # 0x231d + - "⌞": [T: "底左角"] # 0x231e + - "⌟": [T: "底右角"] # 0x231f + - "⌠": [T: "顶半积分"] # 0x2320 + - "⌡": [T: "底半积分"] # 0x2321 + - "⌢": [T: "皱眉"] # 0x2322 + - "⌣": [T: "微笑"] # 0x2323 + - "⌤": [T: "上箭头二水平横线"] # 0x2324 + - "⌥": [T: "选项键"] # 0x2325 + - "⌦": [T: "向前删除键"] # 0x2326 + - "⌧": [T: "矩形方框"] # 0x2327 + - "⌨": [T: "键盘"] # 0x2328 + - "〈": [T: "左指角括号"] # 0x2329 + - "〉": [T: "右指角括号"] # 0x232a + - "⌫": [T: "退格键"] # 0x232b + - "⌬": [T: "苯环"] # 0x232c + - "⌭": [T: "圆柱度"] # 0x232d + - "⌮": [T: "全部轮廓"] # 0x232e + - "⌯": [T: "对称"] # 0x232f + - "⌰": [T: "总跳动"] # 0x2330 + - "⌱": [T: "尺寸原点"] # 0x2331 + - "⌲": [T: "圆锥锥度"] # 0x2332 + - "⌳": [T: "斜率"] # 0x2333 + - "⌴": [T: "沉孔"] # 0x2334 + - "⌵": [T: "沉头孔"] # 0x2335 + - "⌶": [T: "APL 语言功能符号横梁"] # 0x2336 + - "⌽": [T: "APL 语言功能符号圆竖线"] # 0x233d + - "⌿": [T: "APL 语言功能符号斜杠横线"] # 0x233f + - "⍰": [T: "APL 语言功能符号方块问号"] # 0x2370 + - "⍼": [T: "右角向下之字形箭头"] # 0x237c + - "⎔": [T: "六边形"] # 0x2394 + - "⎕": [T: "APL 语言功能符号方块"] # 0x2395 + - "⎶": [T: "底方括号顶方括号"] # 0x23b6 + - "⏜": [T: "顶圆括号"] # 0x23dc + - "⏝": [T: "底圆括号"] # 0x23dd + - "⏞": [T: "顶卷曲括号"] # 0x23de + - "⏟": [T: "底卷曲括号"] # 0x23df + - "⏠": [T: "顶贝壳括号"] # 0x23e0 + - "⏡": [T: "底贝壳括号"] # 0x23e1 + - "⏢": [T: "白色梯形"] # 0x23e2 + - "⏣": [T: "带圆圈的苯环右部"] # 0x23e3 + - "⏤": [T: "直线度"] # 0x23e4 + - "⏥": [T: "平面度"] # 0x23e5 + - "⏦": # 0x23e6 + - SPELL: "ac" + - T: "交流电流" + - "⏧": [T: "电路交叉点"] # 0x23e7 + - "①-⑨": # 0x2460 - 0x2469 + - T: "带圈" + - SPELL: "translate('.', '①②③④⑤⑥⑦⑧⑨', '123456789')" + - "⑩": [T: "带圈数字十"] # 0x2469 + - "⑪": [T: "带圈数字十一"] # 0x246a + - "⑫": [T: "带圈数字十二"] # 0x246b + - "⑬": [T: "带圈数字十三"] # 0x246c + - "⑭": [T: "带圈数字十四"] # 0x246d + - "⑮": [T: "带圈数字十五"] # 0x246e + - "⑯": [T: "带圈数字十六"] # 0x246f + - "⑰": [T: "带圈数字十七"] # 0x2470 + - "⑱": [T: "带圈数字十八"] # 0x2471 + - "⑳": [T: "带圈数字二十"] # 0x2473 + - "⑴-⑼": # 0x2474 - 0x247d + - T: "带括号" + - SPELL: "translate('.', '⑴⑵⑶⑷⑸⑹⑺⑻⑼', '123456789')" + - "⑽": [T: "带括号数字十"] # 0x247d + - "⑾": [T: "带括号数字十一"] # 0x247e + - "⑿": [T: "带括号数字十二"] # 0x247f + - "⒀": [T: "带括号数字十三"] # 0x2480 + - "⒁": [T: "带括号数字十四"] # 0x2481 + - "⒂": [T: "带括号数字十五"] # 0x2482 + - "⒃": [T: "带括号数字十六"] # 0x2483 + - "⒄": [T: "带括号数字十七"] # 0x2484 + - "⒅": [T: "带括号数字十八"] # 0x2485 + - "⒆": [T: "带括号数字十九"] # 0x2486 + - "⒇": [T: "带括号数字二十"] # 0x2487 + - "⒈-⒐": # 0x2488 - 0x2491 + - T: "带句点数字" + - SPELL: "translate('.', '⒈⒉⒊⒋⒌⒍⒎⒏⒐', '123456789')" + - "⒑": [T: "带句点数字十"] # 0x2491 + - "⒒": [T: "带句点数字十一"] # 0x2492 + - "⒓": [T: "带句点数字十二"] # 0x2493 + - "⒔": [T: "带句点数字十三"] # 0x2494 + - "⒕": [T: "带句点数字十四"] # 0x2495 + - "⒖": [T: "带句点数字十五"] # 0x2496 + - "⒗": [T: "带句点数字十六"] # 0x2497 + - "⒘": [T: "带句点数字十七"] # 0x2498 + - "⒙": [T: "带句点数字十八"] # 0x2499 + - "⒚": [T: "带句点数字十九"] # 0x249a + - "⒛": [T: "带句点数字二十"] # 0x249b + - "⒜-⒵": # 0x249c - 0x24b5 + - T: "带括号" + - SPELL: "translate('.', '⒜⒝⒞⒟⒠⒡⒢⒣⒤⒥⒦⒧⒨⒩⒪⒫⒬⒭⒮⒯⒰⒱⒲⒳⒴⒵', 'abcdefghijklmnopqrstuvwxyz')" + + - "Ⓐ-ⓩ": # 0x24b6 - 0x24cf + - T: "带圈" + - SPELL: "translate('.', 'ⒶⒷⒸⒹⒺⒻⒼⒽⒾⒿⓀⓁⓂⓃⓄⓅⓆⓇⓈⓉⓊⓋⓌⓍⓎⓏ', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "🅐-🅩": # 0x1f150 - 0x1f169 + - T: "黑色带圈" + - SPELL: "translate('.', '🅐🅑🅒🅓🅔🅕🅖🅗🅘🅙🅚🅛🅜🅝🅞🅟🅠🅡🅢🅣🅤🅥🅦🅧🅨🅩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "ⓐ-ⓩ": # 0x24d0 - 0x24e9 + - T: "带圈" + - SPELL: "translate('.', 'ⓐⓑⓒⓓⓔⓕⓖⓗⓘⓙⓚⓛⓜⓝⓞⓟⓠⓡⓢⓣⓤⓥⓦⓧⓨⓩ', 'abcdefghijklmnopqrstuvwxyz')" + - "⓪": [T: "带圈数字零"] # 0x24ea + - "⓫": [T: "带圈反白数字十一"] # 0x24eb + - "⓬": [T: "带圈反白数字十二"] # 0x24ec + - "⓭": [T: "带圈反白数字十三"] # 0x24ed + - "⓮": [T: "带圈反白数字十四"] # 0x24ee + - "⓯": [T: "带圈反白数字十五"] # 0x24ef + - "⓰": [T: "带圈反白数字十六"] # 0x24f0 + - "⓱": [T: "带圈反白数字十七"] # 0x24f1 + - "⓲": [T: "带圈反白数字十八"] # 0x24f2 + - "⓳": [T: "带圈反白数字十九"] # 0x24f3 + - "⓴": [T: "带圈反白数字二十"] # 0x24f4 + - "⓵-⓽": # 0x24f5 - 0x24fe + - T: "双带圈" + - SPELL: "translate('.', '⓵⓶⓷⓸⓹⓺⓻⓼⓽', '123456789')" + - "⓾": [T: "双圈数字十"] # 0x24fe + - "⓿": [T: "带圈反白数字零"] # 0x24ff + - "■": [T: "黑方"] # 0x25a0 + - "□": [T: "白方"] # 0x25a1 + - "▢": [T: "白色带边方块"] # 0x25a2 + - "▣": [T: "白色内嵌方块"] # 0x25a3 + - "▤": [T: "白色水平线方块"] # 0x25a4 + - "▥": [T: "白色垂直线方块"] # 0x25a5 + - "▦": [T: "白色交叉线方块"] # 0x25a6 + - "▧": [T: "白色对角线方块"] # 0x25a7 + - "▨": [T: "白色反对角线方块"] # 0x25a8 + - "▩": [T: "白色交叉对角线方块"] # 0x25a9 + - "▪": [T: "黑色小方块"] # 0x25aa + - "▫": [T: "白色小方块"] # 0x25ab + - "▬": [T: "黑色矩形"] # 0x25ac + - "▭": [T: "白色矩形"] # 0x25ad + - "▮": [T: "黑色竖矩形"] # 0x25ae + - "▯": [T: "白色竖矩形"] # 0x25af + - "▰": [T: "黑色平行四边形"] # 0x25b0 + - "▱": [T: "白色平行四边形"] # 0x25b1 + - "▲": [T: "黑上指三角"] # 0x25b2 + - "△": [T: "白上指三角"] # 0x25b3 + - "▴": [T: "黑色上三角(小)"] # 0x25b4 + - "▵": [T: "白色上三角(小)"] # 0x25b5 + - "▶": [T: "黑色右三角"] # 0x25b6 + - "▷": [T: "白色右三角"] # 0x25b7 + - "▸": [T: "黑色右三角(小)"] # 0x25b8 + - "▹": [T: "白色右三角(小)"] # 0x25b9 + - "►": [T: "黑色右指针"] # 0x25ba + - "▻": [T: "白色右指针"] # 0x25bb + - "▼": [T: "黑色下三角"] # 0x25bc + - "▽": [T: "白色下三角"] # 0x25bd + - "▾": [T: "黑色下三角(小)"] # 0x25be + - "▿": [T: "白色下三角(小)"] # 0x25bf + - "◀": [T: "黑色左三角"] # 0x25c0 + - "◁": [T: "白色左三角"] # 0x25c1 + - "◂": [T: "黑色左三角(小)"] # 0x25c2 + - "◃": [T: "白色左三角(小)"] # 0x25c3 + - "◄": [T: "黑色左指针"] # 0x25c4 + - "◅": [T: "白色左指针"] # 0x25c5 + - "◆": [T: "黑菱"] # 0x25c6 + - "◇": [T: "白色菱形"] # 0x25c7 + - "◈": [T: "白色内嵌菱形"] # 0x25c8 + - "◉": [T: "鱼眼"] # 0x25c9 + - "◊": [T: "菱形(长)"] # 0x25ca + - "○": [T: "白色圆"] # 0x25cb + - "◌": [T: "带虚线圆"] # 0x25cc + - "◍": [T: "大圆"] # 0x25cd + - "◎": [T: "靶心"] # 0x25ce + - "●": [T: "黑色圆"] # 0x25cf + - "◐": [T: "圆左半黑"] # 0x25d0 + - "◑": [T: "圆右半黑"] # 0x25d1 + - "◒": [T: "圆下半黑"] # 0x25d2 + - "◓": [T: "圆上半黑"] # 0x25d3 + - "◔": [T: "圆带上半黑"] # 0x25d4 + - "◕": [T: "圆带下半黑"] # 0x25d5 + - "◖": [T: "左半黑圆"] # 0x25d6 + - "◗": [T: "右半黑圆"] # 0x25d7 + - "◘": [T: "反圆(黑中白)"] # 0x25d8 + - "◙": [T: "上半反圆"] # 0x25d9 + - "◚": [T: "下半反圆"] # 0x25da + - "◛": [T: "右上反圆"] # 0x25db + - "◜": [T: "左上四分之一圆"] # 0x25dc + - "◝": [T: "右上四分之一圆"] # 0x25dd + - "◞": [T: "右下四分之一圆"] # 0x25de + - "◟": [T: "左下四分之一圆"] # 0x25df + - "◠": [T: "上半圆"] # 0x25e0 + - "◡": [T: "下半圆"] # 0x25e1 + - "◢": [T: "黑下右三角"] # 0x25e2 + - "◣": [T: "黑下左三角"] # 0x25e3 + - "◤": [T: "黑上左三角"] # 0x25e4 + - "◥": [T: "黑上右三角"] # 0x25e5 + - "◦": [T: "复合运算符"] # 0x25e6 + - "◧": [T: "方左半黑"] # 0x25e7 + - "◨": [T: "方右半黑"] # 0x25e8 + - "◩": [T: "方上左对角半黑"] # 0x25e9 + - "◪": [T: "方下右对角半黑"] # 0x25ea + - "◫": [T: "白方垂直线"] # 0x25eb + - "◬": [T: "白上指三角点"] # 0x25ec + - "◭": [T: "上指三角左半黑"] # 0x25ed + - "◮": [T: "上指三角右半黑"] # 0x25ee + - "◯": [T: "大圆"] # 0x25ef + - "◰": [T: "白方上左象限"] # 0x25f0 + - "◱": [T: "白方下左象限"] # 0x25f1 + - "◲": [T: "白方下右象限"] # 0x25f2 + - "◳": [T: "白方上右象限"] # 0x25f3 + - "◴": [T: "白圆上左象限"] # 0x25f4 + - "◵": [T: "白圆下左象限"] # 0x25f5 + - "◶": [T: "白圆下右象限"] # 0x25f6 + - "◷": [T: "白圆上右象限"] # 0x25f7 + - "◸": [T: "上左三角"] # 0x25f8 + - "◹": [T: "上右三角"] # 0x25f9 + - "◺": [T: "下左三角"] # 0x25fa + - "◻": [T: "白中方"] # 0x25fb + - "◼": [T: "黑中方"] # 0x25fc + - "◽": [T: "白中小写方"] # 0x25fd + - "◾": [T: "黑中小写方"] # 0x25fe + - "◿": [T: "下右三角"] # 0x25ff + - "★": [T: "黑星"] # 0x2605 + - "☆": [T: "白星"] # 0x2606 + - "☉": [T: "行星(太阳)"] # 0x2609 + - "☌": [T: "火星(会合)"] # 0x260c + - "☒": [T: "带叉方框"] # 0x2612 + - "☽": [T: "上弦月"] # 0x263d + - "☾": [T: "下弦月"] # 0x263e + - "☿": [T: "水星"] # 0x263f + - "♀": [T: "女性符号"] # 0x2640 + - "♁": [T: "地球"] # 0x2641 + - "♂": [T: "男性符号"] # 0x2642 + - "♃": [T: "木星"] # 0x2643 + - "♄": [T: "土星"] # 0x2644 + - "♅": [T: "天王星"] # 0x2645 + - "♆": [T: "海王星"] # 0x2646 + - "♇": [T: "冥王星"] # 0x2647 + - "♈": [T: "白羊座"] # 0x2648 + - "♉": [T: "金牛座"] # 0x2649 + - "♩": [T: "四分音符"] # 0x2669 + - "♭": [T: "降号"] # 0x266d + - "♮": [T: "还原号"] # 0x266e + - "♯": [T: "升号"] # 0x266f + - "♠": [T: "黑桃"] # 0x2660 + - "♡": [T: "红桃"] # 0x2661 + - "♢": [T: "方块"] # 0x2662 + - "♣": [T: "梅花"] # 0x2663 + - "♤": [T: "白桃"] # 0x2664 + - "♥": [T: "红心"] # 0x2665 + - "♦": [T: "黑方块(菱形)"] # 0x2666 + - "♧": [T: "白梅花"] # 0x2667 + - "⚀": [T: "骰子一(空心)"] # 0x2680 + - "⚁": [T: "骰子二"] # 0x2681 + - "⚂": [T: "骰子三"] # 0x2682 + - "⚃": [T: "骰子四"] # 0x2683 + - "⚄": [T: "骰子五"] # 0x2684 + - "⚅": [T: "骰子六"] # 0x2685 + - "⚆": [T: "黑色骰子一"] # 0x2686 + - "⚇": [T: "黑色骰子二"] # 0x2687 + - "⚈": [T: "黑色骰子三"] # 0x2688 + - "⚉": [T: "黑色骰子四"] # 0x2689 + - "⚪": [T: "中白圆"] # 0x26aa + - "⚫": [T: "中黑圆"] # 0x26ab + - "⚬": [T: "中白菱形"] # 0x26ac + - "⚲": [T: "无性别符号"] # 0x26b2 + - "✓": [T: "勾号"] # 0x2713 + - "✠": [T: "马耳他十字"] # 0x2720 + - "✪": [T: "带圈白星"] # 0x272a + - "✶": [T: "六角星号"] # 0x2736 + - "❨": [T: "轻左花括号"] # 0x2768 + - "❩": [T: "轻右花括号"] # 0x2769 + - "❪": [T: "中左花括号"] # 0x276a + - "❫": [T: "中右花括号"] # 0x276b + - "❬": [T: "旋转花括号钩"] # 0x276c + - "❭": [T: "镜像花括号钩"] # 0x276d + - "❮": [T: "重型左指角引号"] # 0x276e + - "❯": [T: "重型右指角引号"] # 0x276f + - "❰": [T: "重型左指龟壳引号"] # 0x2770 + - "❱": [T: "重型右指龟壳引号"] # 0x2771 + - "❲": [T: "轻左龟壳引号"] # 0x2772 + - "❳": [T: "轻右龟壳引号"] # 0x2773 + - "❴": [T: "中左龟壳引号"] # 0x2774 + - "❵": [T: "中右龟壳引号"] # 0x2775 + - "❶": [T: "带圈反白数字一"] # 0x2776 + - "❷": [T: "带圈反白数字二"] # 0x2777 + - "❸": [T: "带圈反白数字三"] # 0x2778 + - "❹": [T: "带圈反白数字四"] # 0x2779 + - "❺": [T: "带圈反白数字五"] # 0x277a + - "❻": [T: "带圈反白数字六"] # 0x277b + - "❼": [T: "带圈反白数字七"] # 0x277c + - "❽": [T: "带圈反白数字八"] # 0x277d + - "❾": [T: "带圈反白数字九"] # 0x277e + - "❿": [T: "带圈反白数字十"] # 0x277f + - "➀": [T: "无衬线带圈数字一"] # 0x2780 + - "➁": [T: "无衬线带圈数字二"] # 0x2781 + - "➂": [T: "无衬线带圈数字三"] # 0x2782 + - "➃": [T: "无衬线带圈数字四"] # 0x2783 + - "➄": [T: "无衬线带圈数字五"] # 0x2784 + - "➅": [T: "无衬线带圈数字六"] # 0x2785 + - "➆": [T: "无衬线带圈数字七"] # 0x2786 + - "➇": [T: "无衬线带圈数字八"] # 0x2787 + - "➈": [T: "无衬线带圈数字九"] # 0x2788 + - "➉": [T: "无衬线带圈数字十"] # 0x2789 + - "➊": [T: "无衬线带圈反白数字一"] # 0x278a + - "➋": [T: "无衬线带圈反白数字二"] # 0x278b + - "➌": [T: "无衬线带圈反白数字三"] # 0x278c + - "➍": [T: "无衬线带圈反白数字四"] # 0x278d + - "➎": [T: "无衬线带圈反白数字五"] # 0x278e + - "➏": [T: "无衬线带圈反白数字六"] # 0x278f + - "➐": [T: "无衬线带圈反白数字七"] # 0x2790 + - "➑": [T: "无衬线带圈反白数字八"] # 0x2791 + - "➒": [T: "无衬线带圈反白数字九"] # 0x2792 + - "➓": [T: "无衬线带圈反白数字十"] # 0x2793 + - "➔": [T: "粗向右箭头"] # 0x2794 + - "➕": [T: "粗加号"] # 0x2795 + - "➖": [T: "粗减号"] # 0x2796 + - "➗": [T: "粗除号"] # 0x2797 + - "➘": [T: "粗落右箭头"] # 0x2798 + - "➙": [T: "粗圆头右箭头"] # 0x2799 + - "➚": [T: "粗等长右箭头"] # 0x279a + - "➛": [T: "右箭头带尾"] # 0x279b + - "➜": [T: "重型圆角右箭头"] # 0x279c + - "➝": [T: "开放右箭头"] # 0x279d + - "➞": [T: "重型凸角右箭头"] # 0x279e + - "➟": [T: "右虚线箭头"] # 0x279f + - "➠": [T: "重型右凸角箭头"] # 0x27a0 + - "➡": [T: "粗黑右箭头"] # 0x27a1 + - "➢": [T: "三锥凸右箭头"] # 0x27a2 + - "➣": [T: "圆角凸右箭头"] # 0x27a3 + - "➤": [T: "黑色右箭头"] # 0x27a4 + - "➥": [T: "重型圆角凸右箭头"] # 0x27a5 + - "➦": [T: "圆形凸右箭头"] # 0x27a6 + - "➧": [T: "重型圆形凸右箭头"] # 0x27a7 + - "➨": [T: "粗凹尖黑色向右箭头"] # 0x27a8 + - "➩": [T: "右箭头阴影"] # 0x27a9 + - "➪": [T: "左缩右箭头"] # 0x27aa + - "➫": [T: "上缩右箭头"] # 0x27ab + - "➬": [T: "右上凸箭头"] # 0x27ac + - "➭": [T: "右斜角箭头"] # 0x27ad + - "➮": [T: "右圆角斜角箭头"] # 0x27ae + - "➯": [T: "上钩右箭头"] # 0x27af + - "➱": [T: "上钩右下箭头"] # 0x27b1 + - "➲": [T: "带圈粗白右箭头"] # 0x27b2 + - "➳": [T: "右上羽箭头"] # 0x27b3 + - "➴": [T: "右下羽箭头"] # 0x27b4 + - "➵": [T: "黑羽右上箭头"] # 0x27b5 + - "➶": [T: "黑羽右下箭头"] # 0x27b6 + - "➷": [T: "黑环右箭头"] # 0x27b7 + - "➸": [T: "粗黑右箭头(开放)"] # 0x27b8 + - "➹": [T: "右到栏箭头"] # 0x27b9 + - "➺": [T: "右从栏箭头"] # 0x27ba + - "➻": [T: "右从栏双箭头"] # 0x27bb + - "➼": [T: "右箭头带尾(开口)"] # 0x27bc + - "➽": [T: "粗楔尾向右箭头"] # 0x27bd + - "➾": [T: "打开右箭头"] # 0x27be + - "⟀": [T: "三维角"] # 0x27c0 + - "⟁": [T: "白三角包含小写白三角"] # 0x27c1 + - "⟂⊥": # 0x27c2, 22A5 (22A5 wrongly used for perpendicular by most editors) + - test: + - if: "$SpeechStyle = 'LiteralSpeak' and text()='⊥'" # 22A5 (up tack/bottom) + then: [T: "上丁字"] + - else: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "垂直于" + - "⟃": # 0x27c3 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "开放子集" + - "⟄": # 0x27c4 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "开放超集" + - "⟅": [T: "左集合袋"] # 0x27c5 + - "⟆": [T: "右集合袋"] # 0x27c6 + - "⟇": [T: "内含点的逻辑或"] # 0x27c7 + - "⟈": [T: "反向斜杠子集"] # 0x27c8 + - "⟉": [T: "超集斜杠"] # 0x27c9 + - "⟊": [T: "带水平笔画的竖线"] # 0x27ca + - "⟋": [T: "数学上升对角线"] # 0x27cb + - "⟌": [T: "长除号"] # 0x27cc + - "⟍": [T: "数学下降对角线"] # 0x27cd + - "⟎": [T: "带方框的逻辑与"] # 0x27ce + - "⟏": [T: "带方框的逻辑或"] # 0x27cf + - "⟐": [T: "白菱居中点"] # 0x27d0 + - "⟑": [T: "带点的逻辑与"] # 0x27d1 + - "⟒": [T: "元素打开向上"] # 0x27d2 + - "⟓": [T: "下右角点"] # 0x27d3 + - "⟔": [T: "上左角点"] # 0x27d4 + - "⟕": [T: "左外连接"] # 0x27d5 + - "⟖": [T: "右外连接"] # 0x27d6 + - "⟗": [T: "全外连接"] # 0x27d7 + - "⟘": [T: "大上丁字"] # 0x27d8 + - "⟙": [T: "大下丁字"] # 0x27d9 + - "⟚": [T: "左与右双断言符"] # 0x27da + - "⟛": [T: "左右丁字"] # 0x27db + - "⟜": [T: "左多重映射"] # 0x27dc + - "⟝": [T: "长右丁字"] # 0x27dd + - "⟞": [T: "长左丁字"] # 0x27de + - "⟟": [T: "上方带圆圈的上丁字"] # 0x27df + - "⟠": [T: "菱形水平规则"] # 0x27e0 + - "⟡": [T: "白菱"] # 0x27e1 + - "⟢": [T: "白菱向左"] # 0x27e2 + - "⟣": [T: "白菱向右"] # 0x27e3 + - "⟤": [T: "白方向左"] # 0x27e4 + - "⟥": [T: "白方向右"] # 0x27e5 + - "⟦": [T: "数学左白方括号"] # 0x27e6 + - "⟧": [T: "数学右白方括号"] # 0x27e7 + - "⟨": [T: "数学左角括号"] # 0x27e8 + - "⟩": [T: "数学右角括号"] # 0x27e9 + - "⟪": [T: "数学左双角括号"] # 0x27ea + - "⟫": [T: "数学右双角括号"] # 0x27eb + - "⟬": [T: "数学左白贝壳括号"] # 0x27ec + - "⟭": [T: "数学右白贝壳括号"] # 0x27ed + - "⟮": [T: "数学左圆括号"] # 0x27ee + - "⟯": [T: "数学右圆括号"] # 0x27ef + - "⟰": [T: "向上四重箭头"] # 0x27f0 + - "⟱": [T: "向下四重箭头"] # 0x27f1 + - "⟲": [T: "逆时针圆箭头"] # 0x27f2 + - "⟳": [T: "顺时针圆箭头"] # 0x27f3 + - "⟴": [T: "右箭头带圈加号"] # 0x27f4 + - "⟵": [T: "长向左箭头"] # 0x27f5 + - "⟶": [T: "长向右箭头"] # 0x27f6 + - "⟷": [T: "长左右箭头"] # 0x27f7 + - "⟸": [T: "长向左双线箭头"] # 0x27f8 + - "⟹": [T: "蕴含"] # 0x27f9 + - "⟺": [T: "当且仅当"] # 0x27fa + - "⟻": [T: "长左带尾箭头"] # 0x27fb + - "⟼": [T: "长右带尾箭头"] # 0x27fc + - "⟽": [T: "长向左双线带尾箭头"] # 0x27fd + - "⟾": [T: "长向右双线带尾箭头"] # 0x27fe + - "⟿": [T: "长向右波浪箭头"] # 0x27ff + - "⤀": [T: "右双向箭头"] # 0x2900 + - "⤁": [T: "右带竖线箭头"] # 0x2901 + - "⤂": [T: "右双线带竖线箭头"] # 0x2902 + - "⤃": [T: "左双向箭头"] # 0x2903 + - "⤄": [T: "左右双向箭头"] # 0x2904 + - "⤅": [T: "右双向带尾箭头"] # 0x2905 + - "⤆": [T: "左双线双向箭头"] # 0x2906 + - "⤇": [T: "右双线双向箭头"] # 0x2907 + - "⤈": [T: "向下带尾箭头"] # 0x2908 + - "⤉": [T: "向上带尾箭头"] # 0x2909 + - "⤊": [T: "向上三重箭头"] # 0x290a + - "⤋": [T: "向下三重箭头"] # 0x290b + - "⤌": [T: "向左三联箭头"] # 0x290c + - "⤍": [T: "向右三联箭头"] # 0x290d + - "⤎": [T: "向左三联波浪箭头"] # 0x290e + - "⤏": [T: "向右三联波浪箭头"] # 0x290f + - "⤐": [T: "右双向到栏箭头"] # 0x2910 + - "⤑": [T: "右带尾三联箭头"] # 0x2911 + - "⤒": [T: "指向横线的向上箭头"] # 0x2912 + - "⤓": [T: "指向横线的向下箭头"] # 0x2913 + - "⤔": [T: "向右带圆点箭头"] # 0x2914 + - "⤕": [T: "向右带加号箭头"] # 0x2915 + - "⤖": [T: "向右带乘号箭头"] # 0x2916 + - "⤗": [T: "向右二朝向箭头尾垂直笔画"] # 0x2917 + - "⤘": [T: "向右二朝向箭头尾双垂直笔画"] # 0x2918 + - "⤙": [T: "向左箭头尾"] # 0x2919 + - "⤚": [T: "向右箭头尾"] # 0x291a + - "⤛": [T: "向左双线箭头尾"] # 0x291b + - "⤜": [T: "向右双线箭头尾"] # 0x291c + - "⤝": [T: "向左箭头黑菱"] # 0x291d + - "⤞": [T: "向右箭头黑菱"] # 0x291e + - "⤟": [T: "向左箭头横线黑菱"] # 0x291f + - "⤠": [T: "向右箭头横线黑菱"] # 0x2920 + - "⤡": [T: "左上与右下箭头"] # 0x2921 + - "⤢": [T: "右上与左下箭头"] # 0x2922 + - "⤣": [T: "带钩的左上箭头"] # 0x2923 + - "⤤": [T: "带钩的右上箭头"] # 0x2924 + - "⤥": [T: "带钩的右下箭头"] # 0x2925 + - "⤦": [T: "带钩的左下箭头"] # 0x2926 + - "⤧": [T: "左上箭头与右上箭头"] # 0x2927 + - "⤨": [T: "右上箭头与右下箭头"] # 0x2928 + - "⤩": [T: "右下箭头与左下箭头"] # 0x2929 + - "⤪": [T: "左下箭头与左上箭头"] # 0x292a + - "⤫": [T: "对角交叉对角"] # 0x292b + - "⤬": [T: "对角交叉对角"] # 0x292c + - "⤭": [T: "穿过右上箭头的右下箭头"] # 0x292d + - "⤮": [T: "穿过右下箭头的右上箭头"] # 0x292e + - "⤯": [T: "对角交叉北东箭头"] # 0x292f + - "⤰": [T: "穿过右下箭头的上升对角线"] # 0x2930 + - "⤱": [T: "穿过左上箭头的右上箭头"] # 0x2931 + - "⤲": [T: "穿过右上箭头的左上箭头"] # 0x2932 + - "⤳": [T: "挥手箭头指右"] # 0x2933 + - "⤴": [T: "箭头指向右向上"] # 0x2934 + - "⤵": [T: "箭头指向右向下"] # 0x2935 + - "⤶": [T: "箭头指向下向左"] # 0x2936 + - "⤷": [T: "箭头指向下向右"] # 0x2937 + - "⤸": [T: "右侧弧顺时针箭头"] # 0x2938 + - "⤹": [T: "左侧弧逆时针箭头"] # 0x2939 + - "⤺": [T: "顶弧逆时针箭头"] # 0x293a + - "⤻": [T: "底弧逆时针箭头"] # 0x293b + - "⤼": [T: "顶弧顺时针箭头减号"] # 0x293c + - "⤽": [T: "顶弧逆时针箭头加号"] # 0x293d + - "⤾": [T: "下右顺时针箭头"] # 0x293e + - "⤿": [T: "下左逆时针箭头"] # 0x293f + - "⥀": [T: "逆时针闭合圆箭头"] # 0x2940 + - "⥁": [T: "顺时针闭合圆箭头"] # 0x2941 + - "⥂": [T: "向右箭头上短向左箭头"] # 0x2942 + - "⥃": [T: "向左箭头上短向右箭头"] # 0x2943 + - "⥄": [T: "短向右箭头上向左箭头"] # 0x2944 + - "⥅": [T: "向右箭头加号下"] # 0x2945 + - "⥆": [T: "向左箭头加号下"] # 0x2946 + - "⥇": [T: "向右箭头X"] # 0x2947 + - "⥈": [T: "左右箭头小写圆"] # 0x2948 + - "⥉": [T: "向上二朝向箭头小写圆"] # 0x2949 + - "⥊": [T: "左倒刺上右倒刺下鱼叉箭头"] # 0x294a + - "⥋": [T: "左倒刺下右倒刺上鱼叉箭头"] # 0x294b + - "⥌": [T: "上倒刺右下倒刺左鱼叉箭头"] # 0x294c + - "⥍": [T: "上倒刺左下倒刺右鱼叉箭头"] # 0x294d + - "⥎": [T: "左倒刺上右倒刺上鱼叉箭头"] # 0x294e + - "⥏": [T: "上倒刺右下倒刺右鱼叉箭头"] # 0x294f + - "⥐": [T: "左倒刺下右倒刺下鱼叉箭头"] # 0x2950 + - "⥑": [T: "上倒刺左下倒刺左鱼叉箭头"] # 0x2951 + - "⥒": [T: "向左鱼叉箭头倒刺上横线"] # 0x2952 + - "⥓": [T: "向右鱼叉箭头倒刺上横线"] # 0x2953 + - "⥔": [T: "向上鱼叉箭头倒刺右横线"] # 0x2954 + - "⥕": [T: "向下鱼叉箭头倒刺右横线"] # 0x2955 + - "⥖": [T: "向左鱼叉箭头倒刺下横线"] # 0x2956 + - "⥗": [T: "向右鱼叉箭头倒刺下横线"] # 0x2957 + - "⥘": [T: "向上鱼叉箭头倒刺左横线"] # 0x2958 + - "⥙": [T: "向下鱼叉箭头倒刺左横线"] # 0x2959 + - "⥚": [T: "向左鱼叉箭头倒刺上横线"] # 0x295a + - "⥛": [T: "向右鱼叉箭头倒刺上横线"] # 0x295b + - "⥜": [T: "向上鱼叉箭头倒刺右横线"] # 0x295c + - "⥝": [T: "向下鱼叉箭头倒刺右横线"] # 0x295d + - "⥞": [T: "向左鱼叉箭头倒刺下横线"] # 0x295e + - "⥟": [T: "向右鱼叉箭头倒刺下横线"] # 0x295f + - "⥠": [T: "向上鱼叉箭头倒刺左横线"] # 0x2960 + - "⥡": [T: "向下鱼叉箭头倒刺左横线"] # 0x2961 + - "⥢": [T: "向左鱼叉箭头倒刺上上向左鱼叉箭头倒刺下"] # 0x2962 + - "⥣": [T: "向上鱼叉箭头倒刺左向上鱼叉箭头倒刺右"] # 0x2963 + - "⥤": [T: "向右鱼叉箭头倒刺上上向右鱼叉箭头倒刺下"] # 0x2964 + - "⥥": [T: "向下鱼叉箭头倒刺左向下鱼叉箭头倒刺右"] # 0x2965 + - "⥦": [T: "向左鱼叉箭头倒刺上上向右鱼叉箭头倒刺上"] # 0x2966 + - "⥧": [T: "向左鱼叉箭头倒刺下上向右鱼叉箭头倒刺下"] # 0x2967 + - "⥨": [T: "向右鱼叉箭头倒刺上上向左鱼叉箭头倒刺上"] # 0x2968 + - "⥩": [T: "向右鱼叉箭头倒刺下上向左鱼叉箭头倒刺下"] # 0x2969 + - "⥪": [T: "向左鱼叉箭头倒刺上上长破折号"] # 0x296a + - "⥫": [T: "向左鱼叉箭头倒刺下下长破折号"] # 0x296b + - "⥬": [T: "向右鱼叉箭头倒刺上上长破折号"] # 0x296c + - "⥭": [T: "向右鱼叉箭头倒刺下下长破折号"] # 0x296d + - "⥮": [T: "向上鱼叉箭头倒刺左向下鱼叉箭头倒刺右"] # 0x296e + - "⥯": [T: "向下鱼叉箭头倒刺左向上鱼叉箭头倒刺右"] # 0x296f + - "⥰": [T: "右双线箭头圆角头"] # 0x2970 + - "⥱": [T: "等号上方的向右箭头"] # 0x2971 + - "⥲": [T: "波浪运算符上向右箭头"] # 0x2972 + - "⥳": [T: "向左箭头上波浪运算符"] # 0x2973 + - "⥴": [T: "向右箭头上波浪运算符"] # 0x2974 + - "⥵": [T: "向右箭头上等"] # 0x2975 + - "⥶": [T: "向左箭头上方的小于"] # 0x2976 + - "⥷": [T: "穿过小于号的向左箭头"] # 0x2977 + - "⥸": [T: "向右箭头上方的大于"] # 0x2978 + - "⥹": [T: "子集上向右箭头"] # 0x2979 + - "⥺": [T: "向左箭头子集"] # 0x297a + - "⥻": [T: "超集上向左箭头"] # 0x297b + - "⥼": [T: "左鱼尾"] # 0x297c + - "⥽": [T: "右鱼尾"] # 0x297d + - "⥾": [T: "上鱼尾"] # 0x297e + - "⥿": [T: "下鱼尾"] # 0x297f + - "⦀": [T: "三重竖直线分隔符"] # 0x2980 + - "⦁": [T: "Z 记号点"] # 0x2981 + - "⦂": [T: "Z 记号类型冒号"] # 0x2982 + - "⦃": [T: "左白卷曲括号"] # 0x2983 + - "⦄": [T: "右白卷曲括号"] # 0x2984 + - "⦅": [T: "左白圆括号"] # 0x2985 + - "⦆": [T: "右白圆括号"] # 0x2986 + - "⦇": [T: "Z 记号左像括号"] # 0x2987 + - "⦈": [T: "Z 记号右像括号"] # 0x2988 + - "⦉": [T: "Z 记号左绑定括号"] # 0x2989 + - "⦊": [T: "Z 记号右绑定括号"] # 0x298a + - "⦋": [T: "左方括号下横线"] # 0x298b + - "⦌": [T: "右方括号下横线"] # 0x298c + - "⦍": [T: "左方括号顶角"] # 0x298d + - "⦎": [T: "右方括号底角"] # 0x298e + - "⦏": [T: "左方括号底角"] # 0x298f + - "⦐": [T: "右方括号顶角"] # 0x2990 + - "⦑": [T: "左角括号点"] # 0x2991 + - "⦒": [T: "右角括号点"] # 0x2992 + - "⦓": [T: "左弧小于方括号"] # 0x2993 + - "⦔": [T: "右弧大于方括号"] # 0x2994 + - "⦕": [T: "双左弧大于方括号"] # 0x2995 + - "⦖": [T: "双右弧小于方括号"] # 0x2996 + - "⦗": [T: "左黑贝壳括号"] # 0x2997 + - "⦘": [T: "右黑贝壳括号"] # 0x2998 + - "⦙": [T: "点线"] # 0x2999 + - "⦚": [T: "垂直之字形线"] # 0x299a + - "⦛": [T: "开口向左的度量角"] # 0x299b + - "⦜": [T: "带方框的直角变体"] # 0x299c + - "⦝": [T: "带点的度量直角"] # 0x299d + - "⦞": [T: "内含 s 的角"] # 0x299e + - "⦟": [T: "锐角"] # 0x299f + - "⦠": [T: "开口向左的球面角"] # 0x29a0 + - "⦡": [T: "开口向上的球面角"] # 0x29a1 + - "⦢": [T: "倒置角"] # 0x29a2 + - "⦣": [T: "反向角"] # 0x29a3 + - "⦤": [T: "角下横线"] # 0x29a4 + - "⦥": [T: "反向角下横线"] # 0x29a5 + - "⦦": [T: "开口向上的斜角"] # 0x29a6 + - "⦧": [T: "开口向下的斜角"] # 0x29a7 + - "⦨": [T: "测量角开放臂箭头指上右"] # 0x29a8 + - "⦩": [T: "测量角开放臂箭头指上左"] # 0x29a9 + - "⦪": [T: "测量角开放臂箭头指下右"] # 0x29aa + - "⦫": [T: "测量角开放臂箭头指下左"] # 0x29ab + - "⦬": [T: "测量角开放臂箭头指右上"] # 0x29ac + - "⦭": [T: "测量角开放臂箭头指左上"] # 0x29ad + - "⦮": [T: "测量角开放臂箭头指右下"] # 0x29ae + - "⦯": [T: "测量角开放臂箭头指左下"] # 0x29af + - "⦰": [T: "反向空集合"] # 0x29b0 + - "⦱": [T: "空集合上横线"] # 0x29b1 + - "⦲": [T: "上方带小圆圈的空集"] # 0x29b2 + - "⦳": [T: "上方带向右箭头的空集"] # 0x29b3 + - "⦴": [T: "上方带向左箭头的空集"] # 0x29b4 + - "⦵": [T: "圆水平横线"] # 0x29b5 + - "⦶": [T: "带圈竖线"] # 0x29b6 + - "⦷": [T: "带圈平行"] # 0x29b7 + - "⦸": [T: "带圈反向斜杠"] # 0x29b8 + - "⦹": [T: "带圈垂直"] # 0x29b9 + - "⦺": [T: "圆水平横线顶半垂直横线"] # 0x29ba + - "⦻": [T: "叠加乘号的圆圈"] # 0x29bb + - "⦼": [T: "带圈逆时针旋转除号"] # 0x29bc + - "⦽": [T: "穿过圆圈的向上箭头"] # 0x29bd + - "⦾": [T: "带圈白点"] # 0x29be + - "⦿": [T: "带圈点"] # 0x29bf + - "⧀": [T: "带圈小于"] # 0x29c0 + - "⧁": [T: "带圈大于"] # 0x29c1 + - "⧂": [T: "圆小写圆右"] # 0x29c2 + - "⧃": [T: "圆二水平笔画右"] # 0x29c3 + - "⧄": [T: "带方框对角斜杠"] # 0x29c4 + - "⧅": [T: "带方框对角斜杠"] # 0x29c5 + - "⧆": [T: "带方框星号"] # 0x29c6 + - "⧇": [T: "带方框小写圆"] # 0x29c7 + - "⧈": [T: "带方框方"] # 0x29c8 + - "⧉": [T: "二相连方"] # 0x29c9 + - "⧊": [T: "三角点上"] # 0x29ca + - "⧋": [T: "三角下横线"] # 0x29cb + - "⧌": [T: "三角"] # 0x29cc + - "⧍": [T: "三角衬线底"] # 0x29cd + - "⧎": [T: "右三角上左三角"] # 0x29ce + - "⧏": [T: "左三角垂直横线"] # 0x29cf + - "⧐": [T: "垂直横线右三角"] # 0x29d0 + - "⧑": [T: "蝴蝶结左半黑"] # 0x29d1 + - "⧒": [T: "蝴蝶结右半黑"] # 0x29d2 + - "⧓": [T: "黑蝴蝶结"] # 0x29d3 + - "⧔": [T: "乘号左半黑"] # 0x29d4 + - "⧕": [T: "乘号右半黑"] # 0x29d5 + - "⧖": [T: "白沙漏"] # 0x29d6 + - "⧗": [T: "黑沙漏"] # 0x29d7 + - "⧘": [T: "左波形围栏"] # 0x29d8 + - "⧙": [T: "右波形围栏"] # 0x29d9 + - "⧚": [T: "左双波形围栏"] # 0x29da + - "⧛": [T: "右双波形围栏"] # 0x29db + - "⧜": [T: "不完全无穷大"] # 0x29dc + - "⧝": [T: "领结无穷大"] # 0x29dd + - "⧞": [T: "无穷大垂直横线"] # 0x29de + - "⧟": [T: "双多重映射"] # 0x29df + - "⧠": [T: "方轮廓"] # 0x29e0 + - "⧡": [T: "增加"] # 0x29e1 + - "⧢": [T: "洗牌积"] # 0x29e2 + - "⧣": [T: "等号与斜平行号"] # 0x29e3 + - "⧤": [T: "上方带波浪线的等号与斜平行号"] # 0x29e4 + - "⧥": [T: "恒等且倾斜平行于"] # 0x29e5 + - "⧦": [T: "等势"] # 0x29e6 + - "⧧": [T: "热力学"] # 0x29e7 + - "⧨": [T: "下指三角左半黑"] # 0x29e8 + - "⧩": [T: "下指三角右半黑"] # 0x29e9 + - "⧪": [T: "黑菱下箭头"] # 0x29ea + - "⧫": [T: "黑菱形"] # 0x29eb + - "⧬": [T: "白圆下箭头"] # 0x29ec + - "⧭": [T: "黑圆下箭头"] # 0x29ed + - "⧮": [T: "带误差线的白色方形"] # 0x29ee + - "⧯": [T: "带误差线的黑色方形"] # 0x29ef + - "⧰": [T: "带误差线的白色菱形"] # 0x29f0 + - "⧱": [T: "带误差线的黑色菱形"] # 0x29f1 + - "⧲": [T: "带误差线的白色圆圈"] # 0x29f2 + - "⧳": [T: "带误差线的黑色圆圈"] # 0x29f3 + - "⧴": [T: "规则延迟"] # 0x29f4 + - "⧵": [T: "反向斜杠运算符"] # 0x29f5 + - "⧶": [T: "斜杠上横线"] # 0x29f6 + - "⧷": [T: "反向斜杠水平笔画"] # 0x29f7 + - "⧸": [T: "大斜杠"] # 0x29f8 + - "⧹": [T: "大反向斜杠"] # 0x29f9 + - "⧺": [T: "双加号"] # 0x29fa + - "⧻": [T: "三重加号"] # 0x29fb + - "⧼": [T: "左指曲线角括号"] # 0x29fc + - "⧽": [T: "右指曲线角括号"] # 0x29fd + - "⧾": [T: "微小"] # 0x29fe + - "⧿": [T: "最小值"] # 0x29ff + - "⨀": [T: "多元带圈点运算符"] # 0x2a00 + - "⨁": [T: "多元带圈加号运算符"] # 0x2a01 + - "⨂": [T: "多元带圈乘号运算符"] # 0x2a02 + - "⨃": [T: "多元并集运算符点"] # 0x2a03 + - "⨄": [T: "多元并集运算符加号"] # 0x2a04 + - "⨅": [T: "多元方交集运算符"] # 0x2a05 + - "⨆": [T: "多元方并集运算符"] # 0x2a06 + - "⨇": [T: "二重逻辑与运算符"] # 0x2a07 + - "⨈": [T: "二重逻辑或运算符"] # 0x2a08 + - "⨉": [T: "多元乘号运算符"] # 0x2a09 + - "⨊": [T: "模二和"] # 0x2a0a + - "⨋": [T: "带积分号的求和"] # 0x2a0b + - "⨌": [T: "四重积分运算符"] # 0x2a0c + - "⨍": [T: "有限部积分"] # 0x2a0d + - "⨎": [T: "双划线积分"] # 0x2a0e + - "⨏": [T: "带斜线的积分平均"] # 0x2a0f + - "⨐": [T: "环流函数"] # 0x2a10 + - "⨑": [T: "逆时针积分"] # 0x2a11 + - "⨒": [T: "绕极点矩形路径线积分"] # 0x2a12 + - "⨓": [T: "绕极点半圆路径线积分"] # 0x2a13 + - "⨔": [T: "不包含极点的线积分"] # 0x2a14 + - "⨕": [T: "绕点积分运算符"] # 0x2a15 + - "⨖": [T: "四元数积分运算符"] # 0x2a16 + - "⨗": [T: "积分向左箭头钩"] # 0x2a17 + - "⨘": [T: "积分乘号"] # 0x2a18 + - "⨙": [T: "积分交集"] # 0x2a19 + - "⨚": [T: "积分并集"] # 0x2a1a + - "⨛": [T: "积分上横线"] # 0x2a1b + - "⨜": [T: "积分下横线"] # 0x2a1c + - "⨝": [T: "连接"] # 0x2a1d + - "⨞": [T: "大左三角运算符"] # 0x2a1e + - "⨟": [T: "记号组合"] # 0x2a1f + - "⨠": [T: "记号管道"] # 0x2a20 + - "⨡": [T: "记号投影"] # 0x2a21 + - "⨢": [T: "上方带圆圈的加号"] # 0x2a22 + - "⨣": [T: "上方带抑扬符的加号"] # 0x2a23 + - "⨤": [T: "上方带波浪线的加号"] # 0x2a24 + - "⨥": [T: "下方带点的加号"] # 0x2a25 + - "⨦": [T: "下方带波浪线的加号"] # 0x2a26 + - "⨧": [T: "带下标二的加号"] # 0x2a27 + - "⨨": [T: "带黑三角的加号"] # 0x2a28 + - "⨩": [T: "上方带逗号的减号"] # 0x2a29 + - "⨪": [T: "下方带点的减号"] # 0x2a2a + - "⨫": [T: "带下降点列的减号"] # 0x2a2b + - "⨬": [T: "带上升点列的减号"] # 0x2a2c + - "⨭": [T: "左半圆内的加号"] # 0x2a2d + - "⨮": [T: "右半圆内的加号"] # 0x2a2e + - "⨯": # 0x2a2f + - test: + if: "$Verbosity='Terse'" + then: [T: "叉乘"] + else: [T: "向量叉积"] + - "⨰": [T: "上方带点的乘号"] # 0x2a30 + - "⨱": [T: "下方带横线的乘号"] # 0x2a31 + - "⨲": [T: "底部闭合的半直积"] # 0x2a32 + - "⨳": [T: "Smash 积"] # 0x2a33 + - "⨴": [T: "左半圆内的乘号"] # 0x2a34 + - "⨵": [T: "右半圆内的乘号"] # 0x2a35 + - "⨶": [T: "上方带抑扬符的圈乘号"] # 0x2a36 + - "⨷": [T: "双圆圈内的乘号"] # 0x2a37 + - "⨸": [T: "带圈除号"] # 0x2a38 + - "⨹": [T: "三角形内的加号"] # 0x2a39 + - "⨺": [T: "三角形内的减号"] # 0x2a3a + - "⨻": [T: "三角形内的乘号"] # 0x2a3b + - "⨼": [T: "内乘"] # 0x2a3c + - "⨽": [T: "右内乘"] # 0x2a3d + - "⨾": [T: "Z 记号关系复合"] # 0x2a3e + - "⨿": [T: "合并积或余积"] # 0x2a3f + - "⩀": [T: "交集点"] # 0x2a40 + - "⩁": [T: "并集减号"] # 0x2a41 + - "⩂": [T: "并集上横线"] # 0x2a42 + - "⩃": [T: "交集上横线"] # 0x2a43 + - "⩄": [T: "交集与逻辑与"] # 0x2a44 + - "⩅": [T: "并集与逻辑或"] # 0x2a45 + - "⩆": [T: "并集上交集"] # 0x2a46 + - "⩇": [T: "交集上并集"] # 0x2a47 + - "⩈": [T: "并集上横线上交集"] # 0x2a48 + - "⩉": [T: "交集上横线上并集"] # 0x2a49 + - "⩊": [T: "并集相连并集"] # 0x2a4a + - "⩋": [T: "交集相连交集"] # 0x2a4b + - "⩌": [T: "闭合并集衬线"] # 0x2a4c + - "⩍": [T: "闭合交集衬线"] # 0x2a4d + - "⩎": [T: "双重方形交集"] # 0x2a4e + - "⩏": [T: "双重方形并集"] # 0x2a4f + - "⩐": [T: "带衬线和 Smash 积的闭合并集"] # 0x2a50 + - "⩑": [T: "上加点的逻辑与"] # 0x2a51 + - "⩒": [T: "上加点的逻辑或"] # 0x2a52 + - "⩓": [T: "双逻辑与"] # 0x2a53 + - "⩔": [T: "双逻辑或"] # 0x2a54 + - "⩕": [T: "两个相交的逻辑与"] # 0x2a55 + - "⩖": [T: "两个相交的逻辑或"] # 0x2a56 + - "⩗": [T: "倾斜大型逻辑或"] # 0x2a57 + - "⩘": [T: "倾斜大型逻辑与"] # 0x2a58 + - "⩙": [T: "逻辑或与逻辑与叠加"] # 0x2a59 + - "⩚": [T: "带中间竖线的逻辑与"] # 0x2a5a + - "⩛": [T: "带中间竖线的逻辑或"] # 0x2a5b + - "⩜": [T: "带横线的逻辑与"] # 0x2a5c + - "⩝": [T: "带横线的逻辑或"] # 0x2a5d + - "⩞": [T: "带双上横线的逻辑与"] # 0x2a5e + - "⩟": [T: "带下横线的逻辑与"] # 0x2a5f + - "⩠": [T: "带双下横线的逻辑与"] # 0x2a60 + - "⩡": [T: "带下横线的小逻辑或"] # 0x2a61 + - "⩢": [T: "带双上横线的逻辑或"] # 0x2a62 + - "⩣": [T: "带双下横线的逻辑或"] # 0x2a63 + - "⩤": [T: "Z 记号定义域反限制"] # 0x2a64 + - "⩥": [T: "Z 记号值域反限制"] # 0x2a65 + - "⩦": [T: "下方带点的等号"] # 0x2a66 + - "⩧": [T: "上方带点的恒等号"] # 0x2a67 + - "⩨": [T: "三重水平横线双垂直笔画"] # 0x2a68 + - "⩩": [T: "三重水平横线三重垂直笔画"] # 0x2a69 + - "⩪": [T: "上方带点的波浪运算符"] # 0x2a6a + - "⩫": [T: "带上升点列的波浪运算符"] # 0x2a6b + - "⩬": [T: "相似号、减号、相似号"] # 0x2a6c + - "⩭": [T: "上方带点的全等号"] # 0x2a6d + - "⩮": [T: "等号星号"] # 0x2a6e + - "⩯": [T: "上方带抑扬符的约等号"] # 0x2a6f + - "⩰": [T: "约等于或等于"] # 0x2a70 + - "⩱": [T: "等号在加号上方"] # 0x2a71 + - "⩲": [T: "加号在等号上方"] # 0x2a72 + - "⩳": [T: "等号在波浪运算符上方"] # 0x2a73 + - "⩴": [T: "双冒号等号"] # 0x2a74 + - "⩵": [T: "连续两个等号"] # 0x2a75 + - "⩶": [T: "连续三个等号"] # 0x2a76 + - "⩷": [T: "上下各带两点的等号"] # 0x2a77 + - "⩸": [T: "等价四点上"] # 0x2a78 + - "⩹": [T: "内含圆圈的小于号"] # 0x2a79 + - "⩺": [T: "内含圆圈的大于号"] # 0x2a7a + - "⩻": [T: "带上加问号的小于"] # 0x2a7b + - "⩼": [T: "带上加问号的大于"] # 0x2a7c + - "⩽": [T: "小于或倾斜等号"] # 0x2a7d + - "⩾": [T: "大于或倾斜等号"] # 0x2a7e + - "⩿": [T: "内含点的小于或斜等号"] # 0x2a7f + - "⪀": [T: "内含点的大于或斜等号"] # 0x2a80 + - "⪁": [T: "带上加点的小于或倾斜等号"] # 0x2a81 + - "⪂": [T: "带上加点的大于或倾斜等号"] # 0x2a82 + - "⪃": [T: "带右上方的点的小于或倾斜等号"] # 0x2a83 + - "⪄": [T: "带左上方的点的大于或倾斜等号"] # 0x2a84 + - "⪅": [T: "小于或约等号"] # 0x2a85 + - "⪆": [T: "大于或约等号"] # 0x2a86 + - "⪇": [T: "小于与单线不等号"] # 0x2a87 + - "⪈": [T: "大于与单线不等号"] # 0x2a88 + - "⪉": [T: "小于与不约等号"] # 0x2a89 + - "⪊": [T: "大于与不约等号"] # 0x2a8a + - "⪋": [T: "大于上方的双线等号上方的小于"] # 0x2a8b + - "⪌": [T: "小于上方的双线等号上方的大于"] # 0x2a8c + - "⪍": [T: "相似或等号上方的小于"] # 0x2a8d + - "⪎": [T: "相似或等号上方的大于"] # 0x2a8e + - "⪏": [T: "大于上方的相似上方的小于"] # 0x2a8f + - "⪐": [T: "小于上方的相似上方的大于"] # 0x2a90 + - "⪑": [T: "双线等号上方的大于上方的小于"] # 0x2a91 + - "⪒": [T: "双线等号上方的小于上方的大于"] # 0x2a92 + - "⪓": [T: "倾斜等号上方的大于上方的倾斜等号上方的小于"] # 0x2a93 + - "⪔": [T: "倾斜等号上方的小于上方的倾斜等号上方的大于"] # 0x2a94 + - "⪕": [T: "倾斜等号或小于"] # 0x2a95 + - "⪖": [T: "倾斜等号或大于"] # 0x2a96 + - "⪗": [T: "内含点的斜等号或小于号"] # 0x2a97 + - "⪘": [T: "内含点的斜等号或大于号"] # 0x2a98 + - "⪙": [T: "双线等号或小于"] # 0x2a99 + - "⪚": [T: "双线等号或大于"] # 0x2a9a + - "⪛": [T: "双线倾斜等号或小于"] # 0x2a9b + - "⪜": [T: "双线倾斜等号或大于"] # 0x2a9c + - "⪝": [T: "相似或小于"] # 0x2a9d + - "⪞": [T: "相似或大于"] # 0x2a9e + - "⪟": [T: "相似号、小于号与等号从上到下排列"] # 0x2a9f + - "⪠": [T: "相似号、大于号与等号从上到下排列"] # 0x2aa0 + - "⪡": [T: "双嵌套小于"] # 0x2aa1 + - "⪢": [T: "双嵌套大于"] # 0x2aa2 + - "⪣": [T: "带下划线的双嵌套小于"] # 0x2aa3 + - "⪤": [T: "大于号与小于号叠加"] # 0x2aa4 + - "⪥": [T: "并列的大于号与小于号"] # 0x2aa5 + - "⪦": [T: "曲线闭合的小于号"] # 0x2aa6 + - "⪧": [T: "曲线闭合的大于号"] # 0x2aa7 + - "⪨": [T: "曲线上方为小于号、下方为斜等号"] # 0x2aa8 + - "⪩": [T: "曲线上方为大于号、下方为斜等号"] # 0x2aa9 + - "⪪": [T: "小于"] # 0x2aaa + - "⪫": [T: "大于"] # 0x2aab + - "⪬": [T: "小于或等于"] # 0x2aac + - "⪭": [T: "大于或等于"] # 0x2aad + - "⪮": [T: "上方带波形线的等号"] # 0x2aae + - "⪯": [T: "先于号在单线等号上方"] # 0x2aaf + - "⪰": [T: "后于号在单线等号上方"] # 0x2ab0 + - "⪱": [T: "先于号在单线不等号上方"] # 0x2ab1 + - "⪲": [T: "后于号在单线不等号上方"] # 0x2ab2 + - "⪳": [T: "先于号在等号上方"] # 0x2ab3 + - "⪴": [T: "后于号在等号上方"] # 0x2ab4 + - "⪵": [T: "先于号在不等号上方"] # 0x2ab5 + - "⪶": [T: "后于号在不等号上方"] # 0x2ab6 + - "⪷": [T: "先于号在约等号上方"] # 0x2ab7 + - "⪸": [T: "后于号在约等号上方"] # 0x2ab8 + - "⪹": [T: "先于号在不约等号上方"] # 0x2ab9 + - "⪺": [T: "后于号在不约等号上方"] # 0x2aba + - "⪻": [T: "双先于"] # 0x2abb + - "⪼": [T: "双后于"] # 0x2abc + - "⪽": [T: "带点的子集符号"] # 0x2abd + - "⪾": [T: "带点的超集符号"] # 0x2abe + - "⪿": [T: "下方带加号的子集符号"] # 0x2abf + - "⫀": [T: "下方带加号的超集符号"] # 0x2ac0 + - "⫁": [T: "下方带乘号的子集符号"] # 0x2ac1 + - "⫂": [T: "下方带乘号的超集符号"] # 0x2ac2 + - "⫃": [T: "上方带点的包含于符号"] # 0x2ac3 + - "⫄": [T: "上方带点的包含符号"] # 0x2ac4 + - "⫅": [T: "子集符号在等号上方"] # 0x2ac5 + - "⫆": [T: "超集符号在等号上方"] # 0x2ac6 + - "⫇": [T: "子集符号在波浪运算符上方"] # 0x2ac7 + - "⫈": [T: "超集符号在波浪运算符上方"] # 0x2ac8 + - "⫉": [T: "子集符号在约等号上方"] # 0x2ac9 + - "⫊": [T: "超集符号在约等号上方"] # 0x2aca + - "⫋": [T: "真包含于"] # 0x2acb + - "⫌": [T: "真包含"] # 0x2acc + - "⫍": [T: "方左开放方框运算符"] # 0x2acd + - "⫎": [T: "方右开放方框运算符"] # 0x2ace + - "⫏": [T: "闭合子集"] # 0x2acf + - "⫐": [T: "闭合超集"] # 0x2ad0 + - "⫑": [T: "闭合子集等"] # 0x2ad1 + - "⫒": [T: "闭合超集等"] # 0x2ad2 + - "⫓": [T: "子集上超集"] # 0x2ad3 + - "⫔": [T: "超集上子集"] # 0x2ad4 + - "⫕": [T: "子集上子集"] # 0x2ad5 + - "⫖": [T: "超集上超集"] # 0x2ad6 + - "⫗": [T: "超集子集"] # 0x2ad7 + - "⫘": [T: "超集相连破折号子集"] # 0x2ad8 + - "⫙": [T: "元素打开向下"] # 0x2ad9 + - "⫚": [T: "叉丁字顶"] # 0x2ada + - "⫛": [T: "横截相交"] # 0x2adb + - "⫝̸": [T: "分叉"] # 0x2adc + - "⫝": [T: "非分叉"] # 0x2add + - "⫞": [T: "短左丁字"] # 0x2ade + - "⫟": [T: "短下丁字"] # 0x2adf + - "⫠": [T: "短上丁字"] # 0x2ae0 + - "⫡": [T: "垂直S"] # 0x2ae1 + - "⫢": [T: "竖直线三重右断言符"] # 0x2ae2 + - "⫣": [T: "双竖直线左断言符"] # 0x2ae3 + - "⫤": [T: "竖直线双左断言符"] # 0x2ae4 + - "⫥": [T: "双竖直线双左断言符"] # 0x2ae5 + - "⫦": [T: "长破折号左成员双垂直"] # 0x2ae6 + - "⫧": [T: "短下钉上横线"] # 0x2ae7 + - "⫨": [T: "短上钉下横线"] # 0x2ae8 + - "⫩": [T: "短上钉上短下钉"] # 0x2ae9 + - "⫪": [T: "双下丁字"] # 0x2aea + - "⫫": [T: "双上丁字"] # 0x2aeb + - "⫬": [T: "双笔画非"] # 0x2aec + - "⫭": [T: "反向双笔画非"] # 0x2aed + - "⫮": [T: "带反向否定斜线的不整除"] # 0x2aee + - "⫯": [T: "垂直线圆上"] # 0x2aef + - "⫰": [T: "垂直线圆下"] # 0x2af0 + - "⫱": [T: "下钉圆下"] # 0x2af1 + - "⫲": [T: "平行水平笔画"] # 0x2af2 + - "⫳": [T: "平行波浪运算符"] # 0x2af3 + - "⫴": [T: "三重垂直横线关系"] # 0x2af4 + - "⫵": [T: "带水平笔画的三竖线"] # 0x2af5 + - "⫶": [T: "三重冒号运算符"] # 0x2af6 + - "⫷": [T: "三重嵌套小于"] # 0x2af7 + - "⫸": [T: "三重嵌套大于"] # 0x2af8 + - "⫹": [T: "双线倾斜小于或等号"] # 0x2af9 + - "⫺": [T: "双线倾斜大于或等号"] # 0x2afa + - "⫻": [T: "三重斜杠关系"] # 0x2afb + - "⫼": [T: "大三重竖直线运算符"] # 0x2afc + - "⫽": [T: "双斜杠运算符"] # 0x2afd + - "⫾": [T: "白色竖直线"] # 0x2afe + - "⫿": [T: "白色竖直线"] # 0x2aff + - "⬀": [T: "右上白色箭头"] # 0x2b00 + - "⬁": [T: "左上白色箭头"] # 0x2b01 + - "⬂": [T: "右下白色箭头"] # 0x2b02 + - "⬃": [T: "左下白色箭头"] # 0x2b03 + - "⬄": [T: "左右白色箭头"] # 0x2b04 + - "⬅": [T: "左黑色箭头"] # 0x2b05 + - "⬆": [T: "上黑色箭头"] # 0x2b06 + - "⬇": [T: "下黑色箭头"] # 0x2b07 + - "⬈": [T: "右上黑色箭头"] # 0x2b08 + - "⬉": [T: "左上黑色箭头"] # 0x2b09 + - "⬊": [T: "右下黑色箭头"] # 0x2b0a + - "⬋": [T: "左下黑色箭头"] # 0x2b0b + - "⬌": [T: "左右箭头"] # 0x2b0c + - "⬍": [T: "上下箭头"] # 0x2b0d + - "⬎": [T: "右开放箭头"] # 0x2b0e + - "⬏": [T: "右开放重箭头"] # 0x2b0f + - "⬐": [T: "左开放箭头"] # 0x2b10 + - "⬑": [T: "左开放重箭头"] # 0x2b11 + - "⬒": [T: "方顶半黑"] # 0x2b12 + - "⬓": [T: "方底半黑"] # 0x2b13 + - "⬔": [T: "方上右对角半黑"] # 0x2b14 + - "⬕": [T: "方下左对角半黑"] # 0x2b15 + - "⬖": [T: "菱左半黑"] # 0x2b16 + - "⬗": [T: "菱右半黑"] # 0x2b17 + - "⬘": [T: "菱顶半黑"] # 0x2b18 + - "⬙": [T: "菱底半黑"] # 0x2b19 + - "⬚": [T: "点线方"] # 0x2b1a + - "⬛": [T: "黑色大方块"] # 0x2b1b + - "⬜": [T: "白色大方块"] # 0x2b1c + - "⬝": [T: "中方点"] # 0x2b1d + - "⬞": [T: "白小方块"] # 0x2b1e + - "⬟": [T: "黑五边形"] # 0x2b1f + - "⬠": [T: "白五边形"] # 0x2b20 + - "⬡": [T: "白六边形"] # 0x2b21 + - "⬢": [T: "黑六边形"] # 0x2b22 + - "⬣": [T: "横向黑六边形"] # 0x2b23 + - "⬤": [T: "黑色大圆"] # 0x2b24 + - "⬥": [T: "黑色中菱形"] # 0x2b25 + - "⬦": [T: "白色中菱形"] # 0x2b26 + - "⬧": [T: "黑色中菱形(小)"] # 0x2b27 + - "⬨": [T: "白色中菱形(小)"] # 0x2b28 + - "⬩": [T: "黑小菱形"] # 0x2b29 + - "⬪": [T: "黑色椭圆"] # 0x2b2a + - "⬫": [T: "白色椭圆"] # 0x2b2b + - "⬬": [T: "白小菱形"] # 0x2b2c + - "⬭": [T: "横向白椭圆"] # 0x2b2d + - "⬮": [T: "横向黑椭圆"] # 0x2b2e + - "⬯": [T: "纵向白椭圆"] # 0x2b2f + - "⬰": [T: "纵向黑椭圆"] # 0x2b30 + - "⬱": [T: "三向左箭头"] # 0x2b31 + - "⬲": [T: "左箭头带圈加号"] # 0x2b32 + - "⬳": [T: "长向左箭头"] # 0x2b33 + - "⬴": [T: "向左二朝向箭头垂直笔画"] # 0x2b34 + - "⬵": [T: "向左二朝向箭头双垂直笔画"] # 0x2b35 + - "⬶": [T: "向左二朝向箭头横线"] # 0x2b36 + - "⬷": [T: "向左二朝向三重破折号箭头"] # 0x2b37 + - "⬸": [T: "向左箭头点线杆"] # 0x2b38 + - "⬹": [T: "向左箭头尾垂直笔画"] # 0x2b39 + - "⬺": [T: "向左箭头尾双垂直笔画"] # 0x2b3a + - "⬻": [T: "向左二朝向箭头尾"] # 0x2b3b + - "⬼": [T: "向左二朝向箭头尾垂直笔画"] # 0x2b3c + - "⬽": [T: "向左二朝向箭头尾双垂直笔画"] # 0x2b3d + - "⬾": [T: "向左箭头X"] # 0x2b3e + - "⬿": [T: "挥手箭头指左"] # 0x2b3f + - "⭀": [T: "等号上方的向左箭头"] # 0x2b40 + - "⭁": [T: "反向波浪运算符上向左箭头"] # 0x2b41 + - "⭂": [T: "向左箭头上反向等"] # 0x2b42 + - "⭃": [T: "穿过大于号的向右箭头"] # 0x2b43 + - "⭄": [T: "向右箭头超集"] # 0x2b44 + - "⭅": [T: "向左四重箭头"] # 0x2b45 + - "⭆": [T: "向右四重箭头"] # 0x2b46 + - "⭇": [T: "反向波浪运算符上向右箭头"] # 0x2b47 + - "⭈": [T: "向右箭头上反向等"] # 0x2b48 + - "⭉": [T: "波浪运算符上向左箭头"] # 0x2b49 + - "⭊": [T: "向左箭头上等"] # 0x2b4a + - "⭋": [T: "向左箭头上反向波浪运算符"] # 0x2b4b + - "⭌": [T: "向右箭头上反向波浪运算符"] # 0x2b4c + - "⭐": [T: "白色中号星"] # 0x2b50 + - "⭑": [T: "白色中星(旋转)"] # 0x2b51 + - "⭒": [T: "白色星轮廓"] # 0x2b52 + - "⭓": [T: "黑右箭头(重型)"] # 0x2b53 + - "⭔": [T: "白右箭头(重型)"] # 0x2b54 + - "⭕": [T: "粗大圆圈"] # 0x2b55 + - "⭖": [T: "带圈椭圆"] # 0x2b56 + - "⭗": [T: "带圈上斜椭圆"] # 0x2b57 + - "⭘": [T: "重圆"] # 0x2b58 + - "⭙": [T: "重带圈椭圆"] # 0x2b59 + - "⸀": [T: "右角"] # 0x2e00 + - "⸁": [T: "右角点线"] # 0x2e01 + - "⸂": [T: "左括号"] # 0x2e02 + - "⸃": [T: "右括号"] # 0x2e03 + - "⸄": [T: "左点线括号"] # 0x2e04 + - "⸅": [T: "右点线括号"] # 0x2e05 + - "⸆": [T: "上标"] # 0x2e06 + - "⸇": [T: "上标点线"] # 0x2e07 + - "⸈": [T: "点线"] # 0x2e08 + - "⸉": [T: "左括号"] # 0x2e09 + - "⸊": [T: "右括号"] # 0x2e0a + - "⸋": [T: "上标方"] # 0x2e0b + - "⸌": [T: "左上标括号"] # 0x2e0c + - "⸍": [T: "右上标括号"] # 0x2e0d + - "⸎": [T: "编辑冠状符"] # 0x2e0e + - "⸏": [T: "段落符"] # 0x2e0f + - "⸐": [T: "分叉段落符"] # 0x2e10 + - "⸑": [T: "反向分叉段落符"] # 0x2e11 + - "⸒": [T: "下分号"] # 0x2e12 + - "⸓": [T: "点线"] # 0x2e13 + - "⸔": [T: "向下锚形符"] # 0x2e14 + - "⸕": [T: "向上锚形符"] # 0x2e15 + - "⸖": [T: "点线右指角"] # 0x2e16 + - "⸗": [T: "双连字符"] # 0x2e17 + - "⸘": [T: "倒"] # 0x2e18 + - "⸙": [T: "手掌"] # 0x2e19 + - "⸚": [T: "连字符分音"] # 0x2e1a + - "⸛": [T: "波浪圆圈上"] # 0x2e1b + - "⸜": [T: "左低括号"] # 0x2e1c + - "⸝": [T: "右低括号"] # 0x2e1d + - "⸞": [T: "波浪点上"] # 0x2e1e + - "⸟": [T: "波浪点下"] # 0x2e1f + - "⸠": [T: "左垂直横线"] # 0x2e20 + - "⸡": [T: "右垂直横线"] # 0x2e21 + - "⸢": [T: "顶左半括号"] # 0x2e22 + - "⸣": [T: "顶右半括号"] # 0x2e23 + - "⸤": [T: "底左半括号"] # 0x2e24 + - "⸥": [T: "底右半括号"] # 0x2e25 + - "⸦": [T: "左括号"] # 0x2e26 + - "⸧": [T: "右括号"] # 0x2e27 + - "⸨": [T: "左双圆括号"] # 0x2e28 + - "⸩": [T: "右双圆括号"] # 0x2e29 + - "⸪": [T: "二点一点标点"] # 0x2e2a + - "⸫": [T: "一点二点标点"] # 0x2e2b + - "⸬": [T: "带方框四点标点"] # 0x2e2c + - "⸭": [T: "五点"] # 0x2e2d + - "⸮": [T: "反向问号"] # 0x2e2e + - "ⸯ": [T: "竖直波浪号"] # 0x2e2f + - "⸰": [T: "圆圈指"] # 0x2e30 + - "⸱": [T: "词分隔符中指点"] # 0x2e31 + - "⸲": [T: "倒置逗号"] # 0x2e32 + - "⸳": [T: "上标点"] # 0x2e33 + - "⸴": [T: "上标逗号"] # 0x2e34 + - "⸵": [T: "倒置分号"] # 0x2e35 + - "⸶": [T: "匕首左"] # 0x2e36 + - "⸷": [T: "匕首右"] # 0x2e37 + - "⸸": [T: "倒置匕首"] # 0x2e38 + - "⸹": [T: "顶半章节"] # 0x2e39 + - "⸺": [T: "二破折号"] # 0x2e3a + - "⸻": [T: "三破折号"] # 0x2e3b + - "〃": [T: "同上"] # 0x3003 + - "〈": [T: "左角括号"] # 0x3008 + - "〉": [T: "右角括号"] # 0x3009 + - "《": [T: "左双角括号"] # 0x300a + - "》": [T: "右双角括号"] # 0x300b + - "「": [T: "左角括号"] # 0x300c + - "」": [T: "右角括号"] # 0x300d + - "『": [T: "左白角括号"] # 0x300e + - "』": [T: "右白角括号"] # 0x300f + - "【": [T: "左黑透镜形括号"] # 0x3010 + - "】": [T: "右黑透镜形括号"] # 0x3011 + - "〔": [T: "左贝壳括号"] # 0x3014 + - "〕": [T: "右贝壳括号"] # 0x3015 + - "〖": [T: "左白透镜形括号"] # 0x3016 + - "〗": [T: "右白透镜形括号"] # 0x3017 + - "〘": [T: "左白贝壳括号"] # 0x3018 + - "〙": [T: "右白贝壳括号"] # 0x3019 + - "〚": [T: "左白方括号"] # 0x301a + - "〛": [T: "右白方括号"] # 0x301b + - "〜": [T: "挥手破折号"] # 0x301c + - "〰": [T: "波浪破折号"] # 0x3030 + - "㉈": [T: "带圈数字十在黑色方形"] # 0x3248 + - "㉉": [T: "带圈数字二十在黑色方形"] # 0x3249 + - "㉊": [T: "带圈数字三十在黑色方形"] # 0x324a + - "㉋": [T: "带圈数字四十在黑色方形"] # 0x324b + - "㉌": [T: "带圈数字五十在黑色方形"] # 0x324c + - "㉍": [T: "带圈数字六十在黑色方形"] # 0x324d + - "㉎": [T: "带圈数字七十在黑色方形"] # 0x324e + - "㉏": [T: "带圈数字八十在黑色方形"] # 0x324f + - "㉑": [T: "带圈数字二十一"] # 0x3251 + - "㉒": [T: "带圈数字二十二"] # 0x3252 + - "㉓": [T: "带圈数字二十三"] # 0x3253 + - "㉔": [T: "带圈数字二十四"] # 0x3254 + - "㉕": [T: "带圈数字二十五"] # 0x3255 + - "㉖": [T: "带圈数字二十六"] # 0x3256 + - "㉗": [T: "带圈数字二十七"] # 0x3257 + - "㉘": [T: "带圈数字二十八"] # 0x3258 + - "㉙": [T: "带圈数字二十九"] # 0x3259 + - "㉚": [T: "带圈数字三十"] # 0x325a + - "㉛": [T: "带圈数字三十一"] # 0x325b + - "㉜": [T: "带圈数字三十二"] # 0x325c + - "㉝": [T: "带圈数字三十三"] # 0x325d + - "㉞": [T: "带圈数字三十四"] # 0x325e + - "㉟": [T: "带圈数字三十五"] # 0x325f + - "㊱": [T: "带圈数字三十六"] # 0x32b1 + - "㊲": [T: "带圈数字三十七"] # 0x32b2 (GPT-5.4 translation) + - "㊳": [T: "带圈数字三十八"] # 0x32b3 (GPT-5.4 translation) + - "㊴": [T: "带圈数字三十九"] # 0x32b4 (GPT-5.4 translation) + - "㊵": [T: "带圈数字四十"] # 0x32b5 (GPT-5.4 translation) + - "㊶": [T: "带圈数字四十一"] # 0x32b6 (GPT-5.4 translation) + - "㊷": [T: "带圈数字四十二"] # 0x32b7 (GPT-5.4 translation) + - "㊸": [T: "带圈数字四十三"] # 0x32b8 (GPT-5.4 translation) + - "㊹": [T: "带圈数字四十四"] # 0x32b9 (GPT-5.4 translation) + - "㊺": [T: "带圈数字四十五"] # 0x32ba (GPT-5.4 translation) + - "㊻": [T: "带圈数字四十六"] # 0x32bb (GPT-5.4 translation) + - "㊼": [T: "带圈数字四十七"] # 0x32bc (GPT-5.4 translation) + - "㊽": [T: "带圈数字四十八"] # 0x32bd (GPT-5.4 translation) + - "㊾": [T: "带圈数字四十九"] # 0x32be (GPT-5.4 translation) + - "㊿": [T: "带圈数字五十"] # 0x32bf (GPT-5.4 translation) + - "㋌": [T: "汞柱"] # 0x32cc + - "㋍": [T: "尔格"] # 0x32cd + - "㋎": [T: "电子伏特"] # 0x32ce + - "㋏": [T: "有限责任"] # 0x32cf + - "㍱": [T: "百帕"] # 0x3371 + - "㍲": [T: "道尔顿"] # 0x3372 + - "㍳": [T: "天文单位"] # 0x3373 + - "㍴": [T: "巴"] # 0x3374 + - "㍵": [T: "OV"] # 0x3375 + - "㍶": [T: "秒差距"] # 0x3376 + - "㍷": [T: "分米"] # 0x3377 + - "㍸": [T: "平方分米"] # 0x3378 + - "㍹": [T: "立方分米"] # 0x3379 + - "㍺": [T: "国际单位"] # 0x337a + - "㎀": [T: "皮安"] # 0x3380 + - "㎁": [T: "纳安"] # 0x3381 + - "㎂": [T: "微安"] # 0x3382 + - "㎃": [T: "毫安"] # 0x3383 + - "㎄": [T: "千安"] # 0x3384 + - "㎅": [T: "千字节"] # 0x3385 + - "㎆": [T: "兆字节"] # 0x3386 + - "㎇": [T: "吉字节"] # 0x3387 + - "㎈": [T: "卡"] # 0x3388 + - "㎉": [T: "千卡"] # 0x3389 + - "㎊": [T: "皮法"] # 0x338a + - "㎋": [T: "纳法"] # 0x338b + - "㎌": [T: "微法"] # 0x338c + - "㎍": [T: "微克"] # 0x338d + - "㎎": [T: "毫克"] # 0x338e + - "㎏": [T: "千克"] # 0x338f + - "㎐": [T: "赫兹"] # 0x3390 + - "㎑": [T: "千赫"] # 0x3391 + - "㎒": [T: "兆赫"] # 0x3392 + - "㎓": [T: "吉赫"] # 0x3393 + - "㎔": [T: "太赫"] # 0x3394 + - "㎕": [T: "微升"] # 0x3395 + - "㎖": [T: "毫升"] # 0x3396 + - "㎗": [T: "分升"] # 0x3397 + - "㎘": [T: "千升"] # 0x3398 + - "㎙": [T: "飞米"] # 0x3399 + - "㎚": [T: "纳米"] # 0x339a + - "㎛": [T: "微米"] # 0x339b + - "㎜": [T: "毫米"] # 0x339c + - "㎝": [T: "厘米"] # 0x339d + - "㎞": [T: "千米"] # 0x339e + - "㎟": [T: "平方毫米"] # 0x339f + - "㎠": [T: "平方厘米"] # 0x33a0 + - "㎡": [T: "平方米"] # 0x33a1 + - "㎢": [T: "平方千米"] # 0x33a2 + - "㎣": [T: "立方毫米"] # 0x33a3 + - "㎤": [T: "立方厘米"] # 0x33a4 + - "㎥": [T: "立方米"] # 0x33a5 + - "㎦": [T: "立方千米"] # 0x33a6 + - "㎧": [T: "米每秒"] # 0x33a7 + - "㎨": [T: "米每二次方秒"] # 0x33a8 + - "㎩": [T: "帕"] # 0x33a9 + - "㎪": [T: "千帕"] # 0x33aa + - "㎫": [T: "兆帕"] # 0x33ab + - "㎬": [T: "吉帕"] # 0x33ac + - "㎭": [T: "弧度"] # 0x33ad + - "㎮": [T: "弧度每秒"] # 0x33ae + - "㎯": [T: "弧度每二次方秒"] # 0x33af + - "㎰": [T: "皮秒"] # 0x33b0 + - "㎱": [T: "纳秒"] # 0x33b1 + - "㎲": [T: "微秒"] # 0x33b2 + - "㎳": [T: "毫秒"] # 0x33b3 + - "㎴": [T: "皮伏"] # 0x33b4 + - "㎵": [T: "纳伏"] # 0x33b5 + - "㎶": [T: "微伏"] # 0x33b6 + - "㎷": [T: "毫伏"] # 0x33b7 + - "㎸": [T: "千伏"] # 0x33b8 + - "㎹": [T: "兆伏"] # 0x33b9 + - "㎺": [T: "皮瓦"] # 0x33ba + - "㎻": [T: "纳瓦"] # 0x33bb + - "㎼": [T: "微瓦"] # 0x33bc + - "㎽": [T: "毫瓦"] # 0x33bd + - "㎾": [T: "千瓦"] # 0x33be + - "㎿": [T: "兆瓦"] # 0x33bf + - "㏀": [T: "千欧"] # 0x33c0 + - "㏁": [T: "兆欧"] # 0x33c1 + - "㏂": [T: "阿米"] # 0x33c2 + - "㏃": [T: "贝可勒尔"] # 0x33c3 + - "㏄": [T: "立方厘米"] # 0x33c4 + - "㏅": [T: "坎德拉"] # 0x33c5 + - "㏆": [T: "库仑每千克"] # 0x33c6 + - "㏇": [T: "大写 C,小写 o,句点"] # 0x33c7 (I have no idea what this is) + - "㏈": [T: "分贝"] # 0x33c8 + - "㏉": [T: "戈瑞"] # 0x33c9 + - "㏊": [T: "公顷"] # 0x33ca + - "㏋": [T: "马力"] # 0x33cb + - "㏌": [T: "英寸"] # 0x33cc + - "㏍": [T: "千开尔文"] # 0x33cd + - "㏎": [T: "千米"] # 0x33ce + - "㏏": [T: "节"] # 0x33cf + - "㏐": [T: "流明"] # 0x33d0 + - "㏑": [T: "自然对数"] # 0x33d1 + - "㏒": [T: "对数"] # 0x33d2 + - "㏓": [T: "勒克斯"] # 0x33d3 + - "㏔": [T: "毫靶恩"] # 0x33d4 + - "㏕": [T: "密耳"] # 0x33d5 + - "㏖": [T: "摩尔"] # 0x33d6 + - "㏗": [T: "pH 值"] # 0x33d7 + - "㏘": [T: "皮米"] # 0x33d8 + - "㏙": [T: "百万分之一"] # 0x33d9 + - "㏚": [T: "拍它伦琴"] # 0x33da + - "㏛": [T: "球面度"] # 0x33db + - "㏜": [T: "希沃特"] # 0x33dc + - "㏝": [T: "韦伯"] # 0x33dd + - "㏞": [T: "伏特每米"] # 0x33de + - "㏟": [T: "安培每米"] # 0x33df + - "㏿": [T: "加仑"] # 0x33ff + - "": [T: "下加帽符的等号"] # 0xe900 + - "": [T: "上加加号的等号"] # 0xe901 + - "": [T: "下加加号的等号"] # 0xe902 + - "": [T: "上加加号的波浪号"] # 0xe903 + - "": [T: "下加加号的波浪号"] # 0xe904 + - "": [T: "大于号上方的双等号"] # 0xe908 + - "": [T: "小于号上方的双等号"] # 0xe909 + - "": [T: "包含"] # 0xe90a + - "": [T: "包含"] # 0xe90b + - "": [T: "包含于"] # 0xe90c + - "": [T: "小于号上方的等号"] # 0xe90d + - "": [T: "属于或等于"] # 0xe912 + - "": [T: "等于或大于"] # 0xe913 + - "": [T: "近似超集符号"] # 0xe914 + - "": [T: "近似子集符号"] # 0xe915 + - "": [T: "带点超集(包含关系)"] # 0xe916 + - "": [T: "带点子集(被包含关系)"] # 0xe917 + - "": [T: "带下加点的等号"] # 0xe918 + - "": [T: "右点上方为减号、减号上方为左点"] # 0xe919 + - "": [T: "左点上方为减号、减号上方为右点"] # 0xe91a + - "": [T: "约等于减号"] # 0xe91f + - "": [T: "双方形并集"] # 0xe920 + - "": [T: "双方形交集"] # 0xe921 + - "": [T: "小于、等于或大于"] # 0xe922 + - "": [T: "带点的波浪号"] # 0xe924 + - "": [T: "带两点的波浪号"] # 0xe925 + - "": [T: "小于、大于或等于"] # 0xe926 + - "": [T: "大于、小于或等于"] # 0xe927 + - "": [T: "等价于或小于"] # 0xe928 + - "": [T: "等价于或大于"] # 0xe929 + - "": [T: "左开口方框运算符"] # 0xe92a + - "": [T: "右开口方框运算符"] # 0xe92b + - "": [T: "带点的恒等于"] # 0xe92c + - "": [T: "大于、等于或小于"] # 0xe92d + - "": [T: "线运算符"] # 0xe92e + - "": [T: "双线运算符"] # 0xe92f + - "": [T: "三重线运算符"] # 0xe930 + - "": [T: "小于或约等于"] # 0xe932 + - "": [T: "大于或约等于"] # 0xe933 + - "": [T: "嵌套小于号"] # 0xe936 + - "": [T: "嵌套大于号"] # 0xe937 + - "": [T: "先于或等价于"] # 0xe93a + - "": [T: "后于或等价于"] # 0xe93b + - "": [T: "等号上方的先于"] # 0xe940 + - "": [T: "等号上方的后于"] # 0xe941 + - "": [T: "小于号、斜等号和大于号"] # 0xe942 + - "": [T: "大于号、斜等号和小于号"] # 0xe943 + - "": [T: "被满足"] # 0xe948 + - "": [T: "卧式s"] # 0xe949 + - "": [T: "否定断言符"] # 0xe94a + - "": [T: "双等号"] # 0xe94b + - "": [T: "三重等号"] # 0xe94c + - "": [T: "延迟规则符号"] # 0xe94d + - "": [T: "别名分隔符"] # 0xe94e + - "": [T: "带线的正规子群"] # 0xe950 + - "": [T: "带横线的正规子群包含符号"] # 0xe951 + - "": [T: "圆头蕴含符号"] # 0xe954 + - "": [T: "线下方的笑弧"] # 0xe955 + - "": [T: "线上方的皱弧"] # 0xe956 + - "": [T: "包含或约等于"] # 0xe957 + - "": [T: "包含于或约等于"] # 0xe958 + - "": [T: "大于、约等于或小于"] # 0xe959 + - "": [T: "小于、约等于或大于"] # 0xe95a + - "": [T: "双逻辑或"] # 0xe95c + - "": [T: "双逻辑与"] # 0xe95d + - "": [T: "下加双横线的逻辑或"] # 0xe95e + - "": [T: "下加横线的逻辑或"] # 0xe95f + - "": [T: "等号上方的约等号"] # 0xe962 + - "": [T: "带平分线的向左三角形"] # 0xe964 + - "": [T: "带平分线的向右三角形"] # 0xe965 + - "": [T: "顶线为点线的等号"] # 0xe966 + - "": [T: "带冒号的先于"] # 0xe967 + - "": [T: "带冒号的后于"] # 0xe968 + - "": [T: "小于或倾斜等于"] # 0xe969 + - "": [T: "大于或倾斜等于"] # 0xe96a + - "": [T: "嵌套远小于号"] # 0xe96b + - "": [T: "嵌套远大于号"] # 0xe96c + - "": [T: "差异符号变体"] # 0xe96d + - "": [T: "小于号与大于号叠加"] # 0xe96e + - "": [T: "逻辑或与逻辑与叠加"] # 0xe96f + - "": [T: "超集上方的超集"] # 0xe970 + - "": [T: "子集上方的子集"] # 0xe971 + - "": [T: "子集上方的超集"] # 0xe972 + - "": [T: "超集上方的子集"] # 0xe973 + - "": [T: "三竖线"] # 0xe979 + - "": [T: "成对四重竖点"] # 0xe97a + - "": [T: "横线上方的垂直符号"] # 0xe97b + - "": [T: "左断言符与双竖线"] # 0xe97c + - "": [T: "双左断言符与双竖线"] # 0xe97d + - "": [T: "倒置垂直符号上方的垂直符号"] # 0xe97e + - "": [T: "双左断言符与竖线"] # 0xe97f + - "": [T: "开口向上的球面角"] # 0xe980 + - "": [T: "双斜杠"] # 0xe981 + - "": [T: "带角标的直角"] # 0xe982 + - "": [T: "带圈竖线"] # 0xe984 + - "": [T: "带圈除号"] # 0xe985 + - "": [T: "虚线斜杠"] # 0xe986 + - "": [T: "虚线反斜杠"] # 0xe987 + - "": [T: "中部虚线"] # 0xe988 + - "": [T: "竖直虚线"] # 0xe989 + - "": [T: "带s的垂直"] # 0xe98a + - "": [T: "带s的角"] # 0xe98b + - "": [T: "开口向左的球面角"] # 0xe98c + - "": [T: "开口向左的角"] # 0xe98d + - "": [T: "带双钩的竖线"] # 0xe98e + - "": [T: "自由基中点运算符"] # 0xe98f + - "": [T: "横线上方的白色向上三角形"] # 0xe990 + - "": [T: "恒等且平行于"] # 0xe991 + - "": [T: "Smash 积"] # 0xe992 + - "": [T: "带水平线的三重线运算符"] # 0xe993 + - "": [T: "带双斜杠的恒等于"] # 0xe994 + - "": [T: "三重交叉横线"] # 0xe995 + - "": [T: "圆圈上方的竖线"] # 0xe996 + - "": [T: "竖式正比于"] # 0xe997 + - "": [T: "黑色下弦月"] # 0xe998 + - "": [T: "黑色上弦月"] # 0xe999 + - "": [T: "负正弦波"] # 0xe9a0 + - "": [T: "带括号的点"] # 0xe9a1 + - "": [T: "圆括号"] # 0xe9a2 + - "": [T: "白色笑弧"] # 0xe9a3 + - "": [T: "白色皱弧"] # 0xe9a4 + - "": [T: "六边形"] # 0xe9a5 + - "": [T: "加号上方的等价号"] # 0xe9a6 + - "": [T: "等价号上方的加号"] # 0xe9a7 + - "": [T: "带衬线交集"] # 0xe9b0 + - "": [T: "带衬线并集"] # 0xe9b1 + - "": [T: "带衬线方形交集"] # 0xe9b2 + - "": [T: "带衬线方形并集"] # 0xe9b3 + - "": [T: "先于、等价于或后于"] # 0xe9e0 + - "": [T: "后于、等价于或先于"] # 0xe9e1 + - "": [T: "先于、约等于或后于"] # 0xe9e2 + - "": [T: "后于、约等于或先于"] # 0xe9e3 + - "": [T: "小于、等价于或大于"] # 0xe9f0 + - "": [T: "大于、等价于或小于"] # 0xe9f1 + - "": [T: "带竖线的非远小于"] # 0xea00 + - "": [T: "带竖线的非远大于"] # 0xea01 + - "": [T: "非远小于变体"] # 0xea02 + - "": [T: "非远大于变体"] # 0xea03 + - "": [T: "带竖线的小于且不等于"] # 0xea04 + - "": [T: "带竖线的大于且不等于"] # 0xea05 + - "": [T: "非小于等于"] # 0xea06 + - "": [T: "非大于等于"] # 0xea07 + - "": [T: "既不等于也不小于"] # 0xea09 + - "": [T: "不包含"] # 0xea0a + - "": [T: "不包含"] # 0xea0b + - "": [T: "不包含于"] # 0xea0c + - "": [T: "带反斜杠的子集符号"] # 0xea0d + - "": [T: "既不等于也不大于"] # 0xea0e + - "": [T: "否定减波浪运算符"] # 0xea0f + - "": [T: "既不等于也不小于"] # 0xea10 + - "": [T: "否定波浪号运算符"] # 0xea11 + - "": [T: "既不属于也不等于"] # 0xea12 + - "": [T: "既不等于也不大于"] # 0xea13 + - "": [T: "不约等于"] # 0xea14 + - "": [T: "不后于或相似于"] # 0xea15 + - "": [T: "带斜线的小于或倾斜等于"] # 0xea16 + - "": [T: "带斜线的大于或倾斜等于"] # 0xea17 + - "": [T: "带斜杠的超集符号"] # 0xea1a + - "": [T: "不包含"] # 0xea1b + - "": [T: "非小于等于"] # 0xea1d + - "": [T: "非大于等于"] # 0xea1e + - "": [T: "不约等于减号"] # 0xea1f + - "": [T: "上加点的否定集合隶属符号"] # 0xea22 + - "": [T: "带竖线的否定角符号"] # 0xea2c + - "": [T: "倾斜不平行符号"] # 0xea2d + - "": [T: "否定横线运算符"] # 0xea2e + - "": [T: "否定双横线运算符"] # 0xea2f + - "": [T: "否定三重横线运算符"] # 0xea30 + - "": [T: "小于但不约等于"] # 0xea32 + - "": [T: "大于但不约等于"] # 0xea33 + - "": [T: "小于或不等于"] # 0xea34 + - "": [T: "大于或不等于"] # 0xea35 + - "": [T: "非嵌套小于号"] # 0xea36 + - "": [T: "非嵌套大于号"] # 0xea37 + - "": [T: "非远小于"] # 0xea38 + - "": [T: "非远大于"] # 0xea39 + - "": [T: "先于但不等价于"] # 0xea3a + - "": [T: "后于但不等价于"] # 0xea3b + - "": [T: "先于但不等于"] # 0xea3c + - "": [T: "后于但不等于"] # 0xea3d + - "": [T: "既不等于也不先于"] # 0xea3e + - "": [T: "既不等于也不后于"] # 0xea3f + - "": [T: "先于但不等于"] # 0xea40 + - "": [T: "后于但不等于"] # 0xea41 + - "": [T: "不包含于"] # 0xea42 + - "": [T: "不包含"] # 0xea43 + - "": [T: "包含于或不等于"] # 0xea44 + - "": [T: "包含或不等于"] # 0xea45 + - "": [T: "不包含于"] # 0xea46 + - "": [T: "不包含"] # 0xea47 + - "": [T: "非三重小于号"] # 0xea48 + - "": [T: "非三重大于号"] # 0xea49 + - "": [T: "不先于且不等于"] # 0xea4c + - "": [T: "不后于且不等于"] # 0xea4d + - "": [T: "带线的非正规子群"] # 0xea50 + - "": [T: "带线的不包含正规子群"] # 0xea51 + - "": [T: "非差符号"] # 0xea52 + - "": [T: "几何不等价于"] # 0xea53 + - "": [T: "带竖线的非相似符号"] # 0xea54 + - "": [T: "不等或相似"] # 0xea55 + - "": [T: "带竖线的非约等符号"] # 0xea56 + - "": [T: "不近似恒等于"] # 0xea57 + - "": [T: "非波形等号"] # 0xea58 + - "": [T: "非波形单等号"] # 0xea59 + - "": [T: "带点的不等号"] # 0xea5a + - "": [T: "反向不等价号"] # 0xea5b + - "": [T: "非方形子集符号"] # 0xea60 + - "": [T: "非方形超集符号"] # 0xea61 + - "": [T: "等号上方的不约等于"] # 0xea62 + - "": [T: "非严格等价于"] # 0xea63 + - "": [T: "带点的不全等符号"] # 0xea64 + - "": [T: "反向不等号"] # 0xea65 + - "": [T: "不竖直左三角形等号"] # 0xea70 + - "": [T: "不竖直右三角形等号"] # 0xea71 + - "": [T: "否定偏微分符号"] # 0xea80 + - "": [T: "箭头修饰符延伸段"] # 0xeb00 + - "": [T: "向左箭头上方的向右箭头"] # 0xeb01 + - "": [T: "向左箭头上方的向右箭头"] # 0xeb02 + - "": [T: "向左鱼叉箭头上方的向右鱼叉箭头"] # 0xeb03 + - "": [T: "向左鱼叉箭头上方的向右鱼叉箭头"] # 0xeb04 + - "": [T: "右上至左下双箭头"] # 0xeb05 + - "": [T: "左上至右下双箭头"] # 0xeb06 + - "": [T: "水平鱼叉箭头延伸段"] # 0xeb07 + - "": [T: "逆时针弧形向左箭头"] # 0xeb08 + - "": [T: "逆时针弧形向右箭头"] # 0xeb09 + - "": [T: "大型向右箭头重音符"] # 0xeb0b + - "": [T: "大型向左箭头重音符"] # 0xeb0c + - "": [T: "左箭头尖"] # 0xeb0d + - "": [T: "右箭头尖"] # 0xeb0e + - "": [T: "带笔画的大左右箭头"] # 0xeb0f + - "": [T: "水平双箭头延伸段"] # 0xeb10 + - "": [T: "带笔画的大左右双箭头"] # 0xeb11 + - "": [T: "上箭头左侧的下箭头"] # 0xeb12 + - "": [T: "带向下折角的向左箭头"] # 0xeb13 + - "": [T: "带向上折角的向右箭头"] # 0xeb14 + - "": [T: "带向上折角的向左箭头"] # 0xeb15 + - "": [T: "带加号的逆时针上半圆箭头"] # 0xeb16 + - "": [T: "带减号的顺时针上半圆箭头"] # 0xeb17 + - "": [T: "带尾部和笔画的向右箭头"] # 0xeb18 + - "": [T: "倒钩向下的向右鱼叉箭头"] # 0xeb19 + - "": [T: "倒钩向下的向左鱼叉箭头"] # 0xeb1a + - "": [T: "倒钩向下的左右鱼叉箭头"] # 0xeb1b + - "": [T: "倒钩向上的左右鱼叉箭头"] # 0xeb1c + - "": [T: "倒钩向左的上下鱼叉箭头"] # 0xeb1d + - "": [T: "倒钩向右的上下鱼叉箭头"] # 0xeb1e + - "": [T: "向下箭头右侧的向上箭头"] # 0xeb1f + - "": [T: "带倒钩向上的指向竖线的左鱼叉箭头"] # 0xeb20 + - "": [T: "带倒钩向上的指向竖线的右鱼叉箭头"] # 0xeb21 + - "": [T: "带倒钩向下的指向竖线的左鱼叉箭头"] # 0xeb22 + - "": [T: "带倒钩向下的指向竖线的右鱼叉箭头"] # 0xeb23 + - "": [T: "带倒钩向上的从竖线出发的左鱼叉箭头"] # 0xeb24 + - "": [T: "带倒钩向上的从竖线出发的右鱼叉箭头"] # 0xeb25 + - "": [T: "带倒钩向下的从竖线出发的左鱼叉箭头"] # 0xeb26 + - "": [T: "带倒钩向下的从竖线出发的右鱼叉箭头"] # 0xeb27 + - "": [T: "带倒钩向左的指向横线的上鱼叉箭头"] # 0xeb28 + - "": [T: "带倒钩向左的指向横线的下鱼叉箭头"] # 0xeb29 + - "": [T: "带倒钩向右的指向横线的上鱼叉箭头"] # 0xeb2a + - "": [T: "带倒钩向右的指向横线的下鱼叉箭头"] # 0xeb2b + - "": [T: "带倒钩向左的从横线出发的上鱼叉箭头"] # 0xeb2c + - "": [T: "带倒钩向左的从横线出发的下鱼叉箭头"] # 0xeb2d + - "": [T: "带倒钩向右的从横线出发的上鱼叉箭头"] # 0xeb2e + - "": [T: "带倒钩向右的从横线出发的下鱼叉箭头"] # 0xeb2f + - "": [T: "指向横线的向上箭头"] # 0xeb30 + - "": [T: "指向横线的向下箭头"] # 0xeb31 + - "": [T: "下鱼叉箭头左侧的上鱼叉箭头"] # 0xeb32 + - "": [T: "下鱼叉箭头右侧的上鱼叉箭头"] # 0xeb33 + - "": [T: "上箭头尖"] # 0xeb34 + - "": [T: "下箭头尖"] # 0xeb35 + - "": [T: "带左倒钩下右倒钩上的双鱼叉箭头"] # 0xeb36 + - "": [T: "带左倒钩上右倒钩下的双鱼叉箭头"] # 0xeb37 + - "": [T: "线上方的向左箭头"] # 0xeb38 + - "": [T: "线上方的向右箭头"] # 0xeb39 + - "": [T: "线下方的向左箭头"] # 0xeb3a + - "": [T: "线下方的向右箭头"] # 0xeb3b + - "": [T: "左右三重箭头"] # 0xeb3c + - "": [T: "双箭头右上右下"] # 0xeb3f + - "": [T: "逆时针左半圆箭头"] # 0xeb40 + - "": [T: "顺时针左半圆箭头"] # 0xeb41 + - "": [T: "左侧开口圆圈的左右箭头"] # 0xeb42 + - "": [T: "波浪号上方的向右箭头"] # 0xeb44 + - "": [T: "波浪号上方的向左箭头"] # 0xeb45 + - "": [T: "线上方的左鱼叉箭头"] # 0xeb48 + - "": [T: "线上方的右鱼叉箭头"] # 0xeb49 + - "": [T: "线下方的左鱼叉箭头"] # 0xeb4a + - "": [T: "线下方的右鱼叉箭头"] # 0xeb4b + - "": [T: "短粗黑色向左箭头"] # 0xeb4c + - "": [T: "顺时针右半圆箭头"] # 0xeb50 + - "": [T: "逆时针右半圆箭头"] # 0xeb51 + - "": [T: "左侧开口圆圈的左右鱼叉箭头"] # 0xeb52 + - "": [T: "竖线左侧的向上箭头"] # 0xeb58 + - "": [T: "竖线左侧的向下箭头"] # 0xeb59 + - "": [T: "竖线右侧的向上箭头"] # 0xeb5a + - "": [T: "竖线右侧的向下箭头"] # 0xeb5b + - "": [T: "带延长下钩的向右箭头"] # 0xeb5c + - "": [T: "带延长钩的向左箭头"] # 0xeb5d + - "": [T: "带延长下钩的向左箭头"] # 0xeb5e + - "": [T: "带延长钩的向右箭头"] # 0xeb5f + - "": [T: "非波浪形向右箭头"] # 0xeb60 + - "": [T: "非弯曲向右箭头"] # 0xeb61 + - "": [T: "竖线左侧的向上鱼叉箭头"] # 0xeb68 + - "": [T: "竖线左侧的向下鱼叉箭头"] # 0xeb69 + - "": [T: "竖线右侧的向上鱼叉箭头"] # 0xeb6a + - "": [T: "竖线右侧的向下鱼叉箭头"] # 0xeb6b + - "": [T: "竖直双箭头延伸段"] # 0xeb6c + - "": [T: "倒钩向左的竖直鱼叉箭头延伸段"] # 0xeb6d + - "": [T: "倒钩向右的竖直鱼叉箭头延伸段"] # 0xeb6e + - "": [T: "左鱼叉箭头右部上方的右鱼叉箭头"] # 0xeb6f + - "": [T: "左鱼叉箭头左部上方的右鱼叉箭头"] # 0xeb70 + - "": [T: "右鱼叉箭头右部上方的左鱼叉箭头"] # 0xeb71 + - "": [T: "右鱼叉箭头左部上方的左鱼叉箭头"] # 0xeb72 + - "": [T: "从竖线出发的向左箭头尖"] # 0xeb73 + - "": [T: "从竖线出发的左右箭头延伸段"] # 0xeb74 + - "": [T: "从竖线出发的向左箭头尾部"] # 0xeb75 + - "": [T: "从竖线出发的向右箭头尾部"] # 0xeb76 + - "": [T: "从竖线出发的向右箭头尖"] # 0xeb77 + - "": [T: "带倒钩左箭头尖的从横线出发的上鱼叉箭头"] # 0xeb78 + - "": [T: "向左箭头右部上方的向右箭头"] # 0xeb79 + - "": [T: "向左箭头左部上方的向右箭头"] # 0xeb7a + - "": [T: "向右箭头右部上方的向左箭头"] # 0xeb7b + - "": [T: "向右箭头左部上方的向左箭头"] # 0xeb7c + - "": [T: "从横线出发的向上箭头尖"] # 0xeb7d + - "": [T: "从横线出发的向上箭头尾部"] # 0xeb7e + - "": [T: "从横线出发的向下箭头尾部"] # 0xeb7f + - "": [T: "从横线出发的向下箭头尖"] # 0xeb80 + - "": [T: "带倒钩右箭头尖的从横线出发的下鱼叉箭头"] # 0xeb81 + - "": [T: "下鱼叉箭头底部左侧的上鱼叉箭头"] # 0xeb82 + - "": [T: "下鱼叉箭头延伸段左侧的上鱼叉箭头"] # 0xeb83 + - "": [T: "上鱼叉箭头顶部左侧的下鱼叉箭头"] # 0xeb84 + - "": [T: "下鱼叉箭头顶部左侧的上鱼叉箭头"] # 0xeb85 + - "": [T: "上鱼叉箭头延伸段左侧的下鱼叉箭头"] # 0xeb86 + - "": [T: "上鱼叉箭头底部左侧的下鱼叉箭头"] # 0xeb87 + - "": [T: "向下箭头底部左侧的向上箭头"] # 0xeb88 + - "": [T: "向上箭头顶部左侧的向下箭头"] # 0xeb89 + - "": [T: "向下箭头顶部左侧的向上箭头"] # 0xeb8a + - "": [T: "向上箭头底部左侧的向下箭头"] # 0xeb8b + - "": [T: "左右箭头延伸段"] # 0xeb8c + - "": [T: "右上箭头延伸段"] # 0xeb8d + - "": [T: "左上箭头延伸段"] # 0xeb8e + - "": [T: "向下的花括号左部"] # 0xec00 + - "": [T: "向下的花括号中部"] # 0xec01 + - "": [T: "向下的花括号右部"] # 0xec02 + - "": [T: "水平花括号延伸段"] # 0xec03 + - "": [T: "向上的花括号左部"] # 0xec04 + - "": [T: "向上的花括号中部"] # 0xec05 + - "": [T: "向上的花括号右部"] # 0xec06 + - "": [T: "左竖线"] # 0xec07 + - "": [T: "右竖线"] # 0xec08 + - "": [T: "左双竖线"] # 0xec09 + - "": [T: "右双竖线"] # 0xec0a + - "": [T: "水平方括号延伸段"] # 0xec0b + - "": [T: "下方形方括号"] # 0xec0c + - "⎵": [T: "下方形方括号"] # 0x23b5 + - "": [T: "上方形方括号"] # 0xec0d + - "⎴": [T: "上方形方括号"] # 0x23b4 + - "": [T: "下方方括号左部"] # 0xec0e + - "": [T: "下方方括号右部"] # 0xec0f + - "": [T: "上方方括号左部"] # 0xec10 + - "": [T: "上方方括号右部"] # 0xec11 + - "": [T: "左圆括号部件一"] # 0xec12 + - "": [T: "左圆括号部件二"] # 0xec13 + - "": [T: "左圆括号部件三"] # 0xec14 + - "": [T: "左圆括号部件四"] # 0xec15 + - "": [T: "右圆括号部件一"] # 0xec16 + - "": [T: "右圆括号部件二"] # 0xec17 + - "": [T: "右圆括号部件三"] # 0xec18 + - "": [T: "右圆括号部件四"] # 0xec19 + - "": [T: "根号部件一"] # 0xec1a + - "": [T: "根号部件二"] # 0xec1b + - "": [T: "根号部件三"] # 0xec1c + - "": [T: "根号部件四"] # 0xec1d + - "": [T: "根号部件五"] # 0xec1e + - "": [T: "根号底部"] # 0xec1f + - "": [T: "根号竖直延伸段"] # 0xec20 + - "": [T: "根号顶部"] # 0xec21 + - "": [T: "左白色方括号顶部"] # 0xec22 + - "": [T: "左白色方括号延伸段"] # 0xec23 + - "": [T: "左白色方括号底部"] # 0xec24 + - "": [T: "右白色方括号顶部"] # 0xec25 + - "": [T: "右白色方括号延伸段"] # 0xec26 + - "": [T: "右白色方括号底部"] # 0xec27 + - "": [T: "左白色花括号"] # 0xec30 + - "": [T: "右白色花括号"] # 0xec31 + - "": [T: "长除号"] # 0xec32 + - "": [T: "长除号延伸段"] # 0xec33 + - "": [T: "短除号"] # 0xec34 + - "": [T: "左下至右上全长双键"] # 0xec40 + - "": [T: "左上至右下全长双键"] # 0xec41 + - "": [T: "水平全长单键"] # 0xec42 + - "": [T: "水平全长双键"] # 0xec43 + - "": [T: "水平全长三键"] # 0xec44 + - "": [T: "竖直全长单键"] # 0xec45 + - "": [T: "竖直全长双键"] # 0xec46 + - "": [T: "竖直全长三键"] # 0xec47 + - "": [T: "小于号形全长键"] # 0xec48 + - "": [T: "大于号形全长键"] # 0xec49 + - "": [T: "水平半长单键"] # 0xec4a + - "": [T: "水平半长双键"] # 0xec4b + - "": [T: "水平半长三键"] # 0xec4c + - "": [T: "矩形左上部"] # 0xec80 + - "": [T: "矩形左下部"] # 0xec81 + - "": [T: "矩形右上部"] # 0xec90 + - "": [T: "矩形右下部"] # 0xec91 + - "": [T: "综合除法角部"] # 0xec92 + - "": [T: "综合除法水平延伸段"] # 0xec93 + - "": [T: "综合除法竖直延伸段"] # 0xec94 + - "": [T: "左侧上下取整符号延伸段"] # 0xec95 + - "": [T: "右侧上下取整符号延伸段"] # 0xec96 + - "": [T: "上方括号延伸段"] # 0xec97 + - "": [T: "竖线延伸段"] # 0xec98 + - "": [T: "左双竖线延伸段"] # 0xec99 + - "": [T: "横线延伸段"] # 0xec9a + - "": [T: "下方括号延伸段"] # 0xec9c + - "": [T: "向下的圆括号右部"] # 0xec9d + - "": [T: "向下的圆括号延伸段"] # 0xec9e + - "": [T: "向下的圆括号左部"] # 0xec9f + - "": [T: "向上的花括号延伸段"] # 0xeca0 + - "": [T: "向上的圆括号左部"] # 0xeca1 + - "": [T: "向上的圆括号延伸段"] # 0xeca2 + - "": [T: "向上的圆括号右部"] # 0xeca3 + - "": [T: "向下的花括号延伸段"] # 0xeca4 + - "": [T: "约化普朗克常数"] # 0xed00 + - "": [T: "镜像g"] # 0xed01 + - "": [T: "无点 j"] # 0xed02 + - "": [T: "双伽马"] # 0xed03 + - "ϝ": [T: "双伽马"] # 0x3dd + - "": [T: "d"] # 0xed10 + - "ⅆ": [T: "d"] # 0x2146 + - "": [T: "e"] # 0xed11 + - "ⅇ": [T: "e"] # 0x2147 + - "": [T: "i"] # 0xed12 + - "ⅈ": [T: "i"] # 0x2148 + - "": [T: "j"] # 0xed13 + - "ⅅ": + - SPELL: "translate('.', 'ⅅ', 'DD')" # 0xed16, 0x2145 + +# The private use chars are from MathType + - "": [T: "逆时针围道积分环"] # 0xee00 + - "": [T: "顺时针围道积分环"] # 0xee01 + - "": [T: ""] # 0xee04 + - "": [T: ""] # 0xee05 + - "": [T: ""] # 0xee06 + - "": [T: ""] # 0xee07 + - "": [T: ""] # 0xee08 + - "": [T: ""] # 0xee09 + - "": [T: ""] # 0xee0a + - "": [T: ""] # 0xee0b + - "": [T: ""] # 0xee0c + - "": [T: "连接状态修饰符"] # 0xee0d + - "": [T: "连接状态左侧修饰符"] # 0xee0e + - "": [T: "连接状态右侧修饰符"] # 0xee0f + - "": [T: "连接状态延伸段"] # 0xee10 + - "": [T: "积分环"] # 0xee11 + - "": [T: "双积分环"] # 0xee12 + - "": [T: "三重积分环"] # 0xee13 + - "": [T: "双扩展积分环"] # 0xee15 + - "": [T: "三重扩展积分环"] # 0xee16 + - "": [T: "渐近等于重音符"] # 0xee17 + - "": [T: "等号重音符"] # 0xee18 + - "": [T: "四撇号"] # 0xee19 + - "": [T: "带开口圆圈左部的横线重音符"] # 0xee1a + - "": [T: "带闭合圆圈左部的横线重音符"] # 0xee1b + - "": [T: "带开口圆圈右部的横线重音符"] # 0xee1c + - "": [T: "带上点的横线重音符"] # 0xee1d + - "": [T: "带下点的横线重音符"] # 0xee1e + - "": [T: "带上方双点的横线重音符"] # 0xee1f + - "": [T: "带下方双点的横线重音符"] # 0xee20 + - "": [T: "带脱字符的横线重音符"] # 0xee21 + - "": [T: "粗下横线重音符"] # 0xee22 + - "": [T: "带闭合圆圈右部的横线重音符"] # 0xee23 + - "": [T: "上加大点"] # 0xee24 + - "": [T: "对齐标记"] # 0xef00 + - "": [T: ""] # 0xef01 + - "​": [T: ""] # 0x200b + - "": [T: ""] # 0xef02 + - " ": [T: ""] # 0x2009 + - "": [T: ""] # 0xef03 + - " ": [T: ""] # 0x205f + - "": [T: ""] # 0xef04 + - "": [T: ""] # 0xef05 + - "": [T: ""] # 0xef06 + - "": [T: ""] # 0xef07 + - "": [T: ""] # 0xef08 + - "": [T: ""] # 0xef09 + - "": [T: ""] # 0xef0a + - " ": [T: ""] # 0x200a + - "": [T: ""] # 0xef22 + - "": [T: ""] # 0xef23 + - "": [T: ""] # 0xef24 + - "": [T: ""] # 0xef29 + - "": [T: "缺项"] # 0xef41 + - "": [T: "左侧带箭头的顺时针围道积分"] # 0xef80 + - "": [T: "带方形的积分号"] # 0xef81 + - "": [T: "带斜杠的积分号"] # 0xef82 + - "": [T: "反向积分号"] # 0xef83 + - "": [T: "双零上方的双零"] # 0xef90 + - "": [T: "带斜线的零"] # 0xef91 + + # fraktur chars in math alphabetic block and also MathType private use area + # Some of these are reserved because they were used in Plane 0 -- that shouldn't be an issue other than causing the other chars to not display + - "𝔄-𝔜": # 0x1d504 - 0x1d51d ('z' version is reserved) + - T: "哥特体" + - SPELL: "translate('.', '𝔄𝔅𝔆𝔇𝔈𝔉𝔊𝔋𝔌𝔍𝔎𝔏𝔐𝔑𝔒𝔓𝔔𝔕𝔖𝔗𝔘𝔙𝔚𝔛𝔜', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" + + - "-": # 0xf000 - 0xf018 + - T: "哥特体" + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXY')" + + - "𝔞-𝔷": # 0x1d51e - 0x1d537 + - T: "哥特体" + - SPELL: "translate('.', '𝔞𝔟𝔠𝔡𝔢𝔣𝔤𝔥𝔦𝔧𝔨𝔩𝔪𝔫𝔬𝔭𝔮𝔯𝔰𝔱𝔲𝔳𝔴𝔵𝔶𝔷', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf01a - 0xf033 + - T: "哥特体" + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝕬-𝖅": # 0x1D56C - 0x1D585 + - T: "哥特体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝕬𝕭𝕮𝕯𝕰𝕱𝕲𝕳𝕴𝕵𝕶𝕷𝕸𝕹𝕺𝕻𝕼𝕽𝕾𝕿𝖀𝖁𝖂𝖃𝖄𝖅', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf040 - 0xf059 + - T: "哥特体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖆-𝖟": # 0x1d586 - 0x1d59f + - T: "哥特体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝖆𝖇𝖈𝖉𝖊𝖋𝖌𝖍𝖎𝖏𝖐𝖑𝖒𝖓𝖔𝖕𝖖𝖗𝖘𝖙𝖚𝖛𝖜𝖝𝖞𝖟', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf05a - 0xf073 + - T: "哥特体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + # double struck (blackboard bold) chars in math alphabetic block and also MathType private use area + # Some of these are reserved because they were used in Plane 0 -- that shouldn't be an issue other than causing the other chars to not display + - "𝔸-𝕐": # 0x1d504 - 0x1d51d ('z' version is reserved) + - T: "双线体" + - SPELL: "translate('.', '𝔸𝔹𝔺𝔻𝔼𝔽𝔾𝔿𝕀𝕁𝕂𝕃𝕄𝕅𝕆𝕇𝕈𝕉𝕊𝕋𝕌𝕍𝕎𝕏𝕐', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf080 - 0xf098 + - T: "双线体" + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝕒-𝕫": # 0x1d552 - 0x1d56b + - T: "双线体" + - SPELL: "translate('.', '𝕒𝕓𝕔𝕕𝕖𝕗𝕘𝕙𝕚𝕛𝕜𝕝𝕞𝕟𝕠𝕡𝕢𝕣𝕤𝕥𝕦𝕧𝕨𝕩𝕪𝕫', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf09a - 0xf0b3 + - T: "双线体" + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝟘-𝟡": # 0x1d7d8 - 0x1d7e1 + - T: "双线体" + - SPELL: "translate('.', '𝟘𝟙𝟚𝟛𝟜𝟝𝟞𝟟𝟠𝟡', '0123456789')" + - "-": # 0xf0c0 - 0xf0c9 + - T: "双线体" + - SPELL: "translate('.', '', '0123456789')" + + - "": [T: "双线体纳布拉"] # 0xf0ca + - "": [T: "双线体欧拉常数"] # 0xf0cb + + # script chars in math alphabetic block and also MathType private use area + - "𝒜-𝒵": # 0x1d49c - 0x1d4b5 + - T: "花体" + - SPELL: "translate('.', '𝒜𝒝𝒞𝒟𝒠𝒡𝒢𝒣𝒤𝒥𝒦𝒧𝒨𝒩𝒪𝒫𝒬𝒭𝒮𝒯𝒰𝒱𝒲𝒳𝒴𝒵', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf100 - 0xf119 + - T: "花体" + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝒶-𝓏": # 0x1d4b6 - 0x1d4cf + - T: "花体" + - SPELL: "translate('.', '𝒶𝒷𝒸𝒹𝒺𝒻𝒼𝒽𝒾𝒿𝓀𝓁𝓂𝓃𝓄𝓅𝓆𝓇𝓈𝓉𝓊𝓋𝓌𝓍𝓎𝓏', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf11a - 0xf133 + - T: "花体" + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + # bold script chars in math alphabetic block and also MathType private use area + - "𝓐-𝓩": # 0x1d4d0 - 0x1d4e9 + - T: "花体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝓐𝓑𝓒𝓓𝓔𝓕𝓖𝓗𝓘𝓙𝓚𝓛𝓜𝓝𝓞𝓟𝓠𝓡𝓢𝓣𝓤𝓥𝓦𝓧𝓨𝓩', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf140 - 0xf159 + - T: "花体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝓪-𝔃": # 0x1d4ea - 0x1d503 + - T: "花体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝓪𝓫𝓬𝓭𝓮𝓯𝓰𝓱𝓲𝓳𝓴𝓵𝓶𝓷𝓸𝓹𝓺𝓻𝓼𝓽𝓾𝓿𝔀𝔁𝔂𝔃', 'abcdefghijklmnopqrstuvwxyz')" + - "-": # 0xf15a - 0xf173 + - T: "花体" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf180 - 0xf199 + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "": # 0xf19a + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "ae 连字"] + - "": # 0xf19b + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "尖音 s"] + - "": # 0xf19c + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: [T: "带斜线的 o"] + + # MathType only has a few of the cap Greek letters in PUA + - "": # 0xf201 - 0xf209 + - T: "双线体" + - SPELL: "translate('.', '', 'ΔΨΛΠΣΘΓΩΥ')" + + - "-": # 0xf220 - 0xf236 + - T: "双线体" + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "": [T: "双线体末尾西格马"] # 0xf237 + - "": [T: "双线体柔"] # 0xf250 + - "": [T: "双线体斐"] # 0xf251 + - "𝐀-𝐙": # 0x1d400 - 0x1d419 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝐀𝐁𝐂𝐃𝐄𝐅𝐆𝐇𝐈𝐉𝐊𝐋𝐌𝐍𝐎𝐏𝐐𝐑𝐒𝐓𝐔𝐕𝐖𝐗𝐘𝐙', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf260 - 0xf279 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝐚-𝐳": # 0x1d41a - 0x1d433 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝐚𝐛𝐜𝐝𝐞𝐟𝐠𝐡𝐢𝐣𝐤𝐥𝐦𝐧𝐨𝐩𝐪𝐫𝐬𝐭𝐮𝐯𝐰𝐱𝐲𝐳', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf27a - 0xf293 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝐴-𝑍": # 0x1d434 - 0x1d44d + - SPELL: "translate('.', '𝐴𝐵𝐶𝐷𝐸𝐹𝐺𝐻𝐼𝐽𝐾𝐿𝑀𝑁𝑂𝑃𝑄𝑅𝑆𝑇𝑈𝑉𝑊𝑋𝑌𝑍', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf294 - 0xf2ad + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝑎-𝑧": # 0x1d44e - 0x1d467 + - SPELL: "translate('.', '𝑎𝑏𝑐𝑑𝑒𝑓𝑔𝑕𝑖𝑗𝑘𝑙𝑚𝑛𝑜𝑝𝑞𝑟𝑠𝑡𝑢𝑣𝑤𝑥𝑦𝑧', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf2ae - 0xf2c7 + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝑨-𝒁": # 0x1d468 - 0x1d481 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝑨𝑩𝑪𝑫𝑬𝑭𝑮𝑯𝑰𝑱𝑲𝑳𝑴𝑵𝑶𝑷𝑸𝑹𝑺𝑻𝑼𝑽𝑾𝑿𝒀𝒁', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf2c8 - 0xf2e1 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝒂-𝒛": # 0x1d482 - 0x1d49b + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝒂𝒃𝒄𝒅𝒆𝒇𝒈𝒉𝒊𝒋𝒌𝒍𝒎𝒏𝒐𝒑𝒒𝒓𝒔𝒕𝒖𝒗𝒘𝒙𝒚𝒛', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf2e2 - 0xf2fb + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝖠-𝖹": # 0x1d5a0 - 0x1d5b9 + - SPELL: "translate('.', '𝖠𝖡𝖢𝖣𝖤𝖥𝖦𝖧𝖨𝖩𝖪𝖫𝖬𝖭𝖮𝖯𝖰𝖱𝖲𝖳𝖴𝖵𝖶𝖷𝖸𝖹', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf300 - 0xf319 + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝖺-𝗓": # 0x1d5ba - 0x1d5d3 + - SPELL: "translate('.', '𝖺𝖻𝖼𝖽𝖾𝖿𝗀𝗁𝗂𝗃𝗄𝗅𝗆𝗇𝗈𝗉𝗊𝗋𝗌𝗍𝗎𝗏𝗐𝗑𝗒𝗓', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf31a - 0xf333 + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝗔-𝗭": # 0x1d5d4 - 0x1d5ed + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝗔𝗕𝗖𝗗𝗘𝗙𝗚𝗛𝗜𝗝𝗞𝗟𝗠𝗡𝗢𝗣𝗤𝗥𝗦𝗧𝗨𝗩𝗪𝗫𝗬𝗭', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf334 - 0xf34d + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝗮-𝘇": # 0x1d5ee - 0x1d607 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝗮𝗯𝗰𝗱𝗲𝗳𝗴𝗵𝗶𝗷𝗸𝗹𝗺𝗻𝗼𝗽𝗾𝗿𝘀𝘁𝘂𝘃𝘄𝘅𝘆𝘇', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf34e - 0xf367 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + - "𝘈-𝘡": # 0x1d608 - 0x1d621 + # - t: "italic" + - SPELL: "translate('.', '𝘈𝘉𝘊𝘋𝘌𝘍𝘎𝘏𝘐𝘑𝘒𝘓𝘔𝘕𝘖𝘗𝘘𝘙𝘚𝘛𝘜𝘝𝘞𝘟𝘠𝘡', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + - "-": # 0xf368 - 0xf381 + # - t: "italic" + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝘢-𝘻": # 0x1d622 - 0x1d63b + # - t: "italic" + - SPELL: "translate('.', '𝘢𝘣𝘤𝘥𝘦𝘧𝘨𝘩𝘪𝘫𝘬𝘭𝘮𝘯𝘰𝘱𝘲𝘳𝘴𝘵𝘶𝘷𝘸𝘹𝘺𝘻', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf382 - 0xf39b + # - t: "italic" + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝘼-𝙕": # 0x1d63c - 0x1d655 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝘼𝘽𝘾𝘿𝙀𝙁𝙂𝙃𝙄𝙅𝙆𝙇𝙈𝙉𝙊𝙋𝙌𝙍𝙎𝙏𝙐𝙑𝙒𝙓𝙔𝙕', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf39c - 0xf3b5 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝙖-𝙯": # 0x1d656 - 0x1d66f + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝙖𝙗𝙘𝙙𝙚𝙛𝙜𝙝𝙞𝙟𝙠𝙡𝙢𝙣𝙤𝙥𝙦𝙧𝙨𝙩𝙪𝙫𝙬𝙭𝙮𝙯', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf3b6 - 0xf3cf + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "𝙰-𝚉": # 0x1d670 - 0x1d689 + - SPELL: "translate('.', '𝙰𝙱𝙲𝙳𝙴𝙵𝙶𝙷𝙸𝙹𝙺𝙻𝙼𝙽𝙾𝙿𝚀𝚁𝚂𝚃𝚄𝚅𝚆𝚇𝚈𝚉', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "-": # 0xf3d0 - 0xf3e9 + - SPELL: "translate('.', '', 'ABCDEFGHIJKLMNOPQRSTUVWXYZ')" + + - "𝚊-𝚣": # 0x1d68a - 0x1d6a3 + - SPELL: "translate('.', '𝚊𝚋𝚌𝚍𝚎𝚏𝚐𝚑𝚒𝚓𝚔𝚕𝚖𝚗𝚘𝚙𝚚𝚛𝚜𝚝𝚞𝚟𝚠𝚡𝚢𝚣', 'abcdefghijklmnopqrstuvwxyz')" + + - "-": # 0xf3ea - 0xf403 + - SPELL: "translate('.', '', 'abcdefghijklmnopqrstuvwxyz')" + + - "": [T: "无点 i"] # 0xf404 + - "𝚤": [T: "无点 i"] # 0x1d6a4 + - "𝚥": [T: "无点 j"] # 0x1d6a5 + + - "𝚨-𝛀": # 0x1d6a8 - 0x1d6c0 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝚨𝚩𝚪𝚫𝚬𝚭𝚮𝚯𝚰𝚱𝚲𝚳𝚴𝚵𝚶𝚷𝚸𝚹𝚺𝚻𝚼𝚽𝚾𝚿𝛀', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "-": # 0xf408 - 0xf420 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝛂-𝛚": # 0x1d6c2 - 0x1d6da + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝛂𝛃𝛄𝛅𝛆𝛇𝛈𝛉𝛊𝛋𝛌𝛍𝛎𝛏𝛐𝛑𝛒𝛓𝛔𝛕𝛖𝛗𝛘𝛙𝛚', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf422 - 0xf43a + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "": [T: "粗体纳布拉"] # 0xf421 + - "𝛁": [T: "粗体纳布拉"] # 0x1d6c1 + + - "𝛛𝛜𝛝𝛞𝛟𝛠𝛡": # 0x1D6DB - 0x1D6E1 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝛛𝛜𝛝𝛞𝛟𝛠𝛡', '∂εθκφρπ')" + + - "": # 0xF43C - 0xF441 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '∂εθκφρπ')" + + - "𝛢-𝛺": # 0x1d6e2 - 0x1d6fa + # - t: "italic" + - SPELL: "translate('.', '𝛢𝛣𝛤𝛥𝛦𝛧𝛨𝛩𝛪𝛫𝛬𝛭𝛮𝛯𝛰𝛱𝛲𝛳𝛴𝛵𝛶𝛷𝛸𝛹𝛺', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "-": # 0xf442 - 0xf45a + # - t: "italic" + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝛼-𝜔": # 0x1d6fc - 0x1d714 + # - t: "italic" + - SPELL: "translate('.', '𝛼𝛽𝛾𝛿𝜀𝜁𝜂𝜃𝜄𝜅𝜆𝜇𝜈𝜉𝜊𝜋𝜌𝜍𝜎𝜏𝜐𝜑𝜒𝜓𝜔', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf45c - 0xf474 + # - t: "italic" + - SPELL: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "": [T: "斜体纳布拉"] # 0xf45b + - "𝛻": [T: "斜体纳布拉"] # 0x1d6fb + + - "𝜕𝜖𝜗𝜘𝜙𝜚𝜛": # 0x1d715 - 0x1d71b + # - t: "italic" + - SPELL: "translate('.', '𝜕𝜖𝜗𝜘𝜙𝜚𝜛', '∂εθκφρπ')" + + - "": # 0xf475 - 0xf47b + # - t: "italic" + - SPELL: "translate('.', '', '∂εθκφρπ')" + + - "𝜜-𝜴": # 0x1d71c - 0x1d734 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝜜𝜝𝜞𝜟𝜠𝜡𝜢𝜣𝜤𝜥𝜦𝜧𝜨𝜩𝜪𝜫𝜬𝜭𝜮𝜯𝜰𝜱𝜲𝜳𝜴', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "-": # 0xf47c - 0xf494 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝜶-𝝎": # 0x1d736 - 0x1d74e + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝜶𝜷𝜸𝜹𝜺𝜻𝜼𝜽𝜾𝜿𝝀𝝁𝝂𝝃𝝄𝝅𝝆𝝇𝝈𝝉𝝊𝝋𝝌𝝍𝝎', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf496 - 0xf4ae + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝝏𝝐𝝑𝝒𝝓𝝔𝝕": # 0x1d74f - 0x1d755 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝝏𝝐𝝑𝝒𝝓𝝔𝝕', '∂εθκφρπ')" + + - "": # 0xf422 - 0xf43a + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '∂εθκφρπ')" + + - "𝜵": [T: "粗斜体纳布拉"] # 0x1d735 + - "": [T: "粗斜体纳布拉"] # 0xf495 + + - "𝝖-𝝮": # 0x1d756 - 0x1d76e + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝝖𝝗𝝘𝝙𝝚𝝛𝝜𝝝𝝞𝝟𝝠𝝡𝝢𝝣𝝤𝝥𝝦𝝧𝝨𝝩𝝪𝝫𝝬𝝭𝝮', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + - "-": # 0xf4b6 - 0xf4ce + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝝰-𝞈": # 0x1d770 - 0x1d788 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝝰𝝱𝝲𝝳𝝴𝝵𝝶𝝷𝝸𝝹𝝺𝝻𝝼𝝽𝝾𝝿𝞀𝞁𝞂𝞃𝞄𝞅𝞆𝞇𝞈', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf4d0 - 0xf4e8 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝞉𝞊𝞋𝞌𝞍𝞎𝞏": # 0x1d789 - 0x1d78f + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝞉𝞊𝞋𝞌𝞍𝞎𝞏', '∂εθκφρπ')" + + - "": # 0xf4e9 - 0xf4ef + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '∂εθκφρπ')" + + - "": [T: "粗体纳布拉"] # 0xf4cf + - "𝝯": [T: "粗体纳布拉"] # 0x1d76f + + - "𝞐-𝞨": # 0x1d790 - 0x1d7a8 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝞐𝞑𝞒𝞓𝞔𝞕𝞖𝞗𝞘𝞙𝞚𝞛𝞜𝞝𝞞𝞟𝞠𝞡𝞢𝞣𝞤𝞥𝞦𝞧𝞨', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "-": # 0xf4f0 - 0xf508 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ')" + + - "𝞪-𝟂": # 0x1d7aa - 0x1d7c2 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝞪𝞫𝞬𝞭𝞮𝞯𝞰𝞱𝞲𝞳𝞴𝞵𝞶𝞷𝞸𝞹𝞺𝞻𝞼𝞽𝞾𝞿𝟀𝟁𝟂', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "-": # 0xf50a - 0xf522 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', 'αβγδεζηθικλμνξοπρςστυφχψω')" + + - "𝟃𝟄𝟅𝟆𝟇𝟈𝟉": # 0x1d7c3 - 0x1d7c9 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝟃𝟄𝟅𝟆𝟇𝟈𝟉', '∂εθκφρπ')" + + - "": # 0xf523 - 0xf529 + # - t: "bold italic" + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '∂εθκφρπ')" + + - "": [T: "粗体纳布拉"] # 0xf509 + - "𝞩": [T: "粗体纳布拉"] # 0x1d7a9 + + - "-": # 0xf52e - 0xf537 (old MathType) + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '0123456789')" + + - "𝟎-𝟗": # 0x1d7ce - 0x1d7d7 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝟎𝟏𝟐𝟑𝟒𝟓𝟔𝟕𝟖𝟗', '0123456789')" + + - "-": # 0xf52e - 0xf537 (old MathType) + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '', '0123456789')" + + - "𝟬-𝟵": # 0x1D7EC - 0x1D7F5 + - test: + if: "not($IgnoreBold)" + then: [T: "粗体"] + - SPELL: "translate('.', '𝟬𝟭𝟮𝟯𝟰𝟱𝟲𝟳𝟴𝟵', '0123456789')" + + - "-": # 0xf556 - 0xf55f (old MathType) + - SPELL: "translate('.', '', '0123456789')" + + - "𝟢-𝟫": # 0x1d7e2 - 0x1d7eb + - SPELL: "translate('.', '𝟢𝟣𝟤𝟥𝟦𝟧𝟨𝟩𝟪𝟫', '0123456789')" + + + - "𝟶-𝟿": # 0x1d7f6 - 0x1d7ff + - SPELL: "translate('.', '𝟶𝟷𝟸𝟹𝟺𝟻𝟼𝟽𝟾𝟿', '0123456789')" + + + - "": [T: "未知字符"] # 0xf700 + - "": [T: "左下和右下三角形"] # 0xf726 + - "": [T: "水平省略号延伸段"] # 0xf72d + - "": [T: "中线水平省略号延伸段"] # 0xf72e + - "": [T: "根号延伸段"] # 0xf8e5 + - "": [T: "竖直箭头延伸段"] # 0xf8e6 + - "": [T: "水平箭头延伸段"] # 0xf8e7 + - "": [T: "无衬线体注册符号"] # 0xf8e8 + - "": [T: "无衬线体版权符号"] # 0xf8e9 + - "": [T: "无衬线体商标符号"] # 0xf8ea + - "": [T: "左圆括号顶部"] # 0xf8eb + - "": [T: "左圆括号延伸段"] # 0xf8ec + - "": [T: "左圆括号底部"] # 0xf8ed + - "": [T: "左方括号顶部"] # 0xf8ee + - "": [T: "左方括号延伸段"] # 0xf8ef + - "": [T: "左方括号底部"] # 0xf8f0 + - "": [T: "左花括号顶部"] # 0xf8f1 + - "": [T: "左花括号中部"] # 0xf8f2 + - "": [T: "左花括号底部"] # 0xf8f3 + - "": [T: "花括号延伸段"] # 0xf8f4 + - "": [T: "积分号延伸段"] # 0xf8f5 + - "": [T: "右圆括号顶部"] # 0xf8f6 + - "": [T: "右圆括号延伸段"] # 0xf8f7 + - "": [T: "右圆括号底部"] # 0xf8f8 + - "": [T: "右方括号顶部"] # 0xf8f9 + - "": [T: "右方括号延伸段"] # 0xf8fa + - "": [T: "右方括号底部"] # 0xf8fb + - "": [T: "右花括号顶部"] # 0xf8fc + - "": [T: "右花括号中部"] # 0xf8fd + - "": [T: "右花括号底部"] # 0xf8fe + - "": [T: "苹果标志"] # 0xf8ff + - "ff": [T: "ff 连字"] # 0xfb00 + - "fi": [T: "fi 连字"] # 0xfb01 + - "fl": [T: "fl 连字"] # 0xfb02 + - "ffi": [T: "ffi 连字"] # 0xfb03 + - "ffl": [T: "ffl 连字"] # 0xfb04 + - "ſt": [T: "ft 连字"] # 0xfb05 + - "st": [T: "st 连字"] # 0xfb06 + - "﬩": [T: "希伯来文字母备用加号"] # 0xfb29 + - "︠": [T: "连字左半修饰符"] # 0xfe20 + - "︡": [T: "连字右半修饰符"] # 0xfe21 + - "︢": [T: "双波浪号左半修饰符"] # 0xfe22 + - "︣": [T: "双波浪号右半修饰符"] # 0xfe23 + - "︤": [T: "长音左半修饰符"] # 0xfe24 + - "︥": [T: "长音右半修饰符"] # 0xfe25 + - "︦": [T: "连接长音符"] # 0xfe26 + - "︵": [T: "呈现形垂直左圆括号"] # 0xfe35 + - "︶": [T: "呈现形垂直右圆括号"] # 0xfe36 + - "︷": [T: "呈现形垂直左卷曲括号"] # 0xfe37 + - "︸": [T: "呈现形垂直右卷曲括号"] # 0xfe38 + - "︿": [T: "呈现形垂直左角括号"] # 0xfe3f + - "﹀": [T: "呈现形垂直右角括号"] # 0xfe40 + - "﹡": [T: "小写星号"] # 0xfe61 + - "﹢": [T: "小写加号"] # 0xfe62 + - "﹣": [T: "小写连字符减号"] # 0xfe63 + - "﹤": [T: "小小于"] # 0xfe64 + - "﹥": [T: "小大于"] # 0xfe65 + - "﹦": [T: "小写等号"] # 0xfe66 + - "+": [T: "全角加号"] # 0xff0b + - "<": [T: "小于"] # 0xff1c + - "=": [T: "等于"] # 0xff1d + - ">": [T: "大于"] # 0xff1e + - "\": [T: "反斜杠"] # 0xff3c + - "^": [T: "帽符"] # 0xff3e + - "|": # 0xff5c + # note: for ClearSpeak and SimpleSpeak, "|" inside of sets is handled at the mrow level, same for 'sets' + - with: + variables: [DefaultToGiven: "count(preceding-sibling::*)=1 and count(following-sibling::*)=1 and ../../../*[1][.='P']"] # P(A|B) + replace: + - test: + - if: "$SpeechStyle != 'ClearSpeak'" + then_test: + if: "$DefaultToGiven" + then: [T: "条件为"] + else: [T: "竖线"] + - else_if: "not(preceding-sibling::*) or not(following-sibling::*)" + then: [T: "竖线"] + - else_if: "$ClearSpeak_VerticalLine = 'SuchThat'" + then: [T: "使得"] + - else_if: "$ClearSpeak_VerticalLine = 'Given' or $DefaultToGiven" + then: [T: "条件为"] + - else: [T: "整除"] + - "~": [T: "波浪号"] # 0xff5e + - "¬": [T: "非"] # 0xffe2 + - "←": [T: "向左箭头"] # 0xffe9 + - "↑": [T: "向上箭头"] # 0xffea + - "→": [T: "向右箭头"] # 0xffeb + - "↓": [T: "向下箭头"] # 0xffec + - "": [T: "未知或缺失的对象"] # 0xfffc + - "�": [T: "缺失字符"] # 0xfffd (used for missing input when entering braille) + + - "🣑": [T: "长向右鱼叉箭头长向左鱼叉箭头"] # 0x1F8D1 + - "🣒": [T: "长向右鱼叉箭头上短向左鱼叉箭头"] # 0x1F8D2 + - "🣓": [T: "短向右鱼叉箭头上长向左鱼叉箭头"] # 0x1F8D3 + +# MathJax v4 has adopted these PUA values for some partial chem bonds that aren't in Unicode + - "\uE410": [T: "部分键"] # 0xe410 + - "\uE411": [T: "双部分键"] # 0xe411 + - "\uE412": [T: "三部分键"] # 0xe412 + +# MathJax v4 also adopted these PUA values for some arrows thar are in Unicode +# Hopefully these will be exported properly in future versions of MathJax + - "\uE428": [SPELL: "'⟵'"] # 0xe428 defer to def of arrow + - "\uE429": [SPELL: "'⟶'"] # 0xe429 defer to def of arrow + - "\uE42A": [SPELL: "'⟷'"] # 0xe42a defer to def of arrow + - "\uE408": [SPELL: "'🣑'"] # 0xe408 defer to def of arrow + - "\uE409": [SPELL: "'🣒'"] # 0xe409 defer to def of arrow + - "\uE40A": [SPELL: "'🣓'"] # 0xe40a defer to def of arrow + - "\uE42B": [SPELL: "'⇄'"] # 0xe42b defer to def of arrow + - "\uE42C": [SPELL: "'←'"] # 0xe42c defer to def of arrow + - "\uE42D": [SPELL: "'→'"] # 0xe42d defer to def of arrow + - "\uE42E": [SPELL: "'⇄'"] # 0xe42e defer to def of arrow diff --git a/Rules/Languages/zh/unicode.yaml b/Rules/Languages/zh/unicode.yaml new file mode 100644 index 000000000..2c1a8d7b1 --- /dev/null +++ b/Rules/Languages/zh/unicode.yaml @@ -0,0 +1,530 @@ +--- + # Note to translators: + # most languages don't have two ways to pronounce 'a' -- if not need, remove the rules and change "B-Z" to "A-Z" + # some languages say the word for "uppercase" after the letter. Make sure to change that where appropriate by moving some code around + - "a": + - test: + if: "$TTS='none'" + then: [T: "a"] # long "a" sound in all speech engines I tested (espeak, MS SAPI, eloquence, + else: [SPELL: "'a'"] # AWS Polly, ReadSpeaker, NaturalReader, google cloud, nuance, ibm watson) + - "b-z": + - test: + if: "$TTS='none'" + then: [T: "."] + else: [SPELL: "'.'"] + + # Capital letters are a little tricky: users can pick their favorite word (something that was requested) and + # screen readers have options to use pitch changes or beeps instead of or in addition to say "cap" + # Also, if a user can see the screen, they probably don't need to hear "cap", but if they specified an override, they must want to hear the override. + + - "A": + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + replace: + - test: + if: "$TTS='none'" + then: [T: "a"] + else: [SPELL: "'a'"] + + - "B-Z": + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + # note: processing of ranges converts '.' into the character, so it needs to be in quotes below + replace: [SPELL: "translate('.', 'BCDEFGHIJKLMNOPQRSTUVWXYZ', 'bcdefghijklmnopqrstuvwxyz')"] + + - "0-9": [T: "."] + + - " ": [T: " "] # 0x20 + + - "!": # 0x21 + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then_test: + if: "$Verbosity = 'Terse'" + then: [T: "感叹号"] # 0x21 + else: [T: "感叹号"] # 0x21 + else: [T: "阶乘"] # 0x21 + + - "\"": [T: "双引号"] # 0x22 + - "#": [T: "井号"] # 0x23 + - "$": [T: "美元"] # 0x24 + - "%": [T: "百分号"] # 0x25 + - "&": [T: "与号"] # 0x26 + - "'": [T: "单引号"] # 0x27 + - "(": # 0x28 + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then_test: + if: "$Verbosity='Terse'" + then: [T: "左括号"] # 0x28 + else: [T: "左括号"] # 0x28 + else: [T: "左括号"] # 0x28 + - ")": # 0x29 + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then_test: + if: "$Verbosity='Terse'" + then: [T: "右括号"] # 0x29 + else: [T: "右括号"] # 0x29 + else: [T: "右括号"] # 0x29 + + - "*": # 0x2a + test: + if: "parent::*[name(.)='msup' or name(.)='msubsup' or name(.)='skip-super']" + then: [T: "星号"] # 0x2a + else: [T: "乘"] # 0x2a + - "+": [T: "加"] # 0x2b + - ",": # 0x2c + # the following deals with the interaction of "," with "…" which sometimes wants the ',' to be silent + # that this test is here and not with "…" is not ideal, but seems simplest + test: + if: + - "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_Ellipses = 'Auto' or " + # must be ClearSpeak and $ClearSpeak_Ellipses = 'AndSoOn' + # speak "comma" when not adjacent to '…' + - "( following-sibling::*[1][text()!= '…'] and preceding-sibling::*[1][text()!='…'] ) or " + # except if expression starts with '…' + - "../*[1][.='…'] " + then: + - T: "逗号" # (en: 'comma') + - test: + if: "$Verbosity != Terse" + then: [pause: short] + # else silent + + - "-": [T: "减"] # 0x2d + - ".": # 0x2e + - test: + if: "parent::*[1][self::m:mn]" + then: [T: "点"] + else: [T: "点"] + - "/": # 0x2f + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "斜杠"] # 0x2f + else: [T: "除以"] # 0x2f + + - ":": [T: "冒号"] # 0x3a + - ";": [T: "分号"] # 0x3b + - "<": # 0x3c + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于" + - "=": # 0x3d + - test: + if: "$Verbosity!='Terse'" + then: [T: "等于"] + else: [T: "等于"] + + - ">": # 0x3e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于" + - "?": [T: "问号"] # 0x3f + - "@": [T: "艾特号"] # 0x40 + - "[": # 0x5b + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "左方括号"] + else: [T: "左方括号"] + - "\\": [T: "反斜杠"] # 0x5c + - "]": # 0x5d + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "右方括号"] + else: [T: "右方括号"] + - "^": # 0x5e + - test: + if: "parent::m:modified-variable or parent::m:mover" + then: [T: "帽符"] + else: [T: "脱字符"] + - "_": [T: "下划线"] # 0x5f + - "`": [T: "钝音符"] # 0x60 + - "{": # 0x7b + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "左花括号"] + else: [T: "左花括号"] + - "|": # 0x7c + # note: for ClearSpeak and SimpleSpeak, "|" inside of sets is handled at the mrow level, same for 'sets' + - with: + variables: [DefaultToGiven: "count(preceding-sibling::*)=1 and count(following-sibling::*)=1 and ../../../*[1][.='P']"] # P(A|B) + replace: + - test: + - if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "竖线"] + - else_if: "$SpeechStyle != 'ClearSpeak'" + then_test: + - if: "$DefaultToGiven" + then: [T: "条件为"] + - else_if: "preceding-sibling::*[1][self::m:mn and not(contains(., $DecimalSeparators))] and + following-sibling::*[1][self::m:mn and not(contains(., $DecimalSeparators))]" + then: [T: "整除"] + else: [T: "竖线"] + - else_if: "not(preceding-sibling::*) or not(following-sibling::*)" + then: [T: "竖线"] + - else_if: "$ClearSpeak_VerticalLine = 'SuchThat'" + then: [T: "使得"] + - else_if: "$ClearSpeak_VerticalLine = 'Given' or $DefaultToGiven" + then: [T: "条件为"] + - else: [T: "整除"] + + - "}": # 0x7d + - test: + if: $SpeechStyle = 'ClearSpeak' or $SpeechStyle = 'SimpleSpeak' + then: [T: "右花括号"] + else: [T: "右花括号"] + + - "~": [T: "波浪号"] # 0x7e + - " ": # 0xa0 + - test: + # could be mtext in mtd or mtext in an mrow that is a concatenation of mtd's. Is there a better solution? + if: "@data-empty-in-2D and not(ancestor::*[self::m:piecewise or self::m:system-of-equations or self::m:lines])" + then: [T: "空格"] # want to say something for fraction (etc) with empty child + else: [T: ""] + + - "¬": [T: "非"] # 0xac + - "°": [T: "度"] # 0xb0 + - "±": [T: "正负"] # 0xb1 + - "´": [T: "锐音符"] # 0xb4 + - "·": # 0xB7 + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')] or not($SpeechStyle = 'ClearSpeak' and $ClearSpeak_MultSymbolDot = 'Auto')" + then: [T: "点"] + else: [T: "乘"] + - "×": # 0xd7 + - test: + if: "$SpeechStyle = 'ClearSpeak'" + then_test: + - if: "$ClearSpeak_MultSymbolX = 'Auto'" + then: [T: "乘"] + - else_if: "$ClearSpeak_MultSymbolX = 'By'" + then: [T: "乘"] + else: [T: "叉乘"] + else_test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "叉乘"] + else: [T: "乘"] + + - "÷": [T: "除以"] # 0xf7 + - "̀": [T: "上加钝音符"] # 0x300 + - "́": [T: "上加锐音符"] # 0x301 + - "̂": [T: "上加抑扬符"] # 0x302 + - "̃": [T: "上加波浪号"] # 0x303 + - "̄": [T: "上加长音符"] # 0x304 + - "̅": [T: "上加横线"] # 0x305 + - "̆": [T: "短音符"] # 0x306 + - "̇": [T: "上加点"] # 0x307 + + # Note: ClearSpeak has pref TriangleSymbol for "Δ", but that is wrong + - "Α-Ω": + - test: + if: "$CapitalLetters_Beep" + then: + - audio: + value: "beep.mp4" + replace: [] + - test: + if: "$CapitalLetters_UseWord" + then_test: + if: "$SpeechOverrides_CapitalLetters = ''" + then_test: + if: "$Impairment = 'Blindness'" + then: [T: "大写"] + else: [x: "$SpeechOverrides_CapitalLetters"] + - pitch: + value: "$CapitalLetters_Pitch" + # note: processing of ranges converts '.' into the character, so it needs to be in quotes below + replace: [SPELL: "translate('.', 'ΑΒΓΔΕΖΗΘΙΚΛΜΝΞΟΠΡ΢ΣΤΥΦΧΨΩ', 'αβγδεζηθικλμνξοπρςστυφχψω')"] + + - "α": [T: "阿尔法"] # 0x3b1 + - "β": [T: "贝塔"] # 0x3b2 + - "γ": [T: "伽马"] # 0x3b3 + - "δ": [T: "德尔塔"] # 0x3b4 + - "ε": [T: "艾普西隆"] # 0x3b5 + - "ζ": [T: "泽塔"] # 0x3b6 + - "η": [T: "伊塔"] # 0x3b7 + - "θ": [T: "西塔"] # 0x3b8 + - "ι": [T: "约塔"] # 0x3b9 + - "κ": [T: "卡帕"] # 0x3ba + - "λ": [T: "拉姆达"] # 0x3bb + - "μ": [T: "缪"] # 0x3bc + - "ν": [T: "纽"] # 0x3bd + - "ξ": [T: "克西"] # 0x3be + - "ο": [T: "奥密克戎"] # 0x3bf + - "π": [T: "派"] # 0x3c0 + - "ρ": [T: "柔"] # 0x3c1 + - "ς": [T: "末尾西格马"] # 0x3c2 + - "σ": [T: "西格马"] # 0x3c3 + - "τ": [T: "陶"] # 0x3c4 + - "υ": [T: "宇普西隆"] # 0x3c5 + - "φ": [T: "斐"] # 0x3c6 + - "χ": [T: "希"] # 0x3c7 + - "ψ": [T: "普西"] # 0x3c8 + - "ω": [T: "欧米伽"] # 0x3c9 + - "ϕ": [T: "斐"] # 0x3d5 + - "ϖ": [T: "派"] # 0x3d6 + - "ϵ": [T: "艾普西隆"] # 0x3f5 + - "϶": [T: "反向艾普西隆"] # 0x3f6 + + - "⁡": # 0x2061 + - test: + # skip saying "of" when Terse and a trig function, when it is a shape (does this happen?), or we are in :literal mode + if: "not( + ( $Verbosity='Terse' or ($SpeechStyle = 'ClearSpeak' and IsNode(following-sibling::*[1],'simple')) and + preceding-sibling::*[1][IfThenElse($SpeechStyle='ClearSpeak', + IsInDefinition(., 'ClearSpeakTrigFunctionNames'), + IsInDefinition(., 'TrigFunctionNames') )] + ) or + preceding-sibling::*[1][IsInDefinition(., 'GeometryShapes')] or + (@data-changed='added' and ancestor-or-self::*[contains(@data-intent-property, ':literal:')]) + )" + then: [T: ""] + - "⁢": [T: ""] # 0x2062 + - "⁣": [T: ""] # 0x2063 + - "⁤": [T: "又"] # 0x2064 + - "ℂℕℚℝℤ": # here we rely on this running through the table again to speak "cap xxx" + - T: "双线体" + - SPELL: "translate('.', 'ℂℕℚℝℤ', 'CNQRZ')" + + - "℃": [T: "摄氏度"] # 0x2103 + - "ℋℛℓ": # 0x210b + - T: "花体" + - SPELL: "translate('.', 'ℋℛℓ', 'HRl')" + - "ℜ": # 0x211c + - T: "哥特体" + - SPELL: "'R'" + + - "ⅆⅇⅈⅉ": # 0x2146-9 + - T: "双线斜体" + - SPELL: "translate('.', 'ⅆⅇⅈⅉ', 'deij')" + + - "←": [T: "向左箭头"] # 0x2190 + - "↑": [T: "向上箭头"] # 0x2191 + - "→": # 0x2192 + - test: + if: "ancestor::*[2][self::m:limit]" + then: [T: "趋于"] + else: [T: "向右箭头"] + + - "↓": [T: "向下箭头"] # 0x2193 + - "∈": # 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: + - test: + if: "$Verbosity!='Terse' and not(ancestor::*[self::m:set])" # "the set x is an element of ..." sounds bad" + then: [T: ""] + - T: "属于" + # Several options for speaking elements in ClearSpeak -- they split between being inside a set or not and then the option + else_test: + if: "../../self::m:set or ../../../self::m:set" # inside a set + then_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "∥": # 0x2225 + - test: + if: "ancestor-or-self::*[contains(@data-intent-property, ':literal:')]" + then: [T: "双竖线"] + else: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "平行于" + - "∦": # 0x2226 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不平行于" + - "≤": # 0x2264 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "小于等于" + - "≥": # 0x2265 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "大于等于" + + # --- restored complex short definitions (prefer over unicode-full) --- + - "…": # 0x2026 + test: + if: + - "$SpeechStyle != 'ClearSpeak' or $ClearSpeak_Ellipses = 'Auto' or" + # must be ClearSpeak and $ClearSpeak_Ellipses = 'AndSoOn' + # speak '…' as 'and so on...' unless expr starts with '…' + - "../*[1][.='…']" + then: [T: "省略号"] + else_test: # must have $ClearSpeak_Ellipses = 'AndSoOn' + if: "count(following-sibling::*) = 0" + then: [T: "等等"] + else: [T: "依此类推,直到"] + - "∂": # 0x2202 + - test: + if: "$Verbosity='Terse'" + then: [T: "偏微分"] + else: [T: "偏导数"] + - "∆": [T: "拉普拉斯算子"] # 0x2206 + - "∉": # 0x2209 + # rule is identical to 0x2208 + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不属于" + # Several options for speaking elements in ClearSpeak -- they split between being inside a set or not and then the option + else_test: + if: "../../self::m:set or ../../../self::m:set" # inside a set + then_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "不属于"] + - else: [T: "不属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "不属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "不属于"] + - else: [T: "不属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "∊": # 0x220a + - test: + if: "$SpeechStyle != 'ClearSpeak'" + then: + - test: + if: "$Verbosity!='Terse' and not(ancestor::*[self::m:set])" # "the set x is an element of ..." sounds bad" + then: [T: ""] + - T: "属于" + # Several options for speaking elements in ClearSpeak -- they split between being inside a set or not and then the option + else_test: + if: "../../self::m:set or ../../../self::m:set" # inside a set + then_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + else_test: + - if: $ClearSpeak_SetMemberSymbol = 'Auto' or $ClearSpeak_SetMemberSymbol = 'Member' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'Element' + then: [T: "属于"] + - else_if: $ClearSpeak_SetMemberSymbol = 'In' + then: [T: "属于"] + - else: [T: "属于"] # $ClearSpeak_SetMemberSymbol = 'Belongs' + - "√": # 0x221a + - test: + if: "$Verbosity!='Terse'" + then: [OT: ""] + - T: "平方根" + - "∝": # 0x221d + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "正比于" + - "∶": # 0x2236 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "比" + - "∾": # 0x223e + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "最正值" + - "≠": # 0x2260 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不等于" + - "≡": # 0x2261 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "恒等于" + - "⊂": # 0x2282 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "真包含于" + - "⊃": # 0x2283 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "真包含" + - "⊄": # 0x2284 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不真包含于" + - "⊅": # 0x2285 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "不真包含" + - "⊆": # 0x2286 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "包含于" + - "⊇": # 0x2287 + - test: + if: "$Verbosity!='Terse'" + then: [T: ""] + - T: "包含" + + # --- restored short definitions that differ from unicode-full --- + - "⇒": [T: "向右双线箭头"] # 0x21d2 + - "∐": [T: "余积"] # 0x2210 diff --git a/tests/Languages/zh.rs b/tests/Languages/zh.rs index 82ab40e4f..2e81f3d5e 100644 --- a/tests/Languages/zh.rs +++ b/tests/Languages/zh.rs @@ -1,3 +1,4 @@ #![allow(non_snake_case)] -mod tw; \ No newline at end of file +mod cn; +mod tw; diff --git a/tests/Languages/zh/cn.rs b/tests/Languages/zh/cn.rs new file mode 100644 index 000000000..8550de8fe --- /dev/null +++ b/tests/Languages/zh/cn.rs @@ -0,0 +1,674 @@ +//! Core Simplified Chinese speech and navigation regression tests. + +use crate::common::*; +use anyhow::Result; +use std::panic::{catch_unwind, AssertUnwindSafe}; + +#[test] +fn fractions_use_denominator_first_order() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "12", + "2 分之 1", + )?; + + let complex = + "ab+1"; + test( + "zh", + "SimpleSpeak", + complex, + "分数, b 加 1, 分之 a, 结束分数", + )?; + test("zh", "ClearSpeak", complex, "分数,分子为 a; 分母为 b 加 1") +} + +#[test] +fn zh_cn_locale_falls_back_to_simplified_chinese_rules() -> Result<()> { + // Regional Chinese locales without overrides must use the base zh rules. + test( + "zh-cn", + "SimpleSpeak", + "12", + "2 分之 1", + ) +} + +#[test] +fn roots_and_powers_use_chinese_word_order() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "x+y", + "根号 x 加 y 结束根号", + )?; + test( + "zh", + "SimpleSpeak", + "xn", + "x 的 n 次方根", + )?; + test( + "zh", + "SimpleSpeak", + "xn", + "x 的 n 次方", + )?; + test( + "zh", + "LiteralSpeak", + "x+yn", + "根指数 n 根号, x 加 y, 结束根号", + ) +} + +#[test] +fn absolute_values_use_argument_first_order() -> Result<()> { + let expr = + "|x+1|"; + test("zh", "SimpleSpeak", expr, "x 加 1 的绝对值")?; + test("zh", "ClearSpeak", expr, "x 加 1 的绝对值")?; + test_prefs( + "zh", + "ClearSpeak", + vec![("ClearSpeak_AbsoluteValue", "AbsEnd")], + expr, + "x 加 1 的绝对值, 结束绝对值", + )?; + test_prefs( + "zh", + "ClearSpeak", + vec![("ClearSpeak_AbsoluteValue", "Cardinality")], + "|S|", + "大写 s 的基数", + ) +} + +#[test] +fn literal_speech_matches_documented_examples() -> Result<()> { + test( + "zh", + "LiteralSpeak", + "12", + "2 分之 1", + )?; + test( + "zh", + "LiteralSpeak", + "x", + "根号 x, 结束根号", + ) +} + +#[test] +fn units_and_laplacian_use_standard_terms() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "1kat", + "1 开特", + )?; + test( + "zh", + "SimpleSpeak", + "f", + "拉普拉斯算子 f", + ) +} + +#[test] +fn permutation_cycles_and_repeating_decimals_use_concise_terms() -> Result<()> { + // Use the standard nouns "轮换" and "循环节", without literal English-style expansion. + test( + "zh", + "ClearSpeak", + "x", + "轮换 x", + )?; + test( + "zh", + "SimpleSpeak", + "0.16", + "0.1 循环节为 6", + ) +} + +#[test] +fn ellipses_use_the_standard_symbol_name() -> Result<()> { + // Both horizontal ellipsis characters should be announced as "省略号". + test( + "zh", + "SimpleSpeak", + "13,", + "1 省略号 3, 逗号, 省略号", + ) +} + +#[test] +fn chemical_equilibrium_arrows_use_standard_reaction_terms() -> Result<()> { + // Distinguish a reversible reaction from equilibria biased to either side. + let equation = |arrow: &str| { + format!( + "H{arrow}I" + ) + }; + test( + "zh", + "SimpleSpeak", + &equation("⇌"), + "大写 h, 可逆反应 大写 i", + )?; + test( + "zh", + "SimpleSpeak", + &equation("🣓"), + "大写 h, 平衡偏左 大写 i", + )?; + test( + "zh", + "SimpleSpeak", + &equation("🣒"), + "大写 h, 平衡偏右 大写 i", + ) +} + +#[test] +fn long_multiscripts_state_the_remaining_order_naturally() -> Result<()> { + // Once explicit pairs are exhausted, state that the remaining lower and upper scripts alternate. + test( + "zh", + "SimpleSpeak", + "Tklmnop", + "大写 t 有 3 组前置上下标, 前下标 k 且 前上标 l, 前下标 m 且 前上标 n 其余前置下标、上标依次交替 o p 结束前置上下标", + )?; + test( + "zh", + "SimpleSpeak", + "Tabcdefghij", + "大写 t 有 5 组后置上下标, 下标 a 且 上标 b 下标 c 且 上标 d 下标 e 且 上标 f 下标 g 且 上标 h 其余后置下标、上标依次交替 i j 结束后置上下标", + ) +} + +#[test] +fn set_relations_use_textbook_containment_terms() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "AB,CD,EF,GH", + "大写 a 真包含于 大写 b; 逗号; 大写 c 包含于 大写 d; 逗号; 大写 e 真包含于 大写 f; 逗号; 大写 g 真包含于 大写 h", + )?; + test( + "zh", + "SimpleSpeak", + "AB,CD", + "大写 a 不真包含于 大写 b; 逗号; 大写 c 不包含于 大写 d", + )?; + test( + "zh", + "ClearSpeak", + "xy", + "x 真包含于 y", + )?; + test( + "zh", + "ClearSpeak", + "xy", + "x 包含于 y", + ) +} + +#[test] +fn set_operations_use_standard_verb_forms() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "ABC", + "大写 a 交 大写 b, 并 大写 c", + )?; + test( + "zh", + "ClearSpeak", + "xy", + "x 交 y", + )?; + test( + "zh", + "ClearSpeak", + "xy", + "x 并 y", + ) +} + +#[test] +fn standard_number_sets_use_textbook_names() -> Result<()> { + let cases = [ + ("set-of-integers", "ℤ", "全体整数的集合"), + ("set-of-reals", "ℝ", "全体实数的集合"), + ("set-of-rationals", "ℚ", "全体有理数的集合"), + ("set-of-natural-numbers", "ℕ", "全体自然数的集合"), + ("set-of-complex-numbers", "ℂ", "全体复数的集合"), + ("set-of-primes", "ℙ", "全体素数的集合"), + ]; + + for (intent, symbol, expected) in cases { + let expr = format!("{symbol}"); + test("zh", "ClearSpeak", &expr, expected)?; + } + Ok(()) +} + +#[test] +fn set_builder_description_is_natural() -> Result<()> { + let expr = "{x|x>2}"; + test("zh", "ClearSpeak", expr, "所有满足 x 大于 2 的 x 的集合")?; + test_prefs( + "zh", + "ClearSpeak", + vec![("ClearSpeak_Sets", "woAll")], + expr, + "满足 x 大于 2 的 x 的集合", + )?; + test("zh", "SimpleSpeak", expr, "所有满足 x 大于 2 的 x 的集合") +} + +#[test] +fn multi_argument_set_intent_is_not_mistaken_for_empty_set() -> Result<()> { + test( + "zh", + "ClearSpeak", + "xy", + "集合 x 逗号, y", + ) +} + +#[test] +fn half_open_intervals_name_both_ends() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "(a,b]", + "左开右闭区间 a 逗号 b", + )?; + test( + "zh", + "SimpleSpeak", + "[a,b)", + "左闭右开区间 a 逗号 b", + ) +} + +#[test] +fn quotient_and_remainder_follow_operand_order() -> Result<()> { + test( + "zh", + "ClearSpeak", + "xy", + "x 除以 y 的商", + )?; + test( + "zh", + "ClearSpeak", + "xy", + "x 除以 y 的余数", + ) +} + +#[test] +fn coordinates_and_angles_use_standard_terms() -> Result<()> { + test( + "zh", + "ClearSpeak", + "xyz", + "直角坐标 x 逗号, y 逗号, z", + )?; + test( + "zh", + "ClearSpeak", + "x", + "角 x 的度数", + )?; + test( + "zh", + "SimpleSpeak", + "y", + "角 y 的度数", + ) +} + +#[test] +fn geometry_objects_use_textbook_word_order() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "ab", + "线段 a b", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "射线 a b", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "弧 a b", + )?; + test( + "zh", + "SimpleSpeak", + "abc", + "角 a b c 的度数", + ) +} + +#[test] +fn definite_integral_announces_limits() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "01f(x)dx", + "积分 从 0 到 1, f x d x", + ) +} + +#[test] +fn matrix_announces_dimensions_and_rows() -> Result<()> { + test( + "zh", + "SimpleSpeak", + "(1234)", + "2 乘 2 矩阵; 行 1; 1, 2; 行 2; 3, 4", + ) +} + +#[test] +fn tau_and_dotted_minus_symbols_are_distinguishable() -> Result<()> { + test("zh", "SimpleSpeak", "τ", "陶")?; + test( + "zh", + "SimpleSpeak", + ",", + "带下降点列的减号, 逗号, 带上升点列的减号", + ) +} + +fn init_navigation(mathml: &str) -> Result<()> { + set_rules_dir(abs_rules_dir_path())?; + set_preference("Language", "zh")?; + set_preference("SpeechStyle", "SimpleSpeak")?; + set_preference("Verbosity", "Medium")?; + set_preference("NavMode", "Enhanced")?; + set_preference("NavVerbosity", "Verbose")?; + set_preference("AutoZoomOut", "False")?; + set_preference("Overview", "False")?; + set_mathml(mathml)?; + Ok(()) +} + +#[test] +fn navigation_enters_fraction_numerator() -> Result<()> { + init_panic_handler(); + let result = catch_unwind(AssertUnwindSafe(|| { + init_navigation( + "12", + )?; + let speech = do_navigate_command("SetPlacemarker1")?; + let speech = speech.trim_end_matches([' ', ',', ';']); + assert_eq!("设置位置标记 1; 2 分之 1", speech); + + let speech = do_navigate_command("DescribeCurrent")?; + let speech = speech.trim_end_matches([' ', ',', ';']); + assert_eq!("概述 当前项; 2 分之 1", speech); + + let speech = do_navigate_command("ZoomIn")?; + let speech = speech.trim_end_matches([' ', ',', ';']); + assert_eq!("进入下一层; 进入 分子; 1", speech); + Ok(()) + })); + report_any_panic(result) +} + +#[test] +fn additional_real_world_readings() -> Result<()> { + let cases = [ + ( + "trig-simple", + "SimpleSpeak", + "Medium", + "sin2x+cos2x=1", + "正弦 平方 x, 加 余弦 平方 x; 等于 1", + ), + ( + "trig-clear", + "ClearSpeak", + "Medium", + "sin2x+cos2x=1", + "正弦 平方 x, 加 余弦 平方 x; 等于 1", + ), + ( + "log-base-simple", + "SimpleSpeak", + "Medium", + "log28=3", + "以 2 为底, 8 的对数, 等于 3", + ), + ( + "log-base-clear", + "ClearSpeak", + "Medium", + "log28=3", + "以 2 为底, 8 的对数, 等于 3", + ), + ( + "limit-simple", + "SimpleSpeak", + "Medium", + "limx0sinxx=1", + "极限,当 x 趋于 0; 分数, x 分之, 正弦 x, 结束分数; 等于 1", + ), + ( + "partial-derivative", + "ClearSpeak", + "Medium", + "f", + "偏导数 f", + ), + ( + "sum", + "SimpleSpeak", + "Medium", + "k=1nk2", + "求和 从 k 等于 1 到 n, k 平方", + ), + ( + "product", + "SimpleSpeak", + "Medium", + "k=1nk", + "连乘 从 k 等于 1 到 n k", + ), + ( + "prime-derivative", + "SimpleSpeak", + "Medium", + "f(x)=2x", + "f 撇号, x, 等于 2 x", + ), + ( + "dot-product", + "SimpleSpeak", + "Medium", + "vw", + "向量 v, 点乘 向量 w", + ), + ( + "explicit-dot-product-intent", + "ClearSpeak", + "Medium", + "vw", + "v 点乘 w", + ), + ( + "scalar-centered-dot", + "SimpleSpeak", + "Medium", + "vw", + "v 乘 w", + ), + ( + "cross-product", + "SimpleSpeak", + "Medium", + "v×w", + "向量 v, 向量积 向量 w", + ), + ( + "plain-norm", + "SimpleSpeak", + "Medium", + "x+y", + "x 加 y 的范数", + ), + ( + "norm", + "SimpleSpeak", + "Medium", + "x2", + "x 的 2 范数", + ), + ( + "determinant", + "SimpleSpeak", + "Medium", + "|abcd|", + "2 乘 2 行列式; 行 1; a, b; 行 2; c, d", + ), + ( + "mean", + "ClearSpeak", + "Medium", + "x", + "x 的平均值", + ), + ( + "standard-deviation", + "ClearSpeak", + "Medium", + "x", + "x 的标准差", + ), + ( + "variance", + "ClearSpeak", + "Medium", + "x", + "x 的方差", + ), + ( + "median", + "ClearSpeak", + "Medium", + "x", + "x 的中位数", + ), + ( + "mode", + "ClearSpeak", + "Medium", + "x", + "x 的众数", + ), + ( + "water-terse", + "SimpleSpeak", + "Terse", + "H2O", + "大写 h, 2 大写 o", + ), + ( + "water-medium", + "SimpleSpeak", + "Medium", + "H2O", + "大写 h, 下标 2, 大写 o", + ), + ( + "water-verbose", + "SimpleSpeak", + "Verbose", + "H2O", + "大写 h, 下标 2, 大写 o", + ), + ( + "sulfate-medium", + "SimpleSpeak", + "Medium", + "[SO4]2", + "左方括号, 大写 s, 大写 o, 下标 4; 右方括号 上标 2 减", + ), + ( + "aqueous-terse", + "SimpleSpeak", + "Terse", + "FeCl3(aq)", + "大写 f e, 大写 c l, 3 水溶液", + ), + ( + "acceleration-unit", + "SimpleSpeak", + "Medium", + "3ms2", + "3 米 每 秒 平方", + ), + ( + "piecewise", + "SimpleSpeak", + "Medium", + "f(x)={x2x0xx<0", + "f x 等于; 2 分支; 分支 1; x 平方 当 x, 大于等于 0; 分支 2; 负 x 当 x, 小于 0", + ), + ( + "system-of-equations", + "SimpleSpeak", + "Medium", + "x+y=72x+3y=17", + "2 方程; 方程 1; x 加 y 等于 7; 方程 2; 2 x 加 3 y; 等于 17", + ), + ( + "quadratic-formula-simple", + "SimpleSpeak", + "Medium", + "x=b±b24ac2a", + "x 等于; 分数, 2 a, 分之, 负 b 正负, 根号 b 平方 减 4 a c 结束根号; 结束分数", + ), + ( + "quadratic-formula-literal", + "LiteralSpeak", + "Medium", + "x=b±b24ac2a", + "x 等于; 分数, 2 a, 分之, 减 b 正负; 根号 b 上标 2 结束上标, 减 4 a c, 结束根号; 结束分数", + ), + ( + "binomial", + "SimpleSpeak", + "Medium", + "Ckn", + "n 取 k", + ), + ]; + + for (name, style, verbosity, mathml, expected) in cases { + test_prefs( + "zh", + style, + vec![("Verbosity", verbosity)], + mathml, + expected, + ) + .map_err(|error| anyhow::anyhow!("{name}: {error}"))?; + } + Ok(()) +} From 63d8dba4f57ad5801573ff3d4dd8e6a64993687f Mon Sep 17 00:00:00 2001 From: cary-rowen Date: Sat, 22 Aug 2026 22:00:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?=E7=BB=9F=E4=B8=80=E7=AE=80=E4=BD=93?= =?UTF-8?q?=E4=B8=AD=E6=96=87=E6=95=B0=E5=AD=A6=E6=9C=AF=E8=AF=AD=E5=92=8C?= =?UTF-8?q?=E6=9C=97=E8=AF=BB=E8=A7=84=E5=88=99?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../zh/SharedRules/linear-algebra.yaml | 13 ++ Rules/Languages/zh/definitions.yaml | 8 +- Rules/Languages/zh/unicode-full.yaml | 59 +++---- Rules/Languages/zh/unicode.yaml | 10 +- tests/Languages/zh/cn.rs | 162 +++++++++++++++++- 5 files changed, 210 insertions(+), 42 deletions(-) diff --git a/Rules/Languages/zh/SharedRules/linear-algebra.yaml b/Rules/Languages/zh/SharedRules/linear-algebra.yaml index 2d28e8ae8..0dfe1ded6 100644 --- a/Rules/Languages/zh/SharedRules/linear-algebra.yaml +++ b/Rules/Languages/zh/SharedRules/linear-algebra.yaml @@ -1,5 +1,18 @@ --- +# Use the mainland textbook noun phrases when both operands belong to one intent node. +- name: textbook-vector-product-intent + tag: "*" + match: "count(*)=2 and (name(.)='dot-product' or name(.)='cross-product')" + replace: + - x: "*[1]" + - T: "与" + - x: "*[2]" + - test: + if: "name(.)='dot-product'" + then: [T: "的数量积"] + else: [T: "的叉积"] + - name: scalar-determinant tag: determinant match: "count(*)=1 and not(*[1][self::m:mtr])" diff --git a/Rules/Languages/zh/definitions.yaml b/Rules/Languages/zh/definitions.yaml index abeee966f..1f8fe81c0 100644 --- a/Rules/Languages/zh/definitions.yaml +++ b/Rules/Languages/zh/definitions.yaml @@ -273,13 +273,13 @@ "congruent": "infix=全等于", "cartesian-product": "infix=笛卡尔积", "composed-with": "infix=复合", - "cross-product": "infix=叉乘: 向量积: 向量积", + "cross-product": "infix=叉乘", # Operator form; complete binary expressions use the textbook noun phrase below. "defined-as": "infix=定义为", "divided-by": "infix=除以", "divides": "infix=整除", "does-not-belong-to": "infix=不属于", "does-not-divide": "infix=不能整除", - "dot-product": "infix=点乘", + "dot-product": "infix=点乘", # Operator form; complete binary expressions use the textbook noun phrase below. "downwards-diagonal-ellipsis": "infix=右下省略号", "direct-product": "infix=直积", "element-of": "infix=属于", @@ -300,7 +300,7 @@ "list-separator": "infix=逗号", "maps-to": "infix=映射到", "member-of": "infix=属于", - "minus-or-plus": "infix=负或正", + "minus-or-plus": "infix=减或加", "not-subset": "infix=不真包含于", "not-superset": "infix=不真包含", "not-equal-to": "infix=不等于", @@ -313,7 +313,7 @@ "perpendicular": "infix=垂直于", "plus": "infix=加 || prefix=正", # Prefix not tested "minus": "infix=减 || prefix=负", # Prefix not tested - "plus-or-minus": "infix=正负", + "plus-or-minus": "infix=加或减", "precedes": "infix=先于", "proportional": "infix=正比于", "range-separator": "infix=到", diff --git a/Rules/Languages/zh/unicode-full.yaml b/Rules/Languages/zh/unicode-full.yaml index 964d79d27..fb9074126 100644 --- a/Rules/Languages/zh/unicode-full.yaml +++ b/Rules/Languages/zh/unicode-full.yaml @@ -582,7 +582,7 @@ - "↣": [T: "向右带竖线箭头"] # 0x21a3 - "↤": [T: "向左带尾箭头"] # 0x21a4 - "↥": [T: "向上带尾箭头"] # 0x21a5 - - "↦": [T: "向右带尾箭头"] # 0x21a6 + - "↦": [T: "映射到"] # 0x21a6 - "↧": [T: "向下带尾箭头"] # 0x21a7 - "↨": [T: "上下箭头(带基线)"] # 0x21a8 - "↩": [T: "向左带钩箭头"] # 0x21a9 @@ -626,7 +626,7 @@ - "⇏": [T: "向右双线带斜线箭头"] # 0x21cf - "⇐": [T: "向左双线箭头"] # 0x21d0 - "⇑": [T: "向上双线箭头"] # 0x21d1 - - "⇒": [T: "蕴含"] # 0x21d2 + - "⇒": [T: "推出"] # 0x21d2 - "⇓": [T: "向下双线箭头"] # 0x21d3 - "⇔": [T: "当且仅当"] # 0x21d4 - "⇕": [T: "上下双线箭头"] # 0x21d5 @@ -672,7 +672,7 @@ - "⇽": [T: "向左开放朝向箭头"] # 0x21fd - "⇾": [T: "向右开放朝向箭头"] # 0x21fe - "⇿": [T: "左右开放朝向箭头"] # 0x21ff - - "∀": [T: "全部"] # 0x2200 + - "∀": [T: "任意"] # 0x2200 - "∁": # 0x2201 - test: if: "$Verbosity!='Terse'" @@ -685,15 +685,8 @@ else: [T: "偏导数"] - "∃": [T: "存在"] # 0x2203 - "∄": [T: "不存在"] # 0x2204 - - "∅": [T: "空集合"] # 0x2205 - - "∆": # 0x2206 - - test: - if: "following-sibling::*" - then_test: - if: "$Verbosity!='Terse'" - then: [T: "拉普拉斯算子"] # "LahPlahsian" sounds better than "laplacian" in speech engines tested - else: [T: "拉普拉斯算子"] - else: [T: "拉普拉斯算子"] + - "∅": [T: "空集"] # 0x2205 + - "∆": [T: "增量"] # 0x2206 - "∇": [T: "纳布拉"] # 0x2207 - "∉": # 0x2209 @@ -755,10 +748,14 @@ - "∐": [T: "余积"] # 0x2210 - "∑": [T: "求和"] # 0x2211 - "−": [T: "减"] # 0x2212 - - "∓": [T: "减加号"] # 0x2213 + - "∓": # 0x2213 + - test: + if: "preceding-sibling::* and following-sibling::*" + then: [T: "减或加"] + else: [T: "负或正"] - "∔": [T: "点加号"] # 0x2214 - "∕": [T: "除以"] # 0x2215 - - "∖": [T: "集合减"] # 0x2216 + - "∖": [T: "减去"] # 0x2216 - "∗": [T: "乘"] # 0x2217 - "∘": [T: "复合"] # 0x2218 - "∙": # 0x2219 @@ -794,8 +791,8 @@ - "∢": [T: "球面角"] # 0x2222 - "∣": [T: "整除"] # 0x2223 - "∤": [T: "不整除"] # 0x2224 - - "∧": [T: "逻辑与"] # 0x2227 - - "∨": [T: "逻辑或"] # 0x2228 + - "∧": [T: "且"] # 0x2227 + - "∨": [T: "或"] # 0x2228 - "∩": [T: "交"] # 0x2229 - "∪": [T: "并"] # 0x222a - "∫": [T: "积分"] # 0x222b @@ -828,7 +825,7 @@ then: [T: ""] - T: "位似于" - "∼": [T: "随之变化"] # 0x223c - - "∽": [T: "反向波浪号"] # 0x223d + - "∽": [T: "相似于"] # 0x223d - "∾": # 0x223e - test: if: "$Verbosity!='Terse'" @@ -842,7 +839,7 @@ - test: if: "$Verbosity!='Terse'" then: [T: ""] - - T: "渐近相等" + - T: "渐近等于" - "≄": # 0x2244 - test: if: "$Verbosity!='Terse'" @@ -879,7 +876,7 @@ then: [T: ""] - T: "约等于或等于" - "≋": [T: "三重波浪"] # 0x224b - - "≌": [T: "均等于"] # 0x224c + - "≌": [T: "全等于"] # 0x224c - "≍": # 0x224d - test: if: "$Verbosity!='Terse'" @@ -955,8 +952,8 @@ if: "$Verbosity!='Terse'" then: [T: ""] - T: "严格等价于" - - "≦": [T: "小于号上方的等号"] # 0x2266 - - "≧": [T: "大于号上方的等号"] # 0x2267 + - "≦": [T: "小于等于"] # 0x2266 + - "≧": [T: "大于等于"] # 0x2267 - "≨": # 0x2268 - test: if: "$Verbosity!='Terse'" @@ -1110,7 +1107,11 @@ - "⊖": [T: "带圈减号"] # 0x2296 - "⊗": [T: "带圈乘号"] # 0x2297 - "⊘": [T: "带圈除号"] # 0x2298 - - "⊙": [T: "带圈点运算符"] # 0x2299 + - "⊙": # 0x2299 + - test: + if: "following-sibling::*[1][self::m:mi] and (not(preceding-sibling::*) or preceding-sibling::*[1][self::m:mo])" + then: [T: "圆"] + else: [T: "带圈点运算符"] - "⊚": [T: "带圈环运算符"] # 0x229a - "⊛": [T: "带圈星号运算符"] # 0x229b - "⊜": [T: "带圈等号"] # 0x229c @@ -1171,7 +1172,7 @@ - "⊼": [T: "与非"] # 0x22bc - "⊽": [T: "或非"] # 0x22bd - "⊾": [T: "右角弧"] # 0x22be - - "⊿": [T: "右三角"] # 0x22bf + - "⊿": [T: "直角三角形"] # 0x22bf - "⋀": [T: "n 元逻辑与"] # 0x22c0 - "⋁": [T: "n 元逻辑或"] # 0x22c1 - "⋂": [T: "n 元交集"] # 0x22c2 @@ -1494,9 +1495,9 @@ - "▮": [T: "黑色竖矩形"] # 0x25ae - "▯": [T: "白色竖矩形"] # 0x25af - "▰": [T: "黑色平行四边形"] # 0x25b0 - - "▱": [T: "白色平行四边形"] # 0x25b1 + - "▱": [T: "平行四边形"] # 0x25b1 - "▲": [T: "黑上指三角"] # 0x25b2 - - "△": [T: "白上指三角"] # 0x25b3 + - "△": [T: "三角形"] # 0x25b3 - "▴": [T: "黑色上三角(小)"] # 0x25b4 - "▵": [T: "白色上三角(小)"] # 0x25b5 - "▶": [T: "黑色右三角"] # 0x25b6 @@ -1780,10 +1781,10 @@ - "⟶": [T: "长向右箭头"] # 0x27f6 - "⟷": [T: "长左右箭头"] # 0x27f7 - "⟸": [T: "长向左双线箭头"] # 0x27f8 - - "⟹": [T: "蕴含"] # 0x27f9 + - "⟹": [T: "推出"] # 0x27f9 - "⟺": [T: "当且仅当"] # 0x27fa - "⟻": [T: "长左带尾箭头"] # 0x27fb - - "⟼": [T: "长右带尾箭头"] # 0x27fc + - "⟼": [T: "映射到"] # 0x27fc - "⟽": [T: "长向左双线带尾箭头"] # 0x27fd - "⟾": [T: "长向右双线带尾箭头"] # 0x27fe - "⟿": [T: "长向右波浪箭头"] # 0x27ff @@ -2172,8 +2173,8 @@ - "⩺": [T: "内含圆圈的大于号"] # 0x2a7a - "⩻": [T: "带上加问号的小于"] # 0x2a7b - "⩼": [T: "带上加问号的大于"] # 0x2a7c - - "⩽": [T: "小于或倾斜等号"] # 0x2a7d - - "⩾": [T: "大于或倾斜等号"] # 0x2a7e + - "⩽": [T: "小于等于"] # 0x2a7d + - "⩾": [T: "大于等于"] # 0x2a7e - "⩿": [T: "内含点的小于或斜等号"] # 0x2a7f - "⪀": [T: "内含点的大于或斜等号"] # 0x2a80 - "⪁": [T: "带上加点的小于或倾斜等号"] # 0x2a81 diff --git a/Rules/Languages/zh/unicode.yaml b/Rules/Languages/zh/unicode.yaml index 2c1a8d7b1..d4993ea2f 100644 --- a/Rules/Languages/zh/unicode.yaml +++ b/Rules/Languages/zh/unicode.yaml @@ -215,7 +215,11 @@ - "¬": [T: "非"] # 0xac - "°": [T: "度"] # 0xb0 - - "±": [T: "正负"] # 0xb1 + - "±": # 0xb1 + - test: + if: "preceding-sibling::* and following-sibling::*" + then: [T: "加或减"] + else: [T: "正或负"] - "´": [T: "锐音符"] # 0xb4 - "·": # 0xB7 - test: @@ -408,7 +412,7 @@ if: "$Verbosity='Terse'" then: [T: "偏微分"] else: [T: "偏导数"] - - "∆": [T: "拉普拉斯算子"] # 0x2206 + - "∆": [T: "增量"] # 0x2206 - "∉": # 0x2209 # rule is identical to 0x2208 - test: @@ -526,5 +530,5 @@ - T: "包含" # --- restored short definitions that differ from unicode-full --- - - "⇒": [T: "向右双线箭头"] # 0x21d2 + - "⇒": [T: "推出"] # 0x21d2 - "∐": [T: "余积"] # 0x2210 diff --git a/tests/Languages/zh/cn.rs b/tests/Languages/zh/cn.rs index 8550de8fe..a6fcf86db 100644 --- a/tests/Languages/zh/cn.rs +++ b/tests/Languages/zh/cn.rs @@ -102,7 +102,7 @@ fn literal_speech_matches_documented_examples() -> Result<()> { } #[test] -fn units_and_laplacian_use_standard_terms() -> Result<()> { +fn units_increment_and_laplacian_use_standard_terms() -> Result<()> { test( "zh", "SimpleSpeak", @@ -112,7 +112,13 @@ fn units_and_laplacian_use_standard_terms() -> Result<()> { test( "zh", "SimpleSpeak", - "f", + "x", + "增量 x", + )?; + test( + "zh", + "ClearSpeak", + "f", "拉普拉斯算子 f", ) } @@ -240,6 +246,143 @@ fn set_operations_use_standard_verb_forms() -> Result<()> { ) } +#[test] +fn textbook_logic_and_geometry_symbols_use_standard_readings() -> Result<()> { + // Prefer the relation and shape names used in mainland textbooks over visual Unicode descriptions. + test( + "zh", + "SimpleSpeak", + "xR,pqrs", + "任意 x 属于 大写 r; 逗号; p 且 q 推出 r 或 s", + )?; + test("zh", "SimpleSpeak", "", "空集")?; + test( + "zh", + "SimpleSpeak", + "xx2,AB", + "x 映射到 x 平方, 逗号; 大写 a 减去 大写 b", + )?; + test( + "zh", + "SimpleSpeak", + "xx2", + "x 映射到 x 平方", + )?; + test( + "zh", + "SimpleSpeak", + "pq", + "p 推出 q", + )?; + test( + "zh", + "SimpleSpeak", + "ABCDEF", + "三角形, 大写 a 大写 b 大写 c; 相似于; 三角形, 大写 d 大写 e 大写 f", + )?; + test( + "zh", + "SimpleSpeak", + "ABCDEF", + "三角形, 大写 a 大写 b 大写 c; 全等于; 三角形, 大写 d 大写 e 大写 f", + )?; + test( + "zh", + "SimpleSpeak", + "O,ABCD", + "圆 大写 o, 逗号; 平行四边形, 大写 a 大写 b 大写 c 大写 d", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "a 带圈点运算符 b", + )?; + test( + "zh", + "SimpleSpeak", + "ABC", + "直角三角形, 大写 a 大写 b 大写 c", + ) +} + +#[test] +fn plus_minus_symbols_follow_standard_contextual_readings() -> Result<()> { + // GB 3102.11 distinguishes signs used alone from binary plus/minus operations. + test( + "zh", + "SimpleSpeak", + "±", + "正或负", + )?; + test( + "zh", + "SimpleSpeak", + "±x", + "正或负 x", + )?; + test( + "zh", + "SimpleSpeak", + "20±0.5", + "20 加或减 0.5", + )?; + test( + "zh", + "SimpleSpeak", + "", + "负或正", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "a 减或加 b", + )?; + test( + "zh", + "ClearSpeak", + "ab", + "a 加或减 b", + )?; + test( + "zh", + "ClearSpeak", + "cd", + "c 减或加 d", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "a 小于等于 b", + )?; + test( + "zh", + "SimpleSpeak", + "cd", + "c 大于等于 d", + )?; + test( + "zh", + "SimpleSpeak", + "ab", + "a 小于等于 b", + )?; + test( + "zh", + "SimpleSpeak", + "cd", + "c 大于等于 d", + )?; + test( + "zh", + "SimpleSpeak", + "fg", + "f 渐近等于 g", + ) +} + #[test] fn standard_number_sets_use_textbook_names() -> Result<()> { let cases = [ @@ -509,7 +652,7 @@ fn additional_real_world_readings() -> Result<()> { "ClearSpeak", "Medium", "vw", - "v 点乘 w", + "v 与 w 的数量积", ), ( "scalar-centered-dot", @@ -523,7 +666,14 @@ fn additional_real_world_readings() -> Result<()> { "SimpleSpeak", "Medium", "v×w", - "向量 v, 向量积 向量 w", + "向量 v, 叉乘 向量 w", + ), + ( + "explicit-cross-product-intent", + "ClearSpeak", + "Medium", + "vw", + "v 与 w 的叉积", ), ( "plain-norm", @@ -642,14 +792,14 @@ fn additional_real_world_readings() -> Result<()> { "SimpleSpeak", "Medium", "x=b±b24ac2a", - "x 等于; 分数, 2 a, 分之, 负 b 正负, 根号 b 平方 减 4 a c 结束根号; 结束分数", + "x 等于; 分数, 2 a, 分之, 负 b 加或减; 根号 b 平方 减 4 a c 结束根号; 结束分数", ), ( "quadratic-formula-literal", "LiteralSpeak", "Medium", "x=b±b24ac2a", - "x 等于; 分数, 2 a, 分之, 减 b 正负; 根号 b 上标 2 结束上标, 减 4 a c, 结束根号; 结束分数", + "x 等于; 分数, 2 a, 分之, 减 b 加或减; 根号 b 上标 2 结束上标, 减 4 a c, 结束根号; 结束分数", ), ( "binomial",